def retrieve(self, request, *args, **kwargs): """Get a group. @api {get} /api/v1/groups/:uuid Get a group @apiName getGroup @apiGroup Group @apiVersion 1.0.0 @apiDescription Get a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier. @apiSuccess {String} uuid Group unique identifier @apiSuccess {String} name Group name @apiSuccess {Array} principals Array of principals @apiSuccess {Array} roles Array of roles @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "uuid": "16fd2706-8baf-433b-82eb-8c7fada847da", "name": "GroupA", "principals": [ { "username": "******" } ], "roles": [ { "name": "RoleA", "uuid": "4df211e0-2d88-49a4-8802-728630224d15" } ] } """ validate_uuid(kwargs.get("uuid"), "group uuid validation") return super().retrieve(request=request, args=args, kwargs=kwargs)
def partial_update(self, request, *args, **kwargs): """Patch a cross-account request.""" validate_uuid(kwargs.get("pk"), "cross-account request uuid validation") for field in request.data: if field not in VALID_PATCH_FIELDS: self.throw_validation_error( "cross-accont partial update", f"Field '{field}' is not supported. Please use one or more of: {VALID_PATCH_FIELDS}", ) with tenant_context(Tenant.objects.get(schema_name="public")): current = self.get_object() if current.status != "pending": self.throw_validation_error( "cross-account partial update", "Only pending requests may be updated.") self.validate_and_format_input(request.data) kwargs["partial"] = True response = super().update(request=request, *args, **kwargs) if response.status_code and response.status_code is http_status.HTTP_200_OK: if request.data.get("status"): self.update_status(current, request.data.get("status")) return Response( CrossAccountRequestDetailSerializer(current).data) return response
def update(self, request, *args, **kwargs): """Update a group. @api {post} /api/v1/groups/:uuid Update a group @apiName updateGroup @apiGroup Group @apiVersion 1.0.0 @apiDescription Update a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier @apiSuccess {String} uuid Group unique identifier @apiSuccess {String} name Group name @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "uuid": "16fd2706-8baf-433b-82eb-8c7fada847da", "name": "GroupA" } """ validate_uuid(kwargs.get("uuid"), "group uuid validation") self.protect_default_groups("update") return super().update(request=request, args=args, kwargs=kwargs)
def update(self, request, *args, **kwargs): """Update a cross-account request.""" validate_uuid(kwargs.get("pk"), "cross-account request uuid validation") with tenant_context(Tenant.objects.get(schema_name="public")): current = self.get_object() if current.status != "pending": self.throw_validation_error( "cross-account update", "Only pending requests may be updated.") if "target_account" in request.data and str( request.data.get( "target_account")) != current.target_account: self.throw_validation_error( "cross-account-update", "Target account may not be updated.") self.validate_and_format_input(request.data) response = super().update(request=request, args=args, kwargs=kwargs) if response.status_code and response.status_code is http_status.HTTP_200_OK: if request.data.get("status"): self.update_status(current, request.data.get("status")) return Response( CrossAccountRequestDetailSerializer(current).data) return response
def update(self, request, *args, **kwargs): """Update a group. @api {post} /api/v1/groups/:uuid Update a group @apiName updateGroup @apiGroup Group @apiVersion 1.0.0 @apiDescription Update a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier @apiSuccess {String} uuid Group unique identifier @apiSuccess {String} name Group name @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "uuid": "16fd2706-8baf-433b-82eb-8c7fada847da", "name": "GroupA" } """ validate_uuid(kwargs.get("uuid"), "group uuid validation") self.protect_default_groups("update") group_name = Group.objects.get(uuid=kwargs.get("uuid")).name with tenant_context(Tenant.objects.get(schema_name="public")): group_in_public, _ = Group.objects.update_or_create( name=group_name, tenant=request.tenant, defaults=request.data) return super().update(request=request, args=args, kwargs=kwargs)
def destroy(self, request, *args, **kwargs): """Delete a role. @api {delete} /api/v1/roles/:uuid Delete a role @apiName deleteRole @apiGroup Role @apiVersion 1.0.0 @apiDescription Delete a role @apiHeader {String} token User authorization token @apiParam (Path) {String} uuid Role unique identifier @apiSuccessExample {json} Success-Response: HTTP/1.1 204 NO CONTENT """ validate_uuid(kwargs.get("uuid"), "role uuid validation") role = self.get_object() if role.system or role.platform_default: key = "role" message = "System roles cannot be deleted." error = {key: [_(message)]} raise serializers.ValidationError(error) with transaction.atomic(): # Remove role in public schema. with tenant_context(Tenant.objects.get(schema_name="public")): role_public = Role.objects.get(name=role.name, tenant=request.tenant) self.delete_policies_if_no_role_attached(role_public) role_public.delete() self.delete_policies_if_no_role_attached(role) return super().destroy(request=request, args=args, kwargs=kwargs)
def destroy(self, request, *args, **kwargs): """Delete a role. @api {delete} /api/v1/roles/:uuid Delete a role @apiName deleteRole @apiGroup Role @apiVersion 1.0.0 @apiDescription Delete a role @apiHeader {String} token User authorization token @apiParam (Path) {String} uuid Role unique identifier @apiSuccessExample {json} Success-Response: HTTP/1.1 204 NO CONTENT """ validate_uuid(kwargs.get("uuid"), "role uuid validation") role = self.get_object() if role.system or role.platform_default: key = "role" message = "System roles cannot be deleted." error = {key: [_(message)]} raise serializers.ValidationError(error) with transaction.atomic(): policies = role.policies.all() for policy in policies: if policy.roles.count() == 1: policy.delete() return super().destroy(request=request, args=args, kwargs=kwargs)
def access(self, request, uuid=None): """Return access objects for specified role.""" validate_uuid(uuid, "role uuid validation") try: role = Role.objects.get(uuid=uuid) except (Role.DoesNotExist, ValidationError): raise Http404() access = AccessSerializer(role.access, many=True).data page = self.paginate_queryset(access) return self.get_paginated_response(page)
def partial_update(self, request, *args, **kwargs): """Patch a role.""" validate_uuid(kwargs.get("uuid"), "role uuid validation") payload = json.loads(request.body) for field in payload: if field not in VALID_PATCH_FIELDS: key = "role" message = f"Field '{field}' is not supported. Please use one or more of: {VALID_PATCH_FIELDS}." error = {key: [_(message)]} raise serializers.ValidationError(error) return super().update(request=request, args=args, kwargs=kwargs)
def update(self, request, *args, **kwargs): """Update a cross-account request.""" validate_uuid(kwargs.get("pk"), "cross-account request uuid validation") with tenant_context(Tenant.objects.get(schema_name="public")): current = self.get_object() self.check_update_permission(request, current) request.data["target_account"] = current.target_account self.validate_and_format_input(request.data) response = super().update(request=request, args=args, kwargs=kwargs) if response.status_code and response.status_code is http_status.HTTP_200_OK: if request.data.get("status"): self.update_status(current, request.data.get("status")) return Response(CrossAccountRequestDetailSerializer(current).data) return response
def destroy(self, request, *args, **kwargs): """Delete a group. @api {delete} /api/v1/groups/:uuid Delete a group @apiName deleteGroup @apiGroup Group @apiVersion 1.0.0 @apiDescription Delete a group @apiHeader {String} token User authorization token @apiParam (Path) {String} uuid Group unique identifier @apiSuccessExample {json} Success-Response: HTTP/1.1 204 NO CONTENT """ validate_uuid(kwargs.get("uuid"), "group uuid validation") self.protect_default_groups("delete") return super().destroy(request=request, args=args, kwargs=kwargs)
def retrieve(self, request, *args, **kwargs): """Get a role. @api {get} /api/v1/roles/:uuid Get a role @apiName getRole @apiGroup Role @apiVersion 1.0.0 @apiDescription Get a role @apiHeader {String} token User authorization token @apiParam (Path) {String} id Role unique identifier. @apiSuccess {String} uuid Role unique identifier @apiSuccess {String} name Role name @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "uuid": "16fd2706-8baf-433b-82eb-8c7fada847da", "name": "RoleA", "display_name": "RoleA", "access": [ { "permission": "app:*:read", "resourceDefinitions": [ { "attributeFilter": { "key": "app.attribute.case", "operation": "equal", "value": "thevalue" } } ] } ] } """ validate_uuid(kwargs.get("uuid"), "role uuid validation") return super().retrieve(request=request, args=args, kwargs=kwargs)
def destroy(self, request, *args, **kwargs): """Delete a group. @api {delete} /api/v1/groups/:uuid Delete a group @apiName deleteGroup @apiGroup Group @apiVersion 1.0.0 @apiDescription Delete a group @apiHeader {String} token User authorization token @apiParam (Path) {String} uuid Group unique identifier @apiSuccessExample {json} Success-Response: HTTP/1.1 204 NO CONTENT """ validate_uuid(kwargs.get("uuid"), "group uuid validation") self.protect_default_groups("delete") group_name = Group.objects.get(uuid=kwargs.get("uuid")).name with tenant_context(Tenant.objects.get(schema_name="public")): Group.objects.filter(name=group_name, tenant=request.tenant).delete() return super().destroy(request=request, args=args, kwargs=kwargs)
def uuid_filter(self, queryset, field, values): """Filter for group uuid lookup.""" uuids = values.split(",") for uuid in uuids: validate_uuid(uuid, "groups uuid filter") return CommonFilters.multiple_values_in(self, queryset, field, values)
def principals(self, request, uuid=None): """Get, add or remove principals from a group.""" """ @api {get} /api/v1/groups/:uuid/principals/ Get principals for a group @apiName getPrincipals @apiGroup Group @apiVersion 1.0.0 @apiDescription Get principals for a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier @apiSuccess {String} uuid Group unique identifier @apiSuccess {String} name Group name @apiSuccess {Array} principals Array of principals @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "principals": [ { "username": "******" } ] } """ """ @api {post} /api/v1/groups/:uuid/principals/ Add principals to a group @apiName addPrincipals @apiGroup Group @apiVersion 1.0.0 @apiDescription Add principals to a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier @apiParam (Request Body) {String} username Principal username @apiParamExample {json} Request Body: { "principals": [ { "username": "******" }, { "username": "******" } ] } @apiSuccess {String} uuid Group unique identifier @apiSuccess {String} name Group name @apiSuccess {Array} principals Array of principals @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "uuid": "16fd2706-8baf-433b-82eb-8c7fada847da", "name": "GroupA", "principals": [ { "username": "******" } ] } """ """ @api {delete} /api/v1/groups/:uuid/principals/ Remove principals from group @apiName removePrincipals @apiGroup Group @apiVersion 1.0.0 @apiDescription Remove principals from a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier @apiParam (Query) {String} usernames List of comma separated principal usernames @apiSuccessExample {json} Success-Response: HTTP/1.1 204 NO CONTENT """ principals = [] validate_uuid(uuid, "group uuid validation") group = self.get_object() account = self.request.user.account if request.method == "POST": serializer = GroupPrincipalInputSerializer(data=request.data) if serializer.is_valid(raise_exception=True): principals = serializer.data.pop("principals") resp = self.add_principals(group, principals, account) if isinstance(resp, dict) and "errors" in resp: return Response(status=resp["status_code"], data=resp["errors"]) output = GroupSerializer(resp) response = Response(status=status.HTTP_200_OK, data=output.data) elif request.method == "GET": principals_from_params = self.filtered_principals(group, request) page = self.paginate_queryset(principals_from_params) serializer = PrincipalSerializer(page, many=True) principal_data = serializer.data if principal_data: username_list = [ principal["username"] for principal in principal_data ] else: username_list = [] proxy = PrincipalProxy() all_valid_fields = VALID_PRINCIPAL_ORDER_FIELDS + [ "-" + field for field in VALID_PRINCIPAL_ORDER_FIELDS ] if request.query_params.get(ORDERING_PARAM): sort_field = validate_and_get_key(request.query_params, ORDERING_PARAM, all_valid_fields, "username") sort_order = "des" if sort_field == "-username" else "asc" else: sort_order = None resp = proxy.request_filtered_principals(username_list, account, sort_order=sort_order) if isinstance(resp, dict) and "errors" in resp: return Response(status=resp.get("status_code"), data=resp.get("errors")) response = self.get_paginated_response(resp.get("data")) else: if USERNAMES_KEY not in request.query_params: key = "detail" message = "Query parameter {} is required.".format( USERNAMES_KEY) raise serializers.ValidationError({key: _(message)}) username = request.query_params.get(USERNAMES_KEY, "") principals = [name.strip() for name in username.split(",")] self.remove_principals(group, principals, account) response = Response(status=status.HTTP_204_NO_CONTENT) return response
def roles(self, request, uuid=None): """Get, add or remove roles from a group.""" """ @api {get} /api/v1/groups/:uuid/roles/ Get roles for a group @apiName getRoles @apiGroup Group @apiVersion 1.0.0 @apiDescription Get roles for a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier. @apiParam (Query) {String} order_by Determine ordering of returned roles. @apiSuccess {Array} data Array of roles @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "data": [ { "name": "RoleA", "uuid": "4df211e0-2d88-49a4-8802-728630224d15", "description": "RoleA Description", "policyCount: 0, "applications": [], "system": false, "platform_default": false } ] } """ """ @api {post} /api/v1/groups/:uuid/roles/ Add roles to a group @apiName addRoles @apiGroup Group @apiVersion 1.0.0 @apiDescription Add roles to a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier @apiParam (Request Body) {Array} roles Array of role UUIDs @apiParamExample {json} Request Body: { "roles": [ "4df211e0-2d88-49a4-8802-728630224d15" ] } @apiSuccess {String} uuid Group unique identifier @apiSuccess {String} name Group name @apiSuccess {Array} roles Array of roles @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "data": [ { "name": "RoleA", "uuid": "4df211e0-2d88-49a4-8802-728630224d15", "description": "RoleA Description", "policyCount: 0, "applications": [], "system": false, "platform_default": false } ] } """ """ @api {delete} /api/v1/groups/:uuid/roles/ Remove roles from group @apiName removeRoles @apiGroup Group @apiVersion 1.0.0 @apiDescription Remove roles from a group @apiHeader {String} token User authorization token @apiParam (Path) {String} id Group unique identifier @apiParam (Query) {String} roles List of comma separated role UUIDs @apiSuccessExample {json} Success-Response: HTTP/1.1 204 NO CONTENT """ roles = [] validate_uuid(uuid, "group uuid validation") group = self.get_object() if request.method == "POST": serializer = GroupRoleSerializerIn(data=request.data) if serializer.is_valid(raise_exception=True): roles = request.data.pop(ROLES_KEY, []) add_roles(group, roles) set_system_flag_post_update(group) response_data = GroupRoleSerializerIn(group) elif request.method == "GET": serialized_roles = self.obtain_roles(request, group) page = self.paginate_queryset(serialized_roles) serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) else: if ROLES_KEY not in request.query_params: key = "detail" message = "Query parameter {} is required.".format(ROLES_KEY) raise serializers.ValidationError({key: _(message)}) role_ids = request.query_params.get(ROLES_KEY, "").split(",") serializer = GroupRoleSerializerIn(data={"roles": role_ids}) if serializer.is_valid(raise_exception=True): remove_roles(group, role_ids) set_system_flag_post_update(group) return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_200_OK, data=response_data.data)
def update(self, request, *args, **kwargs): """Update a group. @api {post} /api/v1/roles/:uuid Update a role @apiName updateRole @apiGroup Role @apiVersion 1.0.0 @apiDescription Update a role @apiHeader {String} token User authorization token @apiParam (Path) {String} id Role unique identifier @apiParam (Request Body) {String} name Role name @apiParam (Request Body) {Array} access Access definition @apiParamExample {json} Request Body: { "name": "RoleA", "access": [ { "permission": "app:*:read", "resourceDefinitions": [ { "attributeFilter": { "key": "app.attribute.case", "operation": "equal", "value": "change_value" } } ] } ] } @apiSuccess {String} uuid Role unique identifier @apiSuccess {String} name Role name @apiSuccessExample {json} Success-Response: HTTP/1.1 200 OK { "uuid": "16fd2706-8baf-433b-82eb-8c7fada847da", "name": "RoleA", "display_name": "RoleA", "access": [ { "permission": "app:*:read", "resourceDefinitions": [ { "attributeFilter": { "key": "app.attribute.case", "operation": "equal", "value": "change_value" } } ] } ] } """ validate_uuid(kwargs.get("uuid"), "role uuid validation") access_list = self.validate_and_get_access_list(request.data) if access_list: for perm in access_list: app = perm.get("permission").split(":")[0] if app not in settings.ROLE_CREATE_ALLOW_LIST: key = "role" message = "Custom roles cannot be created for {}".format( app) error = {key: [_(message)]} raise serializers.ValidationError(error) return super().update(request=request, args=args, kwargs=kwargs)