From c06e295f19c7a90799f454b7fb7e49a0f9f32051 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 | 4 +++ src/authentic2/api_views.py | 74 ++++++++++++++++++++++++++++++++++++++++++++- tests/conftest.py | 6 ++++ tests/test_api.py | 45 +++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) diff --git a/src/authentic2/api_urls.py b/src/authentic2/api_urls.py index 61e6d9df..041621d2 100644 --- a/src/authentic2/api_urls.py +++ b/src/authentic2/api_urls.py @@ -13,5 +13,9 @@ 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_list_or_create, name='a2-api-role'), + url(r'^ous/(?P[\w+]*)/roles/(?P[\w+]*)/$', + api_views.roles_detail, 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..31e32012 100644 --- a/src/authentic2/api_views.py +++ b/src/authentic2/api_views.py @@ -27,7 +27,7 @@ from django_filters.rest_framework import FilterSet from .custom_user.models import User from . import utils, decorators, attribute_kinds, app_settings, hooks -from .models import Attribute, PasswordReset +from .models import Attribute, PasswordReset, Service from .a2_rbac.utils import get_default_ou @@ -444,6 +444,65 @@ class BaseUserSerializer(serializers.ModelSerializer): exclude = ('date_joined', 'user_permissions', 'groups', 'last_login') +class RoleSerializer(serializers.ModelSerializer): + ou = serializers.SlugRelatedField( + many=False, + required=True, + queryset=get_ou_model().objects.all(), + slug_field='slug') + uuid = serializers.CharField(required=True) + name = serializers.CharField(required=True) + slug = serializers.CharField(required=True) # TODO? slugify from name? + service = serializers.SlugRelatedField( + many=False, + required=False, + queryset=Service.objects.all(), + slug_field='slug') + 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 = request.parser_context.get('kwargs', {}).get('ou') + # XXX dirty + # /api/ous/(?P) not really a role lookup field + # but is necessary to restrain roles to a single OU. + # Gladly offering a pint to whoever fixes it... + if 'data' in kwargs: + kwargs['data']['ou'] = ou + else: + kwargs['data'] = {'ou': ou} + + # XXX That one won't fix the OU FK dereferencing issue while saving roles + """ + def create(self, validated_data): + Role = get_role_model() + role = Role(**validated_data) # Necessary to created to FK linking to OU? Nope + self.instance = role + self.instance.save() # XXX Broken!! role.ou not saved in DB?! + return role + """ + + # XXX That one won't fix it either, as it's not a data validation issue + """ + def validate(self, data, *args, **kwargs): + logger = logging.getLogger(__name__) + data = super(RoleSerializer, self).validate(data, *args, **kwargs) + Role = get_role_model() + if len(Role.objects.filter(slug=data['slug'])): + logger.error('error: role already existing in the A2 database') + return + return data + """ + + class Meta: + model = get_role_model() + fields = ['uuid', 'name', 'slug', 'ou', + 'admin_scope_id', 'service'] + + class UsersFilter(FilterSet): class Meta: model = get_user_model() @@ -576,6 +635,19 @@ class UsersAPI(HookMixin, ExceptionHandlerMixin, ModelViewSet): return Response({'result': 1}) +class RolesAPI(ExceptionHandlerMixin, ModelViewSet): + permission_classes = (permissions.IsAuthenticated,) + serializer_class = RoleSerializer + lookup_field = 'slug' + + def get_queryset(self): + Role = get_role_model() + return Role.objects.all() + +roles_detail = RolesAPI.as_view({'get': 'retrieve'}) +roles_list_or_create = RolesAPI.as_view({'post': 'create', 'get': 'list'}) + + class RoleMembershipsAPI(ExceptionHandlerMixin, APIView): permission_classes = (permissions.IsAuthenticated,) diff --git a/tests/conftest.py b/tests/conftest.py index e968d341..39635c77 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,6 +14,7 @@ from pytest_django.migrations import DisableMigrations from authentic2.a2_rbac.utils import get_default_ou from authentic2_idp_oidc.models import OIDCClient from authentic2.authentication import OIDCUser +from authentic2.models import Service import utils @@ -46,6 +47,11 @@ def ou_rando(db): return OU.objects.create(name='ou_rando', slug='ou_rando') +@pytest.fixture +def service1(db): + return Service.objects.create(name='Service1', slug='service1') + + def create_user(**kwargs): User = get_user_model() password = kwargs.pop('password', None) or kwargs['username'] diff --git a/tests/test_api.py b/tests/test_api.py index 96085590..9cbd1eb2 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -30,6 +30,51 @@ def test_api_user_simple(logged_app): assert 'username' in resp.json +def test_api_post_role_simple(app, user, ou1, service1): + role_data = { + 'slug': 'coffee-manager', + 'service': 'service1', + 'uuid': 'RhFactor001', + 'name': 'Coffee Manager', + 'admin_scope_id': 2 + } + # 'admin_scope_ct': 1, # TODO + app.authorization = ('Basic', (user.username, user.username)) + resp = app.post_json('/api/ous/ou1/roles/', params=role_data) + assert isinstance(resp.json, dict) + Role = get_role_model() + posted_role = Role.objects.get(slug='coffee-manager') + for key, value in role_data.items(): + assert key in resp.json.keys() + assert value in resp.json.values() + + assert posted_role.slug == role_data['slug'] + assert posted_role.uuid == role_data['uuid'] + assert posted_role.name == role_data['name'] + assert posted_role.admin_scope_id == role_data['admin_scope_id'] + + #assert posted_role.ou.slug == 'ou1' # XXX + assert posted_role.service.slug == role_data['service'] + + +def test_api_get_role_description(app, user, role_random): + app.authorization = ('Basic', (user.username, user.username)) + resp = app.get('/api/ous/ou1/roles/rando/') + + assert resp.json['slug'] == 'rando' + # TODO + + +def test_api_get_role_list(app, user, role_random): + app.authorization = ('Basic', (user.username, user.username)) + resp = app.get('/api/ous/ou1/roles/') + + role_fields = ['slug', 'uuid', 'name', 'admin_scope_id', 'service'] + + for role_dict in resp.json['results']: + for field in role_fields: + assert field in role_dict + def test_api_user(client): # create an user, an ou role, a service and a service role ou = get_default_ou() -- 2.11.0