From 9790ad35667be47ecd054b7cd13df73fcee0c7ba Mon Sep 17 00:00:00 2001 From: Paul Marillonnet Date: Fri, 12 Jan 2018 11:27:12 +0100 Subject: [PATCH] WIP add role creation api (#20706) --- src/authentic2/api_urls.py | 2 ++ src/authentic2/api_views.py | 83 ++++++++++++++++++++++++++++++++++++++++++++- tests/test_api.py | 22 ++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/authentic2/api_urls.py b/src/authentic2/api_urls.py index 61e6d9df..bb7dce4a 100644 --- a/src/authentic2/api_urls.py +++ b/src/authentic2/api_urls.py @@ -13,5 +13,7 @@ urlpatterns = patterns('', api_views.role_memberships, name='a2-api-role-member'), url(r'^check-password/$', api_views.check_password, name='a2-api-check-password'), + url(r'^ous/(?P[\w+]*)/roles/', + api_views.roles, name='a2-api-role'), ) urlpatterns += api_views.router.urls diff --git a/src/authentic2/api_views.py b/src/authentic2/api_views.py index 95fb99ee..74888ac6 100644 --- a/src/authentic2/api_views.py +++ b/src/authentic2/api_views.py @@ -4,7 +4,7 @@ import smtplib from django.db import models from django.contrib.auth import get_user_model -from django.core.exceptions import MultipleObjectsReturned +from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.utils.translation import ugettext as _ from django.views.decorators.vary import vary_on_headers from django.views.decorators.cache import cache_control @@ -444,6 +444,80 @@ class BaseUserSerializer(serializers.ModelSerializer): exclude = ('date_joined', 'user_permissions', 'groups', 'last_login') +class RoleSerializer(serializers.ModelSerializer): + """ + TODO + ou = serializers.SlugRelatedField( + queryset=get_ou_model().objects.all(), + serializer=BaseOrganizationalUnitSerializer, + slug_field='slug', + default=get_default_ou) + """ + ou_id_or_slug = serializers.CharField(required=True) + uuid = serializers.CharField(required=True) + name = serializers.CharField(required=True) + slug = serializers.CharField(required=True) # TODO? slugify from name? + # service = serializers.CharField(required=True, allow_null=True) # TODO + admin_scope_id = serializers.IntegerField(required=True, allow_null=True) + # admin_scope_ct = serializers.IntegerField(required=True, allow_null=True) # XXX todo + + def __init__(self, *args, **kwargs): + super(RoleSerializer, self).__init__(*args, **kwargs) + request = kwargs['context'].get('request') + if hasattr(request, 'parser_context'): + ou_id_or_slug = request.parser_context.get('kwargs', {}).get('ou_id_or_slug') + kwargs['data']['ou_id_or_slug'] = ou_id_or_slug + + def create(self, validated_data): + logger = logging.getLogger(__name__) + try: + Role = get_role_model() + ou_id_or_slug = validated_data['ou_id_or_slug'] + + if len(Role.objects.filter(slug=validated_data['slug'])): + logger.error('error: role already existing in the A2 database') + return + + role = Role.objects.create() + + for role_field in set(self.Meta.fields) - {'service', 'ou', 'admin_scope_ct'}: + setattr(role, role_field, validated_data.get(role_field)) + + OU = get_ou_model() + # try first on the OU uuid then on its slug + try: + ou = OU.objects.get(uuid=ou_id_or_slug) + except (MultipleObjectsReturned, ObjectDoesNotExist) as e: + ou = OU.objects.get(slug=ou_id_or_slug) + role.ou = ou + self.instance = role + self.instance.save() + logger.info('Role %r created.', role) + + except Exception as e: + logger.error('couldn\'t create role: %r', e) + return None + return role + + def validate(self, data, *args, **kwargs): + Role = get_role_model() + qs = Role.objects.all() + ou = None + if self.instance: + ou = self.instance.ou + if 'ou' in data and not ou: + ou = data['ou'] + # XXX which role-specific validation? + # admin-scope content type & identifier ? + return data + + class Meta: + model = get_role_model() + fields = ['uuid', 'name', 'slug', 'ou_id_or_slug', + 'admin_scope_id'] + # exclude = ['admin_scope_ct', 'service'] + + class UsersFilter(FilterSet): class Meta: model = get_user_model() @@ -576,6 +650,13 @@ class UsersAPI(HookMixin, ExceptionHandlerMixin, ModelViewSet): return Response({'result': 1}) +class RolesAPI(ExceptionHandlerMixin, ModelViewSet): + permission_classes = (permissions.IsAuthenticated,) + serializer_class = RoleSerializer + +roles = RolesAPI.as_view({'post': 'create'}) + + class RoleMembershipsAPI(ExceptionHandlerMixin, APIView): permission_classes = (permissions.IsAuthenticated,) diff --git a/tests/test_api.py b/tests/test_api.py index 96085590..c705c743 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -30,6 +30,28 @@ def test_api_user_simple(logged_app): assert 'username' in resp.json +def test_api_role_simple(app, user): + OU = get_ou_model() + epsilon = OU.objects.create(name='OU Epsilon', slug='epsilon') + role_data = { + 'slug': 'soma-officer', + 'uuid': 'H1GH', + 'name': 'Soma Officer', + 'admin_scope_id': 2 + } + # 'service': 'default', # TODO + # 'admin_scope_ct': 1, # TODO + app.authorization = ('Basic', (user.username, user.username)) + resp = app.post_json('/api/ous/epsilon/roles/', params=role_data) + assert isinstance(resp.json, dict) + Role = get_role_model() + posted_role = Role.objects.get(slug='soma-officer') + for key, value in role_data.items(): + assert key in resp.json.keys() + assert value in resp.json.values() + assert getattr(posted_role, key) == value + + def test_api_user(client): # create an user, an ou role, a service and a service role ou = get_default_ou() -- 2.11.0