Skip to content

Commit

Permalink
clean-up more preprint permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
John Tordoff committed Aug 1, 2024
1 parent bd7f6d6 commit 76e803c
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 47 deletions.
41 changes: 41 additions & 0 deletions api/preprints/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from addons.osfstorage.models import OsfStorageFolder
from osf.utils.workflows import DefaultStates
from osf.utils import permissions as osf_permissions
from osf.utils.permissions import ADMIN


class PreprintPublishedOrAdmin(permissions.BasePermission):
Expand Down Expand Up @@ -53,6 +54,46 @@ def has_object_permission(self, request, view, obj):
return True


class PreprintCitationPermissions(permissions.BasePermission):

acceptable_models = (Preprint,)

def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
if not auth.user:
return obj.verified_publishable

if obj.is_published:
return True

if obj.is_public and obj.has_submitted_preprint:
return True

if obj.deleted:
return False

if obj.is_preprint_orphan:
return False

if obj.is_retracted and not obj.ever_public:
return False

if obj.verified_publishable:
return True

if obj.is_public and auth.user.has_perm('view_submissions', obj.provider):
return True

if obj.has_permission(auth.user, ADMIN):
return True

if obj.is_contributor(auth.user) and obj.has_submitted_preprint:
return True

return False


class ContributorDetailPermissions(PreprintPublishedOrAdmin):
"""Permissions for preprint contributor detail page."""

