api: add list and retrieve role member(s) api (#50889)

This commit is contained in:
Nicolas Roche 2021-03-22 10:15:15 +01:00
parent 852655fb95
commit ed01a4e8c3
3 changed files with 207 additions and 4 deletions

View File

@ -22,6 +22,11 @@ urlpatterns = [
url(r'^register/$', api_views.register, name='a2-api-register'),
url(r'^password-change/$', api_views.password_change, name='a2-api-password-change'),
url(r'^user/$', api_views.user, name='a2-api-user'),
url(
r'^roles/(?P<role_uuid>[\w+]*)/members/$',
api_views.roles_members,
name='a2-api-role-members-list',
),
url(
r'^roles/(?P<role_uuid>[\w+]*)/members/(?P<member_uuid>[^/]+)/$',
api_views.role_membership,

View File

@ -878,6 +878,23 @@ class RolesAPI(api_mixins.GetOrCreateMixinView, ExceptionHandlerMixin, ModelView
super(RolesAPI, self).perform_destroy(instance)
class RolesMembersAPI(UsersAPI):
def initial(self, request, *args, **kwargs):
super(RolesMembersAPI, self).initial(request, *args, **kwargs)
Role = get_role_model()
self.role = get_object_or_404(Role, uuid=kwargs['role_uuid'])
def get_queryset(self):
if self.request.GET.get('nested', 'false').lower() in ('true', '1'):
qs = self.role.all_members()
else:
qs = self.role.members.all()
return qs
roles_members = RolesMembersAPI.as_view({'get': 'list'})
class RoleMembershipAPI(ExceptionHandlerMixin, APIView):
permission_classes = (permissions.IsAuthenticated,)
@ -888,18 +905,27 @@ class RoleMembershipAPI(ExceptionHandlerMixin, APIView):
self.role = get_object_or_404(Role, uuid=kwargs['role_uuid'])
self.member = get_object_or_404(User, uuid=kwargs['member_uuid'])
perm = 'a2_rbac.manage_members_role'
authorized = request.user.has_perm(perm, obj=self.role)
if not authorized:
raise PermissionDenied(u'User not allowed to manage role members')
def get(self, request, *args, **kwargs):
if not request.user.has_perm('a2_rbac.view_role', obj=self.role):
raise PermissionDenied(u'User not allowed to view role')
if self.request.GET.get('nested', 'false').lower() in ('true', '1'):
qs = self.role.all_members()
else:
qs = self.role.members.all()
member = get_object_or_404(qs, uuid=kwargs['member_uuid'])
return Response(BaseUserSerializer(member).data)
def post(self, request, *args, **kwargs):
if not request.user.has_perm('a2_rbac.manage_members_role', obj=self.role):
raise PermissionDenied(u'User not allowed to manage role members')
self.role.members.add(self.member)
return Response(
{'result': 1, 'detail': _('User successfully added to role')}, status=status.HTTP_201_CREATED
)
def delete(self, request, *args, **kwargs):
if not request.user.has_perm('a2_rbac.manage_members_role', obj=self.role):
raise PermissionDenied(u'User not allowed to manage role members')
self.role.members.remove(self.member)
return Response(
{'result': 1, 'detail': _('User successfully removed from role')}, status=status.HTTP_200_OK

View File

@ -677,6 +677,115 @@ def test_api_users_create_force_password_reset(app, client, settings, superuser)
assert 'Password changed' in resp
def test_api_role_get_member(app, api_user, role, member):
app.authorization = ('Basic', (api_user.username, api_user.username))
authorized = api_user.has_perm('a2_rbac.view_role', role)
if member.username == 'fake' or role.name == 'fake':
status = 404
elif authorized:
status = 404
else:
status = 403
resp = app.get('/api/roles/{0}/members/{1}/'.format(role.uuid, member.uuid), status=status)
if member.username == 'fake' or role.name == 'fake':
assert resp.json == {'result': 0, 'errors': {'detail': 'Not found.'}}
elif status == 404:
assert resp.json == {'result': 0, 'errors': {'detail': 'Not found.'}}
member.roles.add(role)
resp = app.get('/api/roles/{0}/members/{1}/'.format(role.uuid, member.uuid))
assert resp.json['uuid'] == member.uuid
assert (
set(
[
'id',
'ou',
'date_joined',
'last_login',
'password',
'is_superuser',
'uuid',
'username',
'first_name',
'last_name',
'email',
'email_verified',
'is_staff',
'is_active',
'modified',
'last_account_deletion_alert',
'deactivation',
'first_name_verified',
'last_name_verified',
]
)
== set(resp.json.keys())
)
else:
assert resp.json['result'] == 0
assert resp.json['errors'] == 'User not allowed to view role'
def test_api_role_get_member_nested(app, admin_ou1, user_ou1, role_ou1, role_random):
app.authorization = ('Basic', (admin_ou1.username, admin_ou1.username))
url = reverse(
'a2-api-role-member',
kwargs={
'role_uuid': role_ou1.uuid,
'member_uuid': admin_ou1.uuid,
},
)
Attribute.objects.create(kind='birthdate', name='birthdate', label='birthdate', required=True)
user_ou1.attributes.birthdate = datetime.date(2019, 2, 2)
user_ou1.save()
# admin.ou1 is an inherited member of role_ou1
role_random.members.add(admin_ou1)
role_random.add_parent(role_ou1)
assert admin_ou1 not in role_ou1.members.all()
assert admin_ou1 in role_ou1.all_members()
# default api call without nested users
resp = app.get(url, status=404)
assert resp.json == {'result': 0, 'errors': {'detail': 'Not found.'}}
# api call with nested users
resp = app.get(url, params={'nested': 'true'})
assert resp.json['username'] == 'admin.ou1'
assert (
set(
[
'ou',
'id',
'uuid',
'is_staff',
'is_superuser',
'first_name',
'first_name_verified',
'last_name',
'last_name_verified',
'date_joined',
'last_login',
'username',
'password',
'email',
'is_active',
'modified',
'email_verified',
'last_account_deletion_alert',
'deactivation',
'birthdate',
'birthdate_verified',
]
)
== set(resp.json)
)
def test_api_role_add_member(app, api_user, role, member):
app.authorization = ('Basic', (api_user.username, api_user.username))
@ -719,6 +828,8 @@ def test_api_role_remove_member(app, api_user, role, member):
elif authorized:
assert resp.json['result'] == 1
assert resp.json['detail'] == 'User successfully removed from role'
resp = app.get('/api/roles/{0}/members/{1}/'.format(role.uuid, member.uuid), status=404)
assert resp.json == {'result': 0, 'errors': {'detail': 'Not found.'}}
else:
assert resp.json['result'] == 0
assert resp.json['errors'] == 'User not allowed to manage role members'
@ -1385,6 +1496,67 @@ def test_api_get_role_list(app, admin_ou1, role_ou1, role_random):
assert field in role_dict
def test_api_get_role_member_list(app, admin_ou1, user_ou1, role_ou1, role_random):
app.authorization = ('Basic', (admin_ou1.username, admin_ou1.username))
url = reverse('a2-api-role-members-list', kwargs={'role_uuid': role_ou1.uuid})
Attribute.objects.create(kind='birthdate', name='birthdate', label='birthdate', required=True)
user_ou1.attributes.birthdate = datetime.date(2019, 2, 2)
user_ou1.save()
# john.doe is a direct member of role_ou1
role_ou1.members.add(user_ou1)
assert user_ou1 in role_ou1.members.all()
# admin.ou1 is an inherited member of role_ou1
role_random.members.add(admin_ou1)
role_random.add_parent(role_ou1)
assert admin_ou1 not in role_ou1.members.all()
assert admin_ou1 in role_ou1.all_members()
# default api call without nested users
resp = app.get(url)
assert len(resp.json['results']) > 0
for user_dict in resp.json['results']:
assert (
set(
[
'ou',
'id',
'uuid',
'is_staff',
'is_superuser',
'first_name',
'first_name_verified',
'last_name',
'last_name_verified',
'date_joined',
'last_login',
'username',
'password',
'email',
'is_active',
'modified',
'email_verified',
'last_account_deletion_alert',
'deactivation',
'birthdate',
'birthdate_verified',
]
)
== set(user_dict.keys())
)
assert [x['username'] for x in resp.json['results']] == ['john.doe']
# api call with nested users
resp = app.get(url, params={'nested': 'true'})
assert [x['username'] for x in resp.json['results']] == ['john.doe', 'admin.ou1']
# get users ordered by usernames
resp = app.get(url, params={'nested': 'true', 'ordering': 'username'})
assert [x['username'] for x in resp.json['results']] == ['admin.ou1', 'john.doe']
def test_no_opened_session_cookie_on_api(app, user, settings):
settings.A2_OPENED_SESSION_COOKIE_DOMAIN = 'testserver.local'
app.authorization = ('Basic', (user.username, user.username))