Beispiel #1
0
    def set_supplemental_node(self, node, auth, save=False):
        if not self.has_permission(auth.user, 'write'):
            raise PermissionsError(
                'You must have write permissions to set a supplemental node.')

        if not node.has_permission(auth.user, 'write'):
            raise PermissionsError(
                'You must have write permissions on the supplemental node to attach.'
            )

        if node.is_deleted:
            raise ValueError('Cannot attach a deleted project to a preprint.')

        self.node = node

        self.add_log(
            action=PreprintLog.SUPPLEMENTAL_NODE_ADDED,
            params={
                'preprint': self._id,
                'node': self.node._id,
            },
            auth=auth,
            save=False,
        )

        if save:
            self.save()
Beispiel #2
0
    def auth_callback(self, user, **kwargs):
        """Exchange temporary credentials for permanent credentials

        This is called in the view that handles the user once they are returned
        to the OSF after authenticating on the external service.
        """

        if 'error' in request.args:
            return False

        # make sure the user has temporary credentials for this provider
        try:
            cached_credentials = session.data['oauth_states'][self.short_name]
        except KeyError:
            raise PermissionsError('OAuth flow not recognized.')

        if self._oauth_version == OAUTH1:
            request_token = request.args.get('oauth_token')

            # make sure this is the same user that started the flow
            if cached_credentials.get('token') != request_token:
                raise PermissionsError('Request token does not match')

            response = OAuth1Session(
                client_key=self.client_id,
                client_secret=self.client_secret,
                resource_owner_key=cached_credentials.get('token'),
                resource_owner_secret=cached_credentials.get('secret'),
                verifier=request.args.get('oauth_verifier'),
            ).fetch_access_token(self.callback_url)

        elif self._oauth_version == OAUTH2:
            state = request.args.get('state')

            # make sure this is the same user that started the flow
            if cached_credentials.get('state') != state:
                raise PermissionsError('Request token does not match')

            try:
                response = OAuth2Session(
                    self.client_id,
                    redirect_uri=web_url_for(
                        'oauth_callback',
                        service_name=self.short_name,
                        _absolute=True
                    ),
                ).fetch_token(
                    self.callback_url,
                    client_secret=self.client_secret,
                    code=request.args.get('code'),
                )
            except (MissingTokenError, RequestsHTTPError):
                raise HTTPError(http.SERVICE_UNAVAILABLE)
        # pre-set as many values as possible for the ``ExternalAccount``
        info = self._default_handle_callback(response)
        # call the hook for subclasses to parse values from the response
        info.update(self.handle_callback(response))

        return self._set_external_account(user, info)
    def set_subjects(self, new_subjects, auth, add_log=True):
        """ Helper for setting M2M subjects field from list of hierarchies received from UI.
        Only authorized admins may set subjects.

        :param list[list[Subject._id]] new_subjects: List of subject hierarchies to be validated and flattened
        :param Auth auth: Auth object for requesting user
        :param bool add_log: Whether or not to add a log (if called on a Loggable object)

        :return: None
        """
        AbstractNode = apps.get_model('osf.AbstractNode')
        PreprintService = apps.get_model('osf.PreprintService')
        CollectionSubmission = apps.get_model('osf.CollectionSubmission')
        if getattr(self, 'is_registration', False):
            raise PermissionsError('Registrations may not be modified.')
        if isinstance(self, (AbstractNode, PreprintService)):
            if not self.has_permission(auth.user, ADMIN):
                raise PermissionsError('Only admins can change subjects.')
        elif isinstance(self, CollectionSubmission):
            if not self.guid.referent.has_permission(
                    auth.user, ADMIN) and not auth.user.has_perms(
                        self.collection.groups[ADMIN], self.collection):
                raise PermissionsError('Only admins can change subjects.')

        old_subjects = list(self.subjects.values_list('id', flat=True))
        self.subjects.clear()
        for subj_list in new_subjects:
            subj_hierarchy = []
            for s in subj_list:
                subj_hierarchy.append(s)
            if subj_hierarchy:
                validate_subject_hierarchy(subj_hierarchy)
                for s_id in subj_hierarchy:
                    self.subjects.add(Subject.load(s_id))

        if add_log and hasattr(self, 'add_log'):
            self.add_log(
                action=NodeLog.SUBJECTS_UPDATED,
                params={
                    'subjects':
                    list(self.subjects.values('_id', 'text')),
                    'old_subjects':
                    list(
                        Subject.objects.filter(id__in=old_subjects).values(
                            '_id', 'text'))
                },
                auth=auth,
                save=False,
            )

        self.save(old_subjects=old_subjects)
