Skip to content

Commit

Permalink
Merge pull request #68 from multinet-app/user-permission-with-level
Browse files Browse the repository at this point in the history
Return both permission level and label from user permissions endpoint
  • Loading branch information
jjnesbitt authored Aug 23, 2021
2 parents 570b863 + 79307b4 commit 11fc729
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 40 deletions.
36 changes: 18 additions & 18 deletions multinet/api/models/workspace.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import List, Optional, Type
from typing import List, Optional, Tuple, Type, Union
from uuid import uuid4

from arango.database import StandardDatabase
Expand Down Expand Up @@ -81,31 +81,31 @@ def get_user_permission(self, user: User) -> Optional[WorkspaceRole]:
"""Get the WorkspaceRole for a given user on this workspace."""
return WorkspaceRole.objects.filter(workspace=self.pk, user=user.pk).first()

def get_user_permission_label(self, user: User) -> Optional[str]:
def get_user_permission_tuple(self, user: User) -> Union[Tuple[int, str], Tuple[None, None]]:
"""
Get a the string label of a user's workspace role, or None.
This function differs from Workspace::get_user_permisssion. Whereas that
function returns a WorkspaceRole object describing a user's permission for
a particular workspace, this function returns a human-readable representation
of that permission level. Since ownership of a workspace is modeled by a property
on that workspace, this function takes that into account, and returns 'owner' if
the given user is the owner of this workspace. Moreover, even if a user does not
have explicit permission via the ownersip relationship or a WorkspaceRole object,
they are still considered a reader if this workspace is public. Therefore, if this
workspace is public and the user passed in has no explicit permission, 'reader' will
be returned.
Return a tuple of the user's permission level and string label, or (None, None).
This function differs from Workspace().get_user_permission, as that function returns a
WorkspaceRole object from the database, and this function returns a tuple of the level and
label corresponding to the 'role' field of that object. Since ownership of a workspace is
modeled by a field on that workspace, get_user_permission doesn't address ownership at all,
whereas this function returns the artificail tuple (4, 'owner') if the given user is the
owner of this workspace. Moreover, if a user has no permission on this workspace, or is the
anonymous user (not logged in), but the workspace is public, the reader tuple is returned.
"""
if self.owner == user:
return 'owner'
return 4, 'owner'

workspace_role = self.get_user_permission(user)
if workspace_role is None:
if self.public:
return WorkspaceRoleChoice.READER.label
return None
return WorkspaceRoleChoice.READER.value, WorkspaceRoleChoice.READER.label
return None, None
else:
return WorkspaceRoleChoice(workspace_role.role).label
return (
WorkspaceRoleChoice(workspace_role.role).value,
WorkspaceRoleChoice(workspace_role.role).label,
)

def set_user_permission(self, user: User, permission: WorkspaceRoleChoice) -> bool:
"""
Expand Down
7 changes: 5 additions & 2 deletions multinet/api/tests/test_workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,12 @@ def test_workspace_rest_get_user_permission(
assert r.status_code == status_code

if success:
permission, permission_label = workspace.get_user_permission_tuple(user)
assert r.data == {
'username': user.username,
'workspace': workspace.name,
'permission': workspace.get_user_permission_label(user),
'permission': permission,
'permission_label': permission_label,
}


Expand All @@ -410,5 +412,6 @@ def test_workspace_rest_get_user_permission_public(
assert r.data == {
'username': '', # anonymous user
'workspace': workspace.name,
'permission': WorkspaceRoleChoice.READER.label,
'permission': WorkspaceRoleChoice.READER.value,
'permission_label': WorkspaceRoleChoice.READER.label,
}
3 changes: 2 additions & 1 deletion multinet/api/views/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ class SingleUserWorkspacePermissionSerializer(serializers.Serializer):
# Allow empty username since anonymous user is a reader for public workspaces
username = serializers.CharField(validators=[UnicodeUsernameValidator()], allow_blank=True)
workspace = serializers.CharField()
permission = serializers.CharField(allow_blank=True, allow_null=True)
permission = serializers.IntegerField(allow_null=True)
permission_label = serializers.CharField(allow_null=True)


# The required fields for table creation
Expand Down
44 changes: 25 additions & 19 deletions multinet/api/views/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@
from .common import MultinetPagination


def build_user_list(validated_data: OrderedDict) -> list:
"""
Build a list of user objects from an ordered dictionary of user data.
Accepts an ordered dictionary containing a list of validated user data, e.g.
as a result of validating a PermissionsSerializer with request data.
Returns a list of user objects.
"""
user_list = []
for valid_user in validated_data:
user_object = get_object_or_404(User, username=valid_user['username'])
user_list.append(user_object)
return user_list


class WorkspaceViewSet(ReadOnlyModelViewSet):
queryset = Workspace.objects.select_related('owner').all()
lookup_field = 'name'
Expand Down Expand Up @@ -117,27 +132,18 @@ def get_current_user_workspace_permissions(self, request, name: str):
"""Get the workspace permission for the user of the request."""
workspace: Workspace = get_object_or_404(Workspace, name=name)
user = request.user
permission = workspace.get_user_permission_label(user)
permission, permission_label = workspace.get_user_permission_tuple(user)
data = {
'username': user.username,
'workspace': name,
'permission': permission,
'permission_label': permission_label,
}

data = {'username': user.username, 'workspace': name, 'permission': permission}
serializer = SingleUserWorkspacePermissionSerializer(data=data)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)

def build_user_list(self, validated_data: OrderedDict) -> list:
"""
Build a list of user objects from an ordered dictionary of user data.
Accepts an ordered dictionary containing a list of validated user data, e.g.
as a result of validating a PermissionsSerializer with request data.
Returns a list of user objects.
"""
user_list = []
for valid_user in validated_data:
user_object = get_object_or_404(User, username=valid_user['username'])
user_list.append(user_object)
return user_list

@swagger_auto_schema(
request_body=PermissionsCreateSerializer(), responses={200: PermissionsReturnSerializer()}
)
Expand All @@ -154,9 +160,9 @@ def put_workspace_permissions(self, request, name: str):
workspace.public = validated_data['public']
workspace.save()

new_readers = self.build_user_list(validated_data['readers'])
new_writers = self.build_user_list(validated_data['writers'])
new_maintainers = self.build_user_list(validated_data['maintainers'])
new_readers = build_user_list(validated_data['readers'])
new_writers = build_user_list(validated_data['writers'])
new_maintainers = build_user_list(validated_data['maintainers'])
workspace.set_user_permissions_bulk(
readers=new_readers, writers=new_writers, maintainers=new_maintainers
)
Expand Down

0 comments on commit 11fc729

Please sign in to comment.