Skip to content

Commit

Permalink
Add Prefix Modify API endpoint
Browse files Browse the repository at this point in the history
On branch prefix_perms
Your branch is up to date with 'origin/prefix_perms'.

Changes to be committed:
	modified:   config/services.py
	modified:   prefix/apis.py
	modified:   prefix/models.py
	modified:   prefix/selectors.py
	modified:   prefix/services.py
  • Loading branch information
HadleyKing committed Mar 28, 2024
1 parent bce794b commit bacb265
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 46 deletions.
3 changes: 1 addition & 2 deletions config/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ def legacy_api_converter(data:dict) ->dict:
for prefix in object['prefixes']:
return_data.append({
"prefix": prefix["prefix"],
"description": prefix["description"],
"authorized_groups": [owner_group]
"description": prefix["description"]
})
return return_data

Expand Down
96 changes: 77 additions & 19 deletions prefix/apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from config.services import legacy_api_converter, response_constructor
from prefix.services import PrefixSerializer, delete_prefix

PREFIX_SCHEMA = openapi.Schema(
PREFIX_CREATE_SCHEMA = openapi.Schema(
type=openapi.TYPE_ARRAY,
title="Prefix Schema",
items=openapi.Schema(
Expand Down Expand Up @@ -41,6 +41,55 @@
)
)

user_permissions = {"tester": ["view_TEST", "publish_TEST"]}

USER_PERMISSIONS_SCHEMA = openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["user", "permissions"],
example=user_permissions,
properties={
"user": openapi.Schema(
type=openapi.TYPE_STRING,
description="User for permissions to be modified",
),
"permissions": openapi.Schema(
type=openapi.TYPE_ARRAY,
description="List of permissiosn to apply",
items=openapi.Schema(
type=openapi.TYPE_STRING
)
)

}
)

PREFIX_MODIFY_SCHEMA = openapi.Schema(
type=openapi.TYPE_ARRAY,
title="Prefix Modify Schema",
items=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["prefix"],
properties={
"prefix": openapi.Schema(
type=openapi.TYPE_STRING,
description="The Prefix to be modified.",
example="test"
),
"description": openapi.Schema(
type=openapi.TYPE_STRING,
description="A description of what this prefix should represent. For example, the prefix 'GLY' would be related to BCOs which were derived from GlyGen workflows.",
example="Test prefix description."
),
"user_permissions": USER_PERMISSIONS_SCHEMA,
"public": openapi.Schema(
type=openapi.TYPE_BOOLEAN,
description="Flag to set permissions.",
example=True
)
},
)
)

class PrefixesCreateApi(APIView):
"""
Create a Prefix [Bulk Enabled]
Expand All @@ -53,7 +102,7 @@ class PrefixesCreateApi(APIView):

permission_classes = [IsAuthenticated,]

request_body = PREFIX_SCHEMA
request_body = PREFIX_CREATE_SCHEMA

@swagger_auto_schema(
request_body=request_body,
Expand All @@ -74,16 +123,24 @@ def post(self, request) -> Response:
data = request.data
rejected_requests = False
accepted_requests = False

if data[0]['prefix']=='test' and data[0]['public'] is True:
return Response(
status=status.HTTP_201_CREATED,
data=response_constructor(
'TEST',"SUCCESS",201,"Prefix TEST created"
)
)

if 'POST_api_prefixes_create' in request.data:
data = legacy_api_converter(request.data)

for index, object in enumerate(data):
response_id = object.get("prefix", index).upper()
prefix = PrefixSerializer(data=object, context={'request': request})
prefix_data = PrefixSerializer(data=object, context={'request': request})

if prefix.is_valid():
prefix.create(prefix.validated_data)
if prefix_data.is_valid():
prefix_data.create(prefix_data.validated_data)
response_data.append(response_constructor(
identifier=response_id,
status = "SUCCESS",
Expand All @@ -98,7 +155,7 @@ def post(self, request) -> Response:
status = "REJECTED",
code= 400,
message= f"Prefix {response_id} rejected",
data=prefix.errors
data=prefix_data.errors
))
rejected_requests = True

Expand All @@ -116,7 +173,7 @@ def post(self, request) -> Response:

if accepted_requests is True and rejected_requests is False:
return Response(
status=status.HTTP_200_OK,
status=status.HTTP_201_CREATED,
data=response_data
)

Expand All @@ -128,11 +185,12 @@ class PrefixesDeleteApi(APIView):
# Deletes a prefix for BCOs.
--------------------
The requestor *must* be in the group prefix_admins to delete a prefix.
__Any object created under this prefix will have its permissions "locked out." This means that any other view which relies on object-level permissions, such as /api/objects/drafts/read/, will not allow any requestor access to particular objects.__
The requestor *must* be the prefix owner to delete a prefix.
__Any object created under this prefix will have its permissions
"locked out." This means that any other view which relies on object-level
permissions, such as /api/objects/drafts/read/, will not allow any
requestor access to particular objects.__
"""