Beispiel #4
0
    def set_subjects(self, preprint_subjects, auth, log=True):
        if not self.has_permission(auth.user, 'write'):
            raise PermissionsError(
                'Must have admin or write permissions to change a preprint\'s subjects.'
            )

        old_subjects = list(self.subjects.values_list('id', flat=True))
        self.subjects.clear()
        for subj_list in preprint_subjects:
            subj_hierarchy = []
            for s in subj_list:
                subj_hierarchy.append(s)
            if subj_hierarchy:
                validate_subject_hierarchy(subj_hierarchy)
                for s_id in subj_hierarchy:
                    self.subjects.add(Subject.load(s_id))

        if log:
            self.add_log(
                action=PreprintLog.SUBJECTS_UPDATED,
                params={
                    'subjects':
                    list(self.subjects.values('_id', 'text')),
                    'old_subjects':
                    list(
                        Subject.objects.filter(id__in=old_subjects).values(
                            '_id', 'text')),
                    'preprint':
                    self._id
                },
                auth=auth,
                save=False,
            )

        self.save(old_subjects=old_subjects)
Beispiel #5
0
 def delete(self, auth, save=False):
     if not self.node.can_comment(auth) or self.user._id != auth.user._id:
         raise PermissionsError(
             '{0!r} does not have permission to comment on this node'.
             format(auth.user))
     log_dict = {
         'project': self.node.parent_id,
         'node': self.node._id,
         'user': self.user._id,
         'comment': self._id,
     }
     self.is_deleted = True
     current_time = timezone.now()
     self.deleted = current_time
     log_dict.update(self.root_target.referent.get_extra_log_params(self))
     self.modified = current_time
     if save:
         self.save()
         self.node.add_log(
             NodeLog.COMMENT_REMOVED,
             log_dict,
             auth=auth,
             save=False,
         )
         self.node.save()
Beispiel #6
0
    def add_unclaimed_record(self, node, referrer, given_name, email=None):
        """Add a new project entry in the unclaimed records dictionary.

        :param Node node: Node this unclaimed user was added to.
        :param User referrer: User who referred this user.
        :param str given_name: The full name that the referrer gave for this user.
        :param str email: The given email address.
        :returns: The added record
        """
        if not node.can_edit(user=referrer):
            raise PermissionsError(
                'Referrer does not have permission to add a contributor '
                'to project {0}'.format(node._primary_key))
        project_id = node._primary_key
        referrer_id = referrer._primary_key
        if email:
            clean_email = email.lower().strip()
        else:
            clean_email = None
        record = {
            'name': given_name,
            'referrer_id': referrer_id,
            'token': generate_confirm_token(),
            'email': clean_email
        }
        self.unclaimed_records[project_id] = record
        return record
Beispiel #7
0
    def set_primary_file(self, preprint_file, auth, save=False):
        if not self.node.has_permission(auth.user, ADMIN):
            raise PermissionsError('Only admins can change a preprint\'s primary file.')

        if not isinstance(preprint_file, StoredFileNode):
            preprint_file = preprint_file.stored_object

        if preprint_file.node != self.node or preprint_file.provider != 'osfstorage':
            raise ValueError('This file is not a valid primary file for this preprint.')

        # there is no preprint file yet! This is the first time!
        if not self.node.preprint_file:
            self.node.preprint_file = preprint_file
        elif preprint_file != self.node.preprint_file:
            # if there was one, check if it's a new file
            self.node.preprint_file = preprint_file
            self.node.add_log(
                action=NodeLog.PREPRINT_FILE_UPDATED,
                params={},
                auth=auth,
                save=False,
            )

        if save:
            self.save()
            self.node.save()