Expand Down
63 changes: 29 additions & 34 deletions api/preprints/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
AdminOrPublic,
ContributorDetailPermissions,
PreprintFilesPermissions,
PreprintCitationPermissions
)
from api.nodes.permissions import (
ContributorOrPublic,
Expand All @@ -62,6 +63,7 @@
from api.base.metrics import PreprintMetricsViewMixin
from osf.metrics import PreprintDownload, PreprintView


class PreprintMixin(NodeMixin):
serializer_class = PreprintSerializer
preprint_lookup_url_kwarg = 'preprint_id'
Expand All @@ -84,6 +86,7 @@ def get_preprint(self, check_object_permissions=True, ignore_404=False):

return preprint


class PreprintList(PreprintMetricsViewMixin, JSONAPIBaseView, generics.ListCreateAPIView, PreprintFilterMixin):
"""The documentation for this endpoint can be found [here](https://developer.osf.io/#operation/preprints_list).
"""
Expand Down Expand Up @@ -219,11 +222,13 @@ def get_object(self):
preprint = self.get_preprint()
auth = get_user_auth(self.request)
type_ = 'linked_preprint_nodes' if Version(self.request.version) < Version('2.13') else 'nodes'
obj = {
'data': {'id': preprint.node._id, 'type': type_} if preprint.node and preprint.node.can_view(auth) else None,
return {
'data': {
'id': preprint.node._id,
'type': type_
} if preprint.node and preprint.node.can_view(auth) else None,
'self': preprint,
}
return obj


class PreprintCitationDetail(JSONAPIBaseView, generics.RetrieveAPIView, PreprintMixin):
Expand All @@ -232,6 +237,7 @@ class PreprintCitationDetail(JSONAPIBaseView, generics.RetrieveAPIView, Preprint
permission_classes = (
drf_permissions.IsAuthenticatedOrReadOnly,
base_permissions.TokenHasScope,
PreprintCitationPermissions
)

required_read_scopes = [CoreScopes.PREPRINT_CITATIONS_READ]
Expand All @@ -242,13 +248,7 @@ class PreprintCitationDetail(JSONAPIBaseView, generics.RetrieveAPIView, Preprint
view_name = 'preprint-citation'

def get_object(self):
preprint = self.get_preprint()
auth = get_user_auth(self.request)

if preprint.can_view(auth):
return preprint.csl

raise PermissionDenied if auth.user else NotAuthenticated
return self.get_preprint().csl


class PreprintCitationStyleDetail(JSONAPIBaseView, generics.RetrieveAPIView, PreprintMixin):
Expand All @@ -257,6 +257,7 @@ class PreprintCitationStyleDetail(JSONAPIBaseView, generics.RetrieveAPIView, Pre
permission_classes = (
drf_permissions.IsAuthenticatedOrReadOnly,
base_permissions.TokenHasScope,
PreprintCitationPermissions
)

required_read_scopes = [CoreScopes.PREPRINT_CITATIONS_READ]
Expand All @@ -268,19 +269,18 @@ class PreprintCitationStyleDetail(JSONAPIBaseView, generics.RetrieveAPIView, Pre

def get_object(self):
preprint = self.get_preprint()
auth = get_user_auth(self.request)
style = self.kwargs.get('style_id')

if preprint.can_view(auth):
try:
citation = render_citation(node=preprint, style=style)
except ValueError as err: # style requested could not be found
csl_name = re.findall(r'[a-zA-Z]+\.csl', str(err))[0]
raise NotFound(f'{csl_name} is not a known style.')

return {'citation': citation, 'id': style}

raise PermissionDenied if auth.user else NotAuthenticated
try:
citation = render_citation(node=preprint, style=style)
except ValueError as err: # style requested could not be found
csl_name = re.findall(r'[a-zA-Z]+\.csl', str(err))[0]
raise NotFound(f'{csl_name} is not a known style.')

return {
'citation': citation,
'id': style
}


class PreprintIdentifierList(IdentifierList, PreprintMixin):
Expand Down Expand Up @@ -332,8 +332,8 @@ class PreprintIdentifierList(IdentifierList, PreprintMixin):
view_name = 'identifier-list'

# overrides IdentifierList
def get_object(self, check_object_permissions=True):
return self.get_preprint(check_object_permissions=check_object_permissions)
def get_object(self):
return self.get_preprint()


class PreprintContributorsList(NodeContributorsList, PreprintMixin):
Expand All @@ -354,8 +354,7 @@ class PreprintContributorsList(NodeContributorsList, PreprintMixin):
serializer_class = PreprintContributorsSerializer

def get_default_queryset(self):
preprint = self.get_preprint()
return preprint.preprintcontributor_set.all().prefetch_related('user__guids')
return self.get_preprint().preprintcontributor_set.all().prefetch_related('user__guids')

# overrides NodeContributorsList
def get_serializer_class(self):
Expand All @@ -370,7 +369,7 @@ def get_serializer_class(self):
return PreprintContributorsSerializer

def get_resource(self):
return self.get_preprint(ignore_404=True)
return self.get_preprint()

# Overrides NodeContributorsList
def get_serializer_context(self):
Expand Down Expand Up @@ -402,8 +401,6 @@ def get_resource(self):
def get_object(self):
preprint = self.get_preprint()
user = self.get_user()
# May raise a permission denied
self.check_object_permissions(self.request, user)
try:
return preprint.preprintcontributor_set.get(user=user)
except PreprintContributor.DoesNotExist:
Expand Down Expand Up @@ -474,17 +471,15 @@ class PreprintSubjectsRelationship(SubjectRelationshipBaseView, PreprintMixin):
view_category = 'preprints'
view_name = 'preprint-relationships-subjects'

def get_resource(self, check_object_permissions=True):
return self.get_preprint(check_object_permissions=check_object_permissions)
def get_resource(self):
return self.get_preprint()

def get_object(self):
resource = self.get_resource(check_object_permissions=False)
obj = {
resource = self.get_resource()
return {
'data': resource.subjects.all(),
'self': resource,
}
self.check_object_permissions(self.request, resource)
return obj


class PreprintActionList(JSONAPIBaseView, generics.ListCreateAPIView, ListFilterMixin, PreprintMixin):
Expand Down
27 changes: 14 additions & 13 deletions api/requests/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def has_object_permission(self, request, view, obj):
class PreprintRequestPermission(drf_permissions.BasePermission):
def has_object_permission(self, request, view, obj):
auth = get_user_auth(request)

if auth.user is None:
return False

Expand All @@ -69,30 +70,30 @@ def has_object_permission(self, request, view, obj):
elif isinstance(obj, PreprintRequestableMixin):
target = obj
preprint = obj.target
# Creating a Request is "submitting"
trigger = request.data.get('trigger', DefaultTriggers.SUBMIT.value if request.method not in drf_permissions.SAFE_METHODS else None)
trigger = request.data.get(
'trigger',
DefaultTriggers.SUBMIT.value if request.method not in drf_permissions.SAFE_METHODS else None
)
elif isinstance(obj, Preprint):
preprint = obj
trigger = DefaultTriggers.SUBMIT.value if request.method not in drf_permissions.SAFE_METHODS else None
else:
raise ValueError(f'Not a request-related model: {obj}')

is_requester = target is not None and target.creator == auth.user or trigger == DefaultTriggers.SUBMIT.value
is_requester = (target is not None and target.creator == auth.user) or trigger == DefaultTriggers.SUBMIT.value
is_preprint_admin = preprint.has_permission(auth.user, osf_permissions.ADMIN)
is_moderator = auth.user.has_perm('withdraw_submissions', preprint.provider)
has_view_permission = is_requester or is_preprint_admin or is_moderator

if request.method in drf_permissions.SAFE_METHODS:
# Requesters, moderators, and preprint admins can view actions
return has_view_permission
else:
if not has_view_permission:
return False

if trigger in [DefaultTriggers.ACCEPT.value, DefaultTriggers.REJECT.value]:
# Only moderators can approve or reject requests
return is_moderator
if trigger in [DefaultTriggers.EDIT_COMMENT.value, DefaultTriggers.SUBMIT.value]:
# Requesters may edit their comment or submit their request
return is_requester
if not has_view_permission:
return False

if trigger in [DefaultTriggers.ACCEPT.value, DefaultTriggers.REJECT.value]:
return is_moderator
if trigger in [DefaultTriggers.EDIT_COMMENT.value, DefaultTriggers.SUBMIT.value]:
return is_requester

return False

0 comments on commit 76e803c

Please sign in to comment.