Skip to content

Commit

Permalink
Remove permission for public AF and Changes on mutation
Browse files Browse the repository at this point in the history
Add PR Changes
  • Loading branch information
susilnem committed Jul 22, 2024
1 parent 0adfe5f commit 677631e
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 48 deletions.
10 changes: 4 additions & 6 deletions apps/analysis_framework/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import copy
from typing import Union
from typing import Union, Optional

from django.db import models
from django.core.exceptions import ValidationError
Expand Down Expand Up @@ -63,15 +63,13 @@ def __init__(self, *args, **kwargs):
def __str__(self):
return self.title

def clone(self, user, overrides={}):
def clone(self, user, title: Optional[str] = None, description: Optional[str] = None):
"""
Clone analysis framework along with all widgets,
filters and exportables
"""
title = overrides.get(
'title', '{} (cloned)'.format(self.title[:230])
) # Strip off extra chars from title
description = overrides.get('description', '')
title = title or "{} (cloned)".format(self.title[:230]) # Strip off extra chars from title
description = description or ""
clone_analysis_framework = AnalysisFramework(
title=title,
description=description,
Expand Down
9 changes: 6 additions & 3 deletions apps/analysis_framework/mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,16 @@ def get_valid_delete_items(cls, info, delete_ids):
)


class CloneAnalysisFramework(AfGrapheneMutation):
class CloneAnalysisFramework(GrapheneMutation):
class Arguments:
data = AnalysisFrameworkCloneInputType(required=True)

result = graphene.Field(AnalysisFrameworkDetailType)
serializer_class = AnalysisFrameworkCloneSerializer
permissions = [AfP.Permission.CAN_CLONE_FRAMEWORK]

@classmethod
def check_permissions(cls, info, **_):
return True # NOTE: Checking permissions in serializer