Beispiel #8
0
    def embargo_registration(self, user, end_date, for_existing_registration=False,
                             notify_initiator_on_complete=False):
        """Enter registration into an embargo period at end of which, it will
        be made public
        :param user: User initiating the embargo
        :param end_date: Date when the registration should be made public
        :raises: NodeStateError if Node is not a registration
        :raises: PermissionsError if user is not an admin for the Node
        :raises: ValidationError if end_date is not within time constraints
        """
        if not self.has_permission(user, 'admin'):
            raise PermissionsError('Only admins may embargo a registration')
        if not self._is_embargo_date_valid(end_date):
            if (end_date - timezone.now()) >= settings.EMBARGO_END_DATE_MIN:
                raise ValidationError('Registrations can only be embargoed for up to four years.')
            raise ValidationError('Embargo end date must be at least three days in the future.')

        embargo = self._initiate_embargo(user, end_date,
                                         for_existing_registration=for_existing_registration,
                                         notify_initiator_on_complete=notify_initiator_on_complete)

        self.registered_from.add_log(
            action=NodeLog.EMBARGO_INITIATED,
            params={
                'node': self.registered_from._id,
                'registration': self._id,
                'embargo_id': embargo._id,
            },
            auth=Auth(user),
            save=True,
        )
        if self.is_public:
            self.set_privacy('private', Auth(user))
Beispiel #9
0
    def set_title(self, title, auth, save=False):
        """Set the title of this Node and log it.

        :param str title: The new title.
        :param auth: All the auth information including user, API key.
        """
        if not self.has_permission(auth.user, 'write'):
            raise PermissionsError(
                'Must have admin or write permissions to edit a preprint\'s title.'
            )

        # Called so validation does not have to wait until save.
        validate_title(title)

        original_title = self.title
        new_title = sanitize.strip_html(title)
        # Title hasn't changed after sanitzation, bail out
        if original_title == new_title:
            return False
        self.title = new_title
        self.add_log(
            action=PreprintLog.EDITED_TITLE,
            params={
                'preprint': self._id,
                'title_new': self.title,
                'title_original': original_title,
            },
            auth=auth,
            save=False,
        )
        if save:
            self.save()
        return None
Beispiel #10
0
    def _validate_accept_trigger(self, user):
        """Validate usage of the "accept" trigger on the underlying ApprovalsMachine

        "accept" has three valid usages:
        First, "accept" is called from within the "approve" trigger once all required approvals
        have been granted. This call should receive the user who issued the final "approve"
        so that the correct SchemaResponseAction can be logged.

        Second, moderators "accept" a SchemaResponse if it belongs to a moderated parent resource.
        In this case, the user must have the correct permission on the parent's provider.

        Finally, "accept" can be called without a user in order to bypass the need for approvals
        (the "internal accept shortcut") to make life easier for OSF scripts and utilities.

        "accept" can only be invoked from UNAPPROVED and PENDING_MODERATION, calling from any
        other state will result in a MachineError prior to this validation.
        """
        if self.state is ApprovalStates.UNAPPROVED:
            # user = None -> internal accept shortcut
            # not self.pending_approvers.exists() -> called from within "approve"
            if user is None or not self.pending_approvers.exists():
                return
            raise MachineError(
                f'Invalid usage of "accept" trigger from UNAPPROVED state '
                f'against SchemaResponse with id [{self._id}]'
            )

        if not user.has_perm('accept_submissions', self.parent.provider):
            raise PermissionsError(
                f'User {user} is not a modrator on {self.parent.provider} and does not '
                f'have permission to "accept" SchemaResponse with id [{self._id}]'
            )
Beispiel #11
0
    def set_published(self, published, auth, save=False):
        if not self.node.has_permission(auth.user, ADMIN):
            raise PermissionsError('Only admins can publish a preprint.')

        if self.is_published and not published:
            raise ValueError('Cannot unpublish preprint.')

        self.is_published = published

        if published:
            if not (self.node.preprint_file and self.node.preprint_file.node == self.node):
                raise ValueError('Preprint node is not a valid preprint; cannot publish.')
            if not self.provider:
                raise ValueError('Preprint provider not specified; cannot publish.')
            if not self.subjects:
                raise ValueError('Preprint must have at least one subject to be published.')
            self.date_published = datetime.datetime.utcnow()
            self.node._has_abandoned_preprint = False

            self.node.add_log(action=NodeLog.PREPRINT_INITIATED, params={}, auth=auth, save=False)

            if not self.node.is_public:
                self.node.set_privacy(
                    self.node.PUBLIC,
                    auth=None,
                    log=True
                )

        if save:
            self.node.save()
            self.save()
