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()
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()
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
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()
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()
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}' )
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)
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)
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()
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)
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)
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)
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.')