Beispiel #1
0
    def grant_oauth_access(self, node, external_account, metadata=None):
        """Give a node permission to use an ``ExternalAccount`` instance."""
        # ensure the user owns the external_account
        if not self.owner.external_accounts.filter(
                id=external_account.id).exists():
            raise PermissionsError()

        metadata = metadata or {}

        # create an entry for the node, if necessary
        if node._id not in self.oauth_grants:
            self.oauth_grants[node._id] = {}

        # create an entry for the external account on the node, if necessary
        if external_account._id not in self.oauth_grants[node._id]:
            self.oauth_grants[node._id][external_account._id] = {}

        # update the metadata with the supplied values
        for key, value in metadata.iteritems():
            self.oauth_grants[node._id][external_account._id][key] = value

        self.save()
Beispiel #2
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 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 #3
0
    def _validate_request(self, event_data):
        '''Verify that an approve/accept/reject call meets all preconditions.'''
        action = event_data.event.name
        user = event_data.kwargs.get('user')
        if user is None and event_data.args:
            user = event_data.args[0]
        # Allow certain 'accept' calls with no user for OSF admin use
        if not user and action != 'accept':
            raise ValueError('All state trigger functions must specify a user')

        if not self._verify_user_role(user, action):
            raise PermissionsError(
                self.ACTION_NOT_AUTHORIZED_MESSAGE.format(
                    ACTION=action, DISPLAY_NAME=self.DISPLAY_NAME))

        # Moderator auth is validated by API, no token to check
        # user is None and no prior exception -> OSF-internal accept call
        if self.approval_stage is SanctionStates.PENDING_MODERATION or user is None:
            return True

        token = event_data.kwargs.get('token')
        if token is None:
            try:
                token = event_data.args[1]
            except IndexError:
                raise ValueError('Admin actions require a token')

        if action == 'approve' and self.approval_state[
                user._id]['approval_token'] != token:
            raise InvalidSanctionApprovalToken(
                self.APPROVAL_INVALID_TOKEN_MESSAGE.format(
                    DISPLAY_NAME=self.DISPLAY_NAME))
        elif action == 'reject' and self.approval_state[
                user._id]['rejection_token'] != token:
            raise InvalidSanctionRejectionToken(
                self.REJECTION_INVALID_TOKEN_MESSAGE.format(
                    DISPLAY_NAME=self.DISPLAY_NAME))

        return True
Beispiel #4
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.exists():
                raise ValueError('Preprint must have at least one subject to be published.')
            self.date_published = timezone.now()
            self.node._has_abandoned_preprint = False

            self.node.add_log(
                action=NodeLog.PREPRINT_INITIATED,
                params={
                    'preprint': self._id
                },
                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 #5
0
 def undelete(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))
     self.is_deleted = False
     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.date_modified = timezone.now()
     if save:
         self.save()
         self.node.add_log(
             NodeLog.COMMENT_RESTORED,
             log_dict,
             auth=auth,
             save=False,
         )
         self.node.save()
Beispiel #6
0
    def _validate_submit_trigger(self, user):
        """Validate usage of the "submit" trigger on the underlying ApprovalsMachine.

        Only admins on the parent resource can submit the SchemaResponse for review.

        submit can only be called from IN_PROGRESS, calling from any other state will
        result in a MachineError prior to this validation.
        """
        if not self.parent.is_admin_contributor(user):
            raise PermissionsError(
                f'User {user} is not an admin contributor on parent resource {self.parent} '
                f'and does not have permission to "submit" SchemaResponse with id [{self._id}]'
            )

        # Only check newly udpated keys, as old keys have previously passed validation
        invalid_response_keys = [
            block.schema_key for block in self.updated_response_blocks.all() if not block.is_valid()
        ]
        if invalid_response_keys:
            raise SchemaResponseStateError(
                f'SchemaResponse with id [{self._id}] has invalid responses for the following keys '
                f'and cannot be submitted: {invalid_response_keys}'
            )
Beispiel #7
0
 def reject(self, user):
     if not user.has_perm('osf.administer_prereg'):
         raise PermissionsError('This user does not have permission to approve this draft.')
     self.state = Sanction.REJECTED
     self._on_reject(user)
Beispiel #8
0
 def approve(self, user):
     if not user.has_perm('osf.administer_prereg'):
         raise PermissionsError('This user does not have permission to approve this draft.')
     self.state = Sanction.APPROVED
     self._on_complete(user)
Beispiel #9
0
    def auth_callback(self, user):
        """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.
        """
        session = get_session()

        # 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))

        try:
            # create a new ``ExternalAccount`` ...
            self.account = ExternalAccount(
                provider=self.short_name,
                provider_id=info['provider_id'],
                provider_name=self.name,
            )
            self.account.save()
        except KeyExistsException:
            # ... or get the old one
            self.account = ExternalAccount.find_one(
                Q('provider', 'eq', self.short_name) &
                Q('provider_id', 'eq', info['provider_id'])
            )
            assert self.account is not None

        # ensure that provider_name is correct
        self.account.provider_name = self.name
        # required
        self.account.oauth_key = info['key']

        # only for OAuth1
        self.account.oauth_secret = info.get('secret')

        # only for OAuth2
        self.account.expires_at = info.get('expires_at')
        self.account.refresh_token = info.get('refresh_token')

        # additional information
        self.account.display_name = info.get('display_name')
        self.account.profile_url = info.get('profile_url')

        self.account.save()

        # add it to the user's list of ``ExternalAccounts``
        if self.account not in user.external_accounts:
            user.external_accounts.append(self.account)
            user.save()
Beispiel #10
0
 def reject(self, user):
     if settings.PREREG_ADMIN_TAG not in user.system_tags:
         raise PermissionsError(
             "This user does not have permission to approve this draft.")
     self.state = Sanction.REJECTED
     self._on_reject(user)
Beispiel #11
0
 def approve(self, user):
     if settings.PREREG_ADMIN_TAG not in user.system_tags:
         raise PermissionsError(
             "This user does not have permission to approve this draft.")
     self.state = Sanction.APPROVED
     self._on_complete(user)
Beispiel #12
0
 def post(self, request, *args, **kwargs):
     if not request.user.has_perm('osf.change_preprint'):
         raise PermissionsError(
             "This user does not have permission to update this preprint's provider."
         )
     return super(PreprintView, self).post(request, *args, **kwargs)
 def _require_manager_permission(self, auth=None):
     if auth and not self.has_permission(auth.user, MANAGE):
         raise PermissionsError(
             'Must be a group manager to modify group membership.')
    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:
                # Quirk: Similarly to the `oauth2/authorize` endpoint, the `oauth2/access_token`
                #        endpoint of Bitbucket would fail if a not-none or non-empty `redirect_uri`
                #        were provided in the body of the POST request.
                if self.short_name in ADDONS_OAUTH_NO_REDIRECT:
                    redirect_uri = None
                else:
                    redirect_uri = web_url_for('oauth_callback',
                                               service_name=self.short_name,
                                               _absolute=True)
                response = OAuth2Session(
                    self.client_id,
                    redirect_uri=redirect_uri,
                ).fetch_token(
                    self.callback_url,
                    client_secret=self.client_secret,
                    code=request.args.get('code'),
                )
            except (MissingTokenError, RequestsHTTPError):
                raise HTTPError(http_status.HTTP_503_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)
Beispiel #15
0
 def set_preprint_file(self, preprint_file, auth, save=False):
     if not self.has_permission(auth.user, ADMIN):
         raise PermissionsError('Only admins can change a preprint\'s primary file.')