Beispiel #12
0
    def edit(self, content, auth, save=False):
        if not self.node.can_comment(auth) or self.user._id != auth.user._id:
            raise PermissionsError(
                '{0!r} does not have permission to edit this comment'.format(
                    auth.user))
        log_dict = {
            'project': self.node.parent_id,
            'node': self.node._id,
            'user': self.user._id,
            'comment': self._id,
        }
        log_dict.update(self.root_target.referent.get_extra_log_params(self))
        self.content = content
        self.modified = True
        self.date_modified = timezone.now()
        new_mentions = get_valid_mentioned_users_guids(self,
                                                       self.node.contributors)

        if save:
            if new_mentions:
                project_signals.mention_added.send(self,
                                                   new_mentions=new_mentions,
                                                   auth=auth)
                self.ever_mentioned.extend(new_mentions)
            self.save()
            self.node.add_log(
                NodeLog.COMMENT_UPDATED,
                log_dict,
                auth=auth,
                save=False,
            )
            self.node.save()
Beispiel #13
0
    def set_privacy(self, permissions, auth=None, log=True, save=True, check_addons=False, force=False):
        """Set the permissions for this preprint - mainly for spam purposes.

        :param permissions: A string, either 'public' or 'private'
        :param auth: All the auth information including user, API key.
        :param bool log: Whether to add a NodeLog for the privacy change.
        :param bool meeting_creation: Whether this was created due to a meetings email.
        :param bool check_addons: Check and collect messages for addons?
        """
        if auth and not self.has_permission(auth.user, WRITE):
            raise PermissionsError('Must have admin or write permissions to change privacy settings.')
        if permissions == 'public' and not self.is_public:
            if (self.is_spam or (settings.SPAM_FLAGGED_MAKE_NODE_PRIVATE and self.is_spammy)) and not force:
                raise PreprintStateError(
                    'This preprint has been marked as spam. Please contact the help desk if you think this is in error.'
                )
            self.is_public = True
        elif permissions == 'private' and self.is_public:
            self.is_public = False
        else:
            return False

        if log:
            action = PreprintLog.MADE_PUBLIC if permissions == 'public' else PreprintLog.MADE_PRIVATE
            self.add_log(
                action=action,
                params={
                    'preprint': self._id,
                },
                auth=auth,
                save=False,
            )
        if save:
            self.save()
        return True
Beispiel #14
0
    def set_primary_file(self, preprint_file, auth, save=False):
        if not self.root_folder:
            raise PreprintStateError('Preprint needs a root folder.')

        if not self.has_permission(auth.user, WRITE):
            raise PermissionsError('Must have admin or write permissions to change a preprint\'s primary file.')

        if preprint_file.target != self or preprint_file.provider != 'osfstorage':
            raise ValueError('This file is not a valid primary file for this preprint.')

        existing_file = self.primary_file
        self.primary_file = preprint_file

        self.primary_file.move_under(self.root_folder)
        self.primary_file.save()

        # only log if updating the preprint file, not adding for the first time
        if existing_file:
            self.add_log(
                action=PreprintLog.FILE_UPDATED,
                params={
                    'preprint': self._id,
                    'file': self.primary_file._id
                },
                auth=auth,
                save=False
            )

        if save:
            self.save()
        update_or_enqueue_on_preprint_updated(preprint_id=self._id, saved_fields=['primary_file'])
Beispiel #15
0
    def set_description(self, description, auth, save=False):
        """Set the description and log the event.

        :param str description: The new description
        :param auth: All the auth informtion including user, API key.
        :param bool save: Save self after updating.
        """
        if not self.has_permission(auth.user, 'write'):
            raise PermissionsError(
                'Must have admin or write permissions to edit a preprint\'s title.'
            )

        original = self.description
        new_description = sanitize.strip_html(description)
        if original == new_description:
            return False
        self.description = new_description
        self.add_log(
            action=PreprintLog.EDITED_DESCRIPTION,
            params={
                'preprint': self._id,
                'description_new': self.description,
                'description_original': original
            },
            auth=auth,
            save=False,
        )
        if save:
            self.save()
        return None