class AnalysisFrameworkMutationType(DjangoObjectType):
Expand All @@ -117,7 +120,6 @@ class AnalysisFrameworkMutationType(DjangoObjectType):
"""
analysis_framework_update = UpdateAnalysisFramework.Field()
analysis_framework_membership_bulk = BulkUpdateAnalysisFrameworkMembership.Field()
analysis_framework_clone = CloneAnalysisFramework.Field()

class Meta:
model = AnalysisFramework
Expand All @@ -136,4 +138,5 @@ def get_custom_node(_, info, id):

class Mutation(object):
analysis_framework_create = CreateAnalysisFramework.Field()
analysis_framework_clone = CloneAnalysisFramework.Field()
analysis_framework = DjangoObjectField(AnalysisFrameworkMutationType)
25 changes: 23 additions & 2 deletions apps/analysis_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,18 +712,39 @@ def create(self, validated_data):


class AnalysisFrameworkCloneSerializer(serializers.Serializer):
af_id = IntegerIDField(required=True)
title = serializers.CharField(required=True)
description = serializers.CharField(required=False)
project = serializers.PrimaryKeyRelatedField(queryset=Project.objects.all(), required=False)

def validate_af_id(self, af_id):
af = (
AnalysisFramework
.get_for_gq(self.context['request'].user, only_member=False)
.filter(pk=af_id)
.first()
)
if af is None:
raise serializers.ValidationError('Analysis Framework does not exist')
if not af.can_clone(self.context['request'].user):
raise serializers.ValidationError(
'User does not have permission to modify the AF'
)
return af

def validate_project(self, project):
if not project.can_modify(self.context['request'].user):
raise serializers.ValidationError('User does not have permission to modify the project')
return project

def create(self, validated_data):
af = self.context['request'].active_af
new_af = af.clone(self.context['request'].user, validated_data)
# NOTE: af is an obj, check validate_af_id
af = validated_data['af_id']
new_af = af.clone(
self.context['request'].user,
title=validated_data['title'],
description=validated_data.get('description')
)
new_af.add_member(self.context['request'].user, new_af.get_or_create_owner_role())

if project := validated_data.get('project'):
Expand Down
63 changes: 28 additions & 35 deletions apps/analysis_framework/tests/test_mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,18 +786,16 @@ def _query_check(**kwargs):

def test_analysis_framework_clone(self):
query = '''
mutation MyMutation ($id: ID!, $input: AnalysisFrameworkCloneInputType!) {
mutation MyMutation ($input: AnalysisFrameworkCloneInputType!) {
__typename
analysisFramework (id: $id ) {
analysisFrameworkClone(data: $input) {
ok
errors
result {
id
title
description
clonedFrom
}
analysisFrameworkClone(data: $input) {
ok
errors
result {
id
title
description
clonedFrom
}
}
}
Expand All @@ -815,65 +813,60 @@ def test_analysis_framework_clone(self):
private_af = AnalysisFrameworkFactory.create(created_by=member_user, title='AF Private Orginal', is_private=True)
private_af.add_member(low_permission_user)

minput = dict(
public_minput = dict(
title='AF (TEST)',
description='Af description',
afId=af.id,
)
private_minput = dict(
title='AF (TEST)',
description='Af description',
afId=private_af.id,
)

def _public_af_query_check(**kwargs):
return self.query_check(
query,
minput=minput,
mnested=['analysisFramework'],
variables={'id': af.id},
minput=public_minput,
**kwargs,
)

def _private_af_query_check(**kwargs):
return self.query_check(
query,
minput=minput,
mnested=['analysisFramework'],
variables={'id': private_af.id},
minput=private_minput,
**kwargs,
)

# ---------- Without login
_public_af_query_check(assert_for_error=True)
# ---------- With login
self.force_login(non_member_user)
# ---------- Let's Clone a new AF (Using create test data)
_public_af_query_check(assert_for_error=True)

# ---------- With login (with access member)
self.force_login(member_user)
# ---------- With login (with non access member) on public AF
self.force_login(non_member_user)
response = _public_af_query_check()
self.assertEqual(response['data']['analysisFramework']['analysisFrameworkClone']['result']['clonedFrom'], str(af.id))
self.assertEqual(response['data']['analysisFrameworkClone']['result']['clonedFrom'], str(af.id))

# adding project to the input
minput['project'] = project.id
# adding project to the inputs
public_minput['project'] = project.id
private_minput['project'] = project.id

# with Login (non project member)
self.force_login(non_member_user)
_public_af_query_check(assert_for_error=True)

# with Login (project member with no permission on AF)
self.force_login(low_permission_user)
_public_af_query_check(assert_for_error=True)
_public_af_query_check(okay=False)

# with Login (project member with no permission on Private AF)
self.force_login(member_user)
_private_af_query_check(assert_for_error=True)
_private_af_query_check(okay=False)

# With Login (project member with permission on Public AF)
self.force_login(member_user)
response = _public_af_query_check()['data']['analysisFramework']
response = _public_af_query_check()['data']
project.refresh_from_db()
self.assertEqual(str(project.analysis_framework_id), response['analysisFrameworkClone']['result']['id'])

# with Login (project member with permission on Private AF)
private_af.add_member(member_user, role=self.af_owner)
response = _private_af_query_check()['data']['analysisFramework']
response = _private_af_query_check()['data']
project.refresh_from_db()
self.assertEqual(str(project.analysis_framework_id), response['analysisFrameworkClone']['result']['id'])

Expand Down
3 changes: 2 additions & 1 deletion apps/analysis_framework/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ def post(self, request, af_id, version=None):

new_af = analysis_framework.clone(
request.user,
request.data or {},
title=cloned_title,
description=request.data.get('description'),
)
# Set the requesting user as owner member, don't create other memberships of old framework
new_af.add_member(request.user, new_af.get_or_create_owner_role())
Expand Down
3 changes: 2 additions & 1 deletion schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type AnalysisAutomaticSummaryType {
}

input AnalysisFrameworkCloneInputType {
afId: ID!
title: String!
description: String
project: ID
Expand Down Expand Up @@ -146,7 +147,6 @@ type AnalysisFrameworkMutationType {
title: String!
analysisFrameworkUpdate(data: AnalysisFrameworkInputType!): UpdateAnalysisFramework
analysisFrameworkMembershipBulk(deleteIds: [ID!], items: [BulkAnalysisFrameworkMembershipInputType!]): BulkUpdateAnalysisFrameworkMembership
analysisFrameworkClone(data: AnalysisFrameworkCloneInputType!): CloneAnalysisFramework
}

enum AnalysisFrameworkPermission {
Expand Down Expand Up @@ -5064,6 +5064,7 @@ type Mutation {
updateMe(data: UserMeInputType!): UpdateMe
deleteUser: UserDelete
analysisFrameworkCreate(data: AnalysisFrameworkInputType!): CreateAnalysisFramework
analysisFrameworkClone(data: AnalysisFrameworkCloneInputType!): CloneAnalysisFramework
analysisFramework(id: ID!): AnalysisFrameworkMutationType
}

Expand Down

0 comments on commit 677631e

Please sign in to comment.