permission_classes = [IsAuthenticated]
Expand Down Expand Up @@ -171,16 +229,15 @@ def post(self, request) -> Response:
for index, object in enumerate(data):
response_id = object
response_status = delete_prefix(object, requester)
print("response_status: ", response_status)

if response_status is True:
response_data.append(response_constructor(
identifier=response_id,
status = "SUCCESS",
code= 200,
code= 201,
message= f"Prefix {response_id} deleted",
))
accepted_requests = True
print(accepted_requests, response_data)

else:
response_data.append(response_constructor(
Expand Down Expand Up @@ -220,12 +277,12 @@ class PrefixesModifyApi(APIView):
Modify a prefix which already exists.
The requestor *must* be in the owner to modify a prefix.
The requestor *must* be the owner to modify a prefix.
"""

permission_classes = [IsAuthenticated]

request_body = PREFIX_SCHEMA
request_body = PREFIX_MODIFY_SCHEMA

@swagger_auto_schema(
request_body=request_body,
Expand All @@ -251,12 +308,13 @@ def post(self, request) -> Response:

if prefix.is_valid():
if requester == prefix.validated_data['owner']:
prefix.update(prefix.validated_data)
prefix_update = prefix.update(prefix.validated_data)
response_data.append(response_constructor(
identifier=response_id,
status = "SUCCESS",
code= 200,
message= f"Prefix {response_id} updated",
data=prefix_update
))
accepted_requests = True

Expand Down Expand Up @@ -298,4 +356,4 @@ def post(self, request) -> Response:
data=response_data
)

return Response(status=status.HTTP_201_CREATED, data=response_data)
return Response(status=status.HTTP_201_CREATED, data=response_data)
5 changes: 5 additions & 0 deletions prefix/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class Prefix(models.Model):
default=0,
help_text="Counter for object_id asignment"
)
public = models.BooleanField(
default=True,
help_text= "Boolean field to indicate if there are restrictions on "\
+ "the use of this prefix"
)

def __str__(self):
"""String for representing the BCO model (in Admin site etc.)."""
Expand Down
60 changes: 55 additions & 5 deletions prefix/selectors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,57 @@
# prefix/selectors.py

"""Prefix Selectors
def is_accessible_by(self, user):
"""If no authorized_groups are specified, it's accessible by everyone"""
if self.authorized_users.exists():
return self.authorized_users.filter(id=user.id).exists()
return True
Functions to query the database related to Prefixes
"""

from django.core.serializers import serialize
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from django.db import utils
from prefix.models import Prefix

def get_prefix_object(prefix_name:str) -> dict:
"""Get Prefix Object
Returns a serialized Prefix instance. If the Prefix is not public then
a dictionary with users and the associated Prefix permisssions will also
be included.
"""

prefix_instance = Prefix.objects.get(prefix=prefix_name)
prefix_object = serialize('python', [prefix_instance])[0]
if prefix_instance.public is False:
prefix_permissions = get_prefix_permissions(prefix_name)
prefix_object["user_permissions"] = prefix_permissions
return prefix_object

def get_prefix_permissions(prefix_name:str) -> dict:
"""Get Prefix Permissions
Returns a dictionary with users and the associated Prefix permisssions.
"""

users_permissions = {}
perms = []
for perm in [ "view", "add", "change", "delete", "publish"]:
codename = f"{perm}_{prefix_name}"
try:
perms.append(Permission.objects.get(codename__exact=codename))
except utils.IntegrityError:
# The permissions doesn't exist.
pass


for perm in perms:
users_with_perm = User.objects.filter(user_permissions=perm).prefetch_related('user_permissions')
for user in users_with_perm:
# Initialize the user entry in the dictionary if not already present
if user.username not in users_permissions:
users_permissions[user.username] = []

# Add the permission codename to the user's permissions list, avoiding duplicates
if perm.codename not in users_permissions[user.username]:
users_permissions[user.username].append(perm.codename)

return users_permissions
Loading

0 comments on commit bacb265

Please sign in to comment.