Beispiel #16
0
 def remove_email(self, email):
     """Remove a confirmed email"""
     if email == self.username:
         raise PermissionsError("Can't remove primary email")
     if email in self.emails:
         self.emails.remove(email)
         signals.user_email_removed.send(self, email=email)
Beispiel #17
0
    def set_primary_file(self, preprint_file, auth, save=False):
        if not self.node.has_permission(auth.user, ADMIN):
            raise PermissionsError(
                'Only admins can change a preprint\'s primary file.')

        if not isinstance(preprint_file, StoredFileNode):
            preprint_file = preprint_file.stored_object

        if preprint_file.node != self.node or preprint_file.provider != 'osfstorage':
            raise ValueError(
                'This file is not a valid primary file for this preprint.')

        existing_file = self.node.preprint_file
        self.node.preprint_file = preprint_file

        # only log if updating the preprint file, not adding for the first time
        if existing_file:
            self.node.add_log(action=NodeLog.PREPRINT_FILE_UPDATED,
                              params={'preprint': self._id},
                              auth=auth,
                              save=False)

        if save:
            self.save()
            self.node.save()
Beispiel #18
0
 def add_preprint_provider(self, preprint_provider, user, save=False):
     if not self.has_permission(user, ADMIN):
         raise PermissionsError('Only admins can update a preprint provider.')
     if not preprint_provider:
         raise ValueError('Must specify a provider to set as the preprint_provider')
     self.providers.add(preprint_provider)
     if save:
         self.save()
Beispiel #19
0
    def remove_unconfirmed_email(self, email):
        """Remove an unconfirmed email addresses and their tokens."""
        if email == self.username:
            raise PermissionsError("Can't remove primary email")
        for token, value in self.email_verifications.iteritems():
            if value.get('email') == email:
                del self.email_verifications[token]
                return True

        return False
Beispiel #20
0
    def set_title(self, title, auth, save=False):
        """Set the title of this Preprint and log it.

        :param str title: The new title.
        :param auth: All the auth information including user, API key.
        """
        if not self.has_permission(auth.user, WRITE):
            raise PermissionsError('Must have admin or write permissions to edit a preprint\'s title.')

        return super(Preprint, self).set_title(title, auth, save)
Beispiel #21
0
    def set_preprint_subjects(self, preprint_subjects, auth, save=False):
        if not self.has_permission(auth.user, ADMIN):
            raise PermissionsError('Only admins can change a preprint\'s subjects.')

        self.subjects.clear()
        self.subjects.add(
            *Subject.objects.filter(guid__object_id__in=preprint_subjects).values_list('pk', flat=True)
        )
        if save:
            self.save()
Beispiel #22
0
 def token_for_user(self, user, method):
     """
     :param str method: 'approval' | 'rejection'
     """
     try:
         user_state = self.approval_state[user._id]
     except KeyError:
         raise PermissionsError(self.APPROVAL_NOT_AUTHORIZED_MESSAGE.format(
             DISPLAY_NAME=self.DISPLAY_NAME))
     return user_state['{0}_token'.format(method)]
Beispiel #23
0
    def set_description(self, description, auth, save=False):
        """Set the description and log the event.

        :param str description: The new description
        :param auth: All the auth informtion including user, API key.
        :param bool save: Save self after updating.
        """
        if not self.has_permission(auth.user, WRITE):
            raise PermissionsError('Must have admin or write permissions to edit a preprint\'s title.')

        return super(Preprint, self).set_description(description, auth, save)
Beispiel #24
0
    def _validate_reject_trigger(self, user):
        """Validate usage of the "reject" trigger on the underlying ApprovalsMachine

        "reject" must be called by a pending approver or a moderator, depending on the state.

        "reject" can only be invoked from UNAPPROVED and PENDING_MODERATION, calling from any
        other state will result in a MachineError prior to this validation.
        """
        if self.state is ApprovalStates.UNAPPROVED:
            if user not in self.pending_approvers.all():
                raise PermissionsError(
                    f'User {user} is not a pending approver for SchemaResponse with id [{self._id}]'
                )
            return

        if not user.has_perm('reject_submissions', self.parent.provider):
            raise PermissionsError(
                f'User {user} is not a modrator on {self.parent.provider} and does not '
                f'have permission to "reject" SchemaResponse with id [{self._id}]'
            )
Beispiel #25
0
 def remove_preprint_provider(self, preprint_provider, user, save=False):
     if not self.has_permission(user, ADMIN):
         raise PermissionsError('Only admins can remove a preprint provider.')
     if not preprint_provider:
         raise ValueError('Must specify a provider to remove from this preprint.')
     if self.providers.filter(id=preprint_provider.id).exists():
         self.providers.remove(preprint_provider)
         if save:
             self.save()
         return True
     return False
Beispiel #26
0
    def create(cls, auth, **kwargs):
        comment = cls(**kwargs)
        if not comment.node.can_comment(auth):
            raise PermissionsError(
                '{0!r} does not have permission to comment on this node'.
                format(auth.user))
        log_dict = {
            'project': comment.node.parent_id,
            'node': comment.node._id,
            'user': comment.user._id,
            'comment': comment._id,
        }
        if isinstance(comment.target.referent, Comment):
            comment.root_target = comment.target.referent.root_target
        else:
            comment.root_target = comment.target

        page = getattr(comment.root_target.referent, 'root_target_page', None)
        if not page:
            raise ValueError('Invalid root target.')
        comment.page = page

        log_dict.update(
            comment.root_target.referent.get_extra_log_params(comment))

        new_mentions = []
        if comment.content:
            if not comment.id:
                # must have id before accessing M2M
                comment.save()
            new_mentions = get_valid_mentioned_users_guids(
                comment, comment.node.contributors_and_group_members)
            if new_mentions:
                project_signals.mention_added.send(comment,
                                                   new_mentions=new_mentions,
                                                   auth=auth)
                comment.ever_mentioned.add(*comment.node.contributors.filter(
                    guids___id__in=new_mentions))

        comment.save()

        comment.node.add_log(
            NodeLog.COMMENT_ADDED,
            log_dict,
            auth=auth,
            save=False,
        )

        comment.node.save()
        project_signals.comment_added.send(comment,
                                           auth=auth,
                                           new_mentions=new_mentions)

        return comment
Beispiel #27
0
 def post(self, request, *args, **kwargs):
     old_provider = self.get_object().provider
     if not request.user.has_perm('osf.change_preprint'):
         raise PermissionsError(
             "This user does not have permission to update this preprint's provider."
         )
     response = super(PreprintView, self).post(request, *args, **kwargs)
     new_provider = self.get_object().provider
     if new_provider and old_provider.id != new_provider.id:
         self.update_subjects_for_provider(request, old_provider,
                                           new_provider)
     return response
Beispiel #28
0
    def _validate_approve_trigger(self, user):
        """Validate usage of the "approve" trigger on the underlying ApprovalsMachine

        Only users listed in self.pending_approvers can approve.

        "approve" can only be invoked from UNAPPROVED, calling from any other state will
        result in a MachineError prior to this validation.
        """
        if user not in self.pending_approvers.all():
            raise PermissionsError(
                f'User {user} is not a pending approver for SchemaResponse with id [{self._id}]'
            )
Beispiel #29
0
 def approve(self, user, token):
     """Add user to approval list if user is admin and token verifies."""
     try:
         if self.approval_state[user._id]['approval_token'] != token:
             raise InvalidSanctionApprovalToken(
                 self.APPROVAL_INVALID_TOKEN_MESSAGE.format(
                     DISPLAY_NAME=self.DISPLAY_NAME))
     except KeyError:
         raise PermissionsError(self.APPROVAL_NOT_AUTHORIZED_MESSAGE.format(
             DISPLAY_NAME=self.DISPLAY_NAME))
     self.approval_state[user._id]['has_approved'] = True
     self._on_approve(user, token)
     self.save()
Beispiel #30
0
 def reject(self, user, token):
     """Cancels sanction if user is admin and token verifies."""
     try:
         if self.approval_state[user._id]['rejection_token'] != token:
             raise InvalidSanctionRejectionToken(
                 self.REJECTION_INVALID_TOKEN_MESSAGE.format(
                     DISPLAY_NAME=self.DISPLAY_NAME))
     except KeyError:
         raise PermissionsError(
             self.REJECTION_NOT_AUTHORIZED_MESSAEGE.format(
                 DISPLAY_NAME=self.DISPLAY_NAME))
     self.state = Sanction.REJECTED
     self._on_reject(user)