def test_provider_down(self): # Create a 500 error _prepare_mock_500_error() user = UserFactory() # Fake a request context for the callback with self.app.app.test_request_context( path="/oauth/callback/mock2/", query_string="code=mock_code&state=mock_state"): # make sure the user is logged in authenticate(user=user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state', }, } session.save() # do the key exchange with assert_raises(HTTPError) as error_raised: self.provider.auth_callback(user=user) assert_equal( error_raised.exception.code, 503, )
def test_callback(self): # Exchange temporary credentials for permanent credentials # Mock the exchange of the code for an access token _prepare_mock_oauth2_handshake_response() user = UserFactory() # Fake a request context for the callback with self.app.app.test_request_context( path="/oauth/callback/mock2/", query_string="code=mock_code&state=mock_state" ): # make sure the user is logged in authenticate(user=self.user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state', }, } session.save() # do the key exchange self.provider.auth_callback(user=user) account = ExternalAccount.objects.first() assert_equal(account.oauth_key, 'mock_access_token') assert_equal(account.provider_id, 'mock_provider_id')
def test_provider_down(self): # Create a 500 error _prepare_mock_500_error() user = UserFactory() # Fake a request context for the callback with self.app.app.test_request_context( path="/oauth/callback/mock2/", query_string="code=mock_code&state=mock_state" ): # make sure the user is logged in authenticate(user=user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state', }, } session.save() # do the key exchange with assert_raises(HTTPError) as error_raised: self.provider.auth_callback(user=user) assert_equal( error_raised.exception.code, 503, )
def restfulapi_cancel(auth, **kwargs): if u'restfulapi' not in session.data: session.data[u'restfulapi'] = {} if u'task_list' not in session.data[u'restfulapi']: session.data[u'restfulapi'][u'task_list'] = [] cancel_count = 0 for task_id in session.data[u'restfulapi'][u'task_list']: task = AsyncResult(task_id) if not task.ready(): # Raise SoftTimeLimitExceeded exception on the task task.revoke(terminate=True, signal='SIGUSR1') cancel_count += 1 session.data[u'restfulapi'][u'task_list'] = [] session.save() if cancel_count == 0: return { 'status': 'No download tasks', 'message': 'There are no active download tasks.' } # Recent activity log node = kwargs.get('node') pid = kwargs.get('pid') node.add_log(action='restfulapi_cancel', params={ 'node': pid, 'project': pid }, auth=auth) return {'status': 'OK', 'message': 'Download task has been cancelled.'}
def pop_status_messages(level=0): messages = session.data.get('status') session.status_prev = messages if 'status' in session.data: del session.data['status'] session.save() return messages
def test_callback_oauth_standard(self, mock_fetch_token, mock_oauth2session): # During token exchange, OAuth2Session is initialized w/ redirect_uri for standard addons. # Make sure that the mock oauth2 provider is a standard one. assert self.provider.short_name not in ADDONS_OAUTH_NO_REDIRECT # Mock OAuth2Session and its property `fetch_token`. mock_oauth2session.return_value = OAuth2Session( self.provider.client_id, None) mock_fetch_token.return_value = {'access_token': 'mock_access_token'} user = UserFactory() with self.app.app.test_request_context( path='/oauth/callback/mock2/', query_string='code=mock_code&state=mock_state'): authenticate(user=self.user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state' } } session.save() self.provider.auth_callback(user=user) redirect_uri = web_url_for('oauth_callback', service_name=self.provider.short_name, _absolute=True) mock_oauth2session.assert_called_with(self.provider.client_id, redirect_uri=redirect_uri)
def test_callback_oauth_no_redirect(self, mock_fetch_token, mock_oauth2session): # During token exchange, OAuth2Session is initialize w/o redirect_uri for non-standard ones. # Temporarily add the mock provider to the `ADDONS_OAUTH_NO_REDIRECT` list. ADDONS_OAUTH_NO_REDIRECT.append(self.provider.short_name) # Mock OAuth2Session and its property `fetch_token`. mock_oauth2session.return_value = OAuth2Session( self.provider.client_id, None) mock_fetch_token.return_value = {'access_token': 'mock_access_token'} user = UserFactory() with self.app.app.test_request_context( path='/oauth/callback/mock2/', query_string='code=mock_code&state=mock_state'): authenticate(user=self.user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state' } } session.save() self.provider.auth_callback(user=user) mock_oauth2session.assert_called_with(self.provider.client_id, redirect_uri=None) # Reset the `ADDONS_OAUTH_NO_REDIRECT` list. ADDONS_OAUTH_NO_REDIRECT.remove(self.provider.short_name)
def ftp_download(auth, **kwargs): node = kwargs.get('node') pid = kwargs.get('pid') uid = auth.user._id request_info = { 'node_id': node._id, 'pid': pid, 'uid': uid } data = request.get_json() validation_result = validate_data(data) if not validation_result['valid']: return { 'status': 'Failed', 'message': validation_result['message'] } if data['passMethod'] == 'plaintext': data['key'] = None args = { 'host': '', 'port': '22' if data['protocol'] == 'sftp' else '21', 'username': '', 'password': '', 'key': None, 'path': '/' } args.update(data) if u'ftp' not in session.data: session.data[u'ftp'] = {} if u'task_list' not in session.data[u'ftp']: session.data[u'ftp'][u'task_list'] = [] # Clear finished tasks for task_id in session.data[u'ftp'][u'task_list']: task = AsyncResult(task_id) if task.ready(): session.data[u'ftp'][u'task_list'].remove(task_id) task = main_task.delay(request.cookies.get('osf'), args, request_info) session.data[u'ftp'][u'task_list'].append(task.task_id) session.save() # Recent activity log node.add_log( action='ftp_download', params={ 'node': pid, 'project': pid }, auth=auth ) return { 'status': 'OK', 'message': 'Selected files are being downloaded to storage.' }
def get_repo_auth_url(self, repoid): """The URL to begin the OAuth dance. This property method has side effects - it at least adds temporary information to the session so that callbacks can be associated with the correct user. For OAuth1, it calls the provider to obtain temporary credentials to start the flow. """ # create a dict on the session object if it's not already there if session.data.get('oauth_states') is None: session.data['oauth_states'] = {} repo_settings = weko_settings.REPOSITORIES[repoid] assert self._oauth_version == OAUTH2 # build the URL oauth = OAuth2Session( repo_settings['client_id'], redirect_uri=web_url_for('weko_oauth_callback', repoid=repoid, _absolute=True), scope=self.default_scopes, ) url, state = oauth.authorization_url(repo_settings['authorize_url']) # save state token to the session for confirmation in the callback session.data['oauth_states'][self.short_name] = {'state': state} session.save() return url
def test_callback(self): # Exchange temporary credentials for permanent credentials # Mock the exchange of the code for an access token _prepare_mock_oauth2_handshake_response() user = UserFactory() # Fake a request context for the callback with self.app.app.test_request_context( path="/oauth/callback/mock2/", query_string="code=mock_code&state=mock_state"): # make sure the user is logged in authenticate(user=self.user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state', }, } session.save() # do the key exchange self.provider.auth_callback(user=user) account = ExternalAccount.find_one() assert_equal(account.oauth_key, 'mock_access_token') assert_equal(account.provider_id, 'mock_provider_id')
def auth_url(self): """The URL to begin the OAuth dance. This property method has side effects - it at least adds temporary information to the session so that callbacks can be associated with the correct user. For OAuth1, it calls the provider to obtain temporary credentials to start the flow. """ # create a dict on the session object if it's not already there if session.data.get('oauth_states') is None: session.data['oauth_states'] = {} if self._oauth_version == OAUTH2: # Quirk: Some time between 2019/05/31 and 2019/06/04, Bitbucket's OAuth2 API no longer # expects the query param `redirect_uri` in the `oauth2/authorize` endpoint. In # addition, it relies on the "Callback URL" of the "OAuth Consumer" to redirect # the auth flow after successful authorization. `ADDONS_OAUTH_NO_REDIRECT` is a # list containing addons that do not use `redirect_uri` in OAuth2 requests. 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 ) # build the URL oauth = OAuth2Session( self.client_id, redirect_uri=redirect_uri, scope=self.default_scopes, ) url, state = oauth.authorization_url(self.auth_url_base) # save state token to the session for confirmation in the callback session.data['oauth_states'][self.short_name] = {'state': state} elif self._oauth_version == OAUTH1: # get a request token oauth = OAuth1Session( client_key=self.client_id, client_secret=self.client_secret, ) # request temporary credentials from the provider response = oauth.fetch_request_token(self.request_token_url) # store them in the session for use in the callback session.data['oauth_states'][self.short_name] = { 'token': response.get('oauth_token'), 'secret': response.get('oauth_token_secret'), } url = oauth.authorization_url(self.auth_url_base) session.save() return url
def test_multiple_users_associated(self): # Create only one ExternalAccount for multiple OSF users # # For some providers (ex: GitHub), the act of completing the OAuth flow # revokes previously generated credentials. In addition, there is often no # way to know the user's id on the external service until after the flow # has completed. # # Having only one ExternalAccount instance per account on the external # service means that connecting subsequent OSF users to the same external # account will not invalidate the credentials used by the OSF for users # already associated. user_a = UserFactory() external_account = ExternalAccountFactory( provider='mock2', provider_id='mock_provider_id', provider_name='Mock Provider', ) user_a.external_accounts.add(external_account) user_a.save() user_b = UserFactory() # Mock the exchange of the code for an access token _prepare_mock_oauth2_handshake_response() # Fake a request context for the callback with self.app.app.test_request_context( path="/oauth/callback/mock2/", query_string="code=mock_code&state=mock_state" ) as ctx: # make sure the user is logged in authenticate(user=user_b, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state', }, } session.save() # do the key exchange self.provider.auth_callback(user=user_b) user_a.reload() user_b.reload() external_account.reload() assert_equal( list(user_a.external_accounts.values_list('pk', flat=True)), list(user_b.external_accounts.values_list('pk', flat=True)), ) assert_equal( ExternalAccount.find().count(), 1 )
def test_multiple_users_associated(self): # Create only one ExternalAccount for multiple OSF users # # For some providers (ex: GitHub), the act of completing the OAuth flow # revokes previously generated credentials. In addition, there is often no # way to know the user's id on the external service until after the flow # has completed. # # Having only one ExternalAccount instance per account on the external # service means that connecting subsequent OSF users to the same external # account will not invalidate the credentials used by the OSF for users # already associated. user_a = UserFactory() external_account = ExternalAccountFactory( provider='mock2', provider_id='mock_provider_id', provider_name='Mock Provider', ) user_a.external_accounts.add(external_account) user_a.save() user_b = UserFactory() # Mock the exchange of the code for an access token _prepare_mock_oauth2_handshake_response() # Fake a request context for the callback with self.app.app.test_request_context( path="/oauth/callback/mock2/", query_string="code=mock_code&state=mock_state" ) as ctx: # make sure the user is logged in authenticate(user=user_b, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state', }, } session.save() # do the key exchange self.provider.auth_callback(user=user_b) user_a.reload() user_b.reload() external_account.reload() assert_equal( list(user_a.external_accounts.values_list('pk', flat=True)), list(user_b.external_accounts.values_list('pk', flat=True)), ) assert_equal( ExternalAccount.objects.all().count(), 1 )
def pop_status_messages(level=0): messages = session.data.get('status') for message in messages or []: if len(message) == 5: message += [None, None] # Make sure all status's have enough arguments session.status_prev = messages if 'status' in session.data: del session.data['status'] session.save() return messages
def push_status_message(message, kind='warning', dismissible=True, trust=True, jumbotron=False, id=None, extra=None): """ Push a status message that will be displayed as a banner on the next page loaded by the user. :param message: Text of the message to display :param kind: The type of alert message to use; translates into a bootstrap CSS class of `alert-<kind>` :param dismissible: Whether the status message can be dismissed by the user :param trust: Whether the text is safe to insert directly into HTML as given. (useful if the message includes custom code, eg links) If false, the message will be automatically escaped as an HTML-safe string. :param jumbotron: Should this be in a jumbotron element rather than an alert """ # TODO: Change the default to trust=False once conversion to markupsafe rendering is complete try: statuses = session.data.get('status') except RuntimeError as e: exception_message = getattr(e, 'message', None) if exception_message == 'working outside of request context': # Working outside of request context, so should be a DRF issue. Status messages are not appropriate there. # If it's any kind of notification, then it doesn't make sense to send back to the API routes. if kind == 'error': # If it's an error, then the call should fail with the error message. I do not know of any cases where # this branch will be hit, but I'd like to avoid a silent failure. from rest_framework.exceptions import ValidationError raise ValidationError(message) return else: raise if not statuses: statuses = [] if not extra: extra = {} css_class = TYPE_MAP.get(kind, 'warning') statuses.append( Status(message=message, jumbotron=jumbotron, css_class=css_class, dismissible=dismissible, id=id, extra=extra, trust=trust)) session.data['status'] = statuses session.save()
def auth_url(self): """The URL to begin the OAuth dance. This property method has side effects - it at least adds temporary information to the session so that callbacks can be associated with the correct user. For OAuth1, it calls the provider to obtain temporary credentials to start the flow. """ # create a dict on the session object if it's not already there if session.data.get('oauth_states') is None: session.data['oauth_states'] = {} if self._oauth_version == OAUTH2: # build the URL oauth = OAuth2Session( self.client_id, redirect_uri=web_url_for('oauth_callback', service_name=self.short_name, _absolute=True), scope=self.default_scopes, ) url, state = oauth.authorization_url(self.auth_url_base) # save state token to the session for confirmation in the callback session.data['oauth_states'][self.short_name] = {'state': state} elif self._oauth_version == OAUTH1: # get a request token oauth = OAuth1Session( client_key=self.client_id, client_secret=self.client_secret, ) # request temporary credentials from the provider response = oauth.fetch_request_token(self.request_token_url) # store them in the session for use in the callback session.data['oauth_states'][self.short_name] = { 'token': response.get('oauth_token'), 'secret': response.get('oauth_token_secret'), } url = oauth.authorization_url(self.auth_url_base) session.save() return url
def restfulapi_download(auth, **kwargs): node = kwargs.get('node') pid = kwargs.get('pid') uid = auth.user._id request_info = {'node_id': node._id, 'pid': pid, 'uid': uid} data = request.get_json() prevalidation_result = prevalidate_data(data) if not prevalidation_result['valid']: return {'status': 'Failed', 'message': prevalidation_result['message']} # Clean up input data['url'] = data['url'].strip().split(' ')[0] postvalidation_result = postvalidate_data(data) if not postvalidation_result['valid']: return { 'status': 'Failed', 'message': postvalidation_result['message'] } if u'restfulapi' not in session.data: session.data[u'restfulapi'] = {} if u'task_list' not in session.data[u'restfulapi']: session.data[u'restfulapi'][u'task_list'] = [] # Clear finished tasks for task_id in session.data[u'restfulapi'][u'task_list']: task = AsyncResult(task_id) if task.ready(): session.data[u'restfulapi'][u'task_list'].remove(task_id) task = main_task.delay(request.cookies.get('osf'), data, request_info) session.data[u'restfulapi'][u'task_list'].append(task.task_id) session.save() # Recent activity log node.add_log(action='restfulapi_submit', params={ 'node': pid, 'project': pid }, auth=auth) return { 'status': 'OK', }
def test_callback(self): # Exchange temporary credentials for permanent credentials # mock a successful call to the provider to exchange temp keys for # permanent keys responses.add( responses.Response( responses.POST, 'http://mock1a.com/callback', body=( 'oauth_token=perm_token' '&oauth_token_secret=perm_secret' '&oauth_callback_confirmed=true' ) ) ) user = UserFactory() # Fake a request context for the callback ctx = self.app.app.test_request_context( path='/oauth/callback/mock1a/', query_string='oauth_token=temp_key&oauth_verifier=mock_verifier', ) with ctx: # make sure the user is logged in authenticate(user=user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'token': 'temp_key', 'secret': 'temp_secret', }, } session.save() # do the key exchange self.provider.auth_callback(user=user) account = ExternalAccount.objects.first() assert_equal(account.oauth_key, 'perm_token') assert_equal(account.oauth_secret, 'perm_secret') assert_equal(account.provider_id, 'mock_provider_id') assert_equal(account.provider_name, 'Mock OAuth 1.0a Provider')
def push_status_message(message, kind='warning', dismissible=True, trust=True, jumbotron=False, id=None, extra=None): """ Push a status message that will be displayed as a banner on the next page loaded by the user. :param message: Text of the message to display :param kind: The type of alert message to use; translates into a bootstrap CSS class of `alert-<kind>` :param dismissible: Whether the status message can be dismissed by the user :param trust: Whether the text is safe to insert directly into HTML as given. (useful if the message includes custom code, eg links) If false, the message will be automatically escaped as an HTML-safe string. :param jumbotron: Should this be in a jumbotron element rather than an alert """ # TODO: Change the default to trust=False once conversion to markupsafe rendering is complete try: statuses = session.data.get('status') except RuntimeError as e: exception_message = getattr(e, 'message', None) if exception_message == 'working outside of request context': # Working outside of request context, so should be a DRF issue. Status messages are not appropriate there. # If it's any kind of notification, then it doesn't make sense to send back to the API routes. if kind == 'error': # If it's an error, then the call should fail with the error message. I do not know of any cases where # this branch will be hit, but I'd like to avoid a silent failure. from rest_framework.exceptions import ValidationError raise ValidationError(message) return else: raise if not statuses: statuses = [] if not extra: extra = {} css_class = TYPE_MAP.get(kind, 'warning') statuses.append(Status(message=message, jumbotron=jumbotron, css_class=css_class, dismissible=dismissible, id=id, extra=extra, trust=trust)) session.data['status'] = statuses session.save()
def test_user_denies_access(self): # Create a 401 error _prepare_mock_401_error() user = UserFactory() # Fake a request context for the callback with self.app.app.test_request_context( path="/oauth/callback/mock2/", query_string="error=mock_error&code=mock_code&state=mock_state" ): # make sure the user is logged in authenticate(user=user, access_token=None, response=None) session.data['oauth_states'] = { self.provider.short_name: { 'state': 'mock_state', }, } session.save() assert_false(self.provider.auth_callback(user=user))
def _set_external_account(self, user, info): self.account, created = ExternalAccount.objects.get_or_create( provider=self.short_name, provider_id=info['provider_id'], ) # 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') self.account.date_last_refreshed = timezone.now() # 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 not user.external_accounts.filter(id=self.account.id).exists(): user.external_accounts.add(self.account) user.save() if self.short_name in session.data.get('oauth_states', {}): del session.data['oauth_states'][self.short_name] session.save() return True
def pop_previous_status_messages(level=0): messages = session.data.get('status_prev') if 'status_prev' in session.data: del session.data['status_prev'] session.save() return messages
def update_counter(cls, page, node_info): cleaned_page = cls.clean_page(page) date = timezone.now() date_string = date.strftime('%Y/%m/%d') visited_by_date = session.data.get('visited_by_date', {'date': date_string, 'pages': []}) with transaction.atomic(): model_instance, created = cls.objects.select_for_update().get_or_create(_id=cleaned_page) # if they visited something today if date_string == visited_by_date['date']: # if they haven't visited this page today if cleaned_page not in visited_by_date['pages']: # if the model_instance has today in it if date_string in model_instance.date.keys(): # increment the number of unique visitors for today model_instance.date[date_string]['unique'] += 1 else: # set the number of unique visitors for today to 1 model_instance.date[date_string] = dict(unique=1) # if they haven't visited something today else: # set their visited by date to blank visited_by_date['date'] = date_string visited_by_date['pages'] = [] # if the model_instance has today in it if date_string in model_instance.date.keys(): # increment the number of unique visitors for today model_instance.date[date_string]['unique'] += 1 else: # set the number of unique visitors to 1 model_instance.date[date_string] = dict(unique=1) # update their sessions visited_by_date['pages'].append(cleaned_page) session.data['visited_by_date'] = visited_by_date if date_string in model_instance.date.keys(): if 'total' not in model_instance.date[date_string].keys(): model_instance.date[date_string].update(total=0) model_instance.date[date_string]['total'] += 1 else: model_instance.date[date_string] = dict(total=1) # if a download counter is being updated, only perform the update # if the user who is downloading isn't a contributor to the project page_type = cleaned_page.split(':')[0] if page_type == 'download' and node_info: if node_info['contributors'].filter(guids___id__isnull=False, guids___id=session.data.get('auth_user_id')).exists(): model_instance.save() return visited = session.data.get('visited', []) if page not in visited: model_instance.unique += 1 visited.append(page) session.data['visited'] = visited session.save() model_instance.total += 1 model_instance.save()
def claim_user_registered(auth, node, **kwargs): """ View that prompts user to enter their password in order to claim being a contributor on a project. A user must be logged in. """ current_user = auth.user sign_out_url = cas.get_logout_url(service_url=cas.get_login_url(service_url=request.url)) if not current_user: return redirect(sign_out_url) # Logged in user should not be a contributor the project if node.is_contributor(current_user): data = { 'message_short': 'Already a contributor', 'message_long': ('The logged-in user is already a contributor to this ' 'project. Would you like to <a href="{}">log out</a>?').format(sign_out_url) } raise HTTPError(http.BAD_REQUEST, data=data) uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token'] unreg_user = OSFUser.load(uid) if not verify_claim_token(unreg_user, token, pid=node._primary_key): error_data = { 'message_short': 'Invalid url.', 'message_long': 'The token in the URL is invalid or has expired.' } raise HTTPError(http.BAD_REQUEST, data=error_data) # Store the unreg_user data on the session in case the user registers # a new account session.data['unreg_user'] = { 'uid': uid, 'pid': pid, 'token': token } session.save() # If a user is already validated though external auth, it is OK to claim should_claim = check_external_auth(auth.user) form = PasswordForm(request.form) if request.method == 'POST': if form.validate(): if current_user.check_password(form.password.data): should_claim = True else: status.push_status_message(language.LOGIN_FAILED, kind='warning', trust=False) else: forms.push_errors_to_status(form.errors) if should_claim: node.replace_contributor(old=unreg_user, new=current_user) node.save() status.push_status_message( 'You are now a contributor to this project.', kind='success', trust=False ) return redirect(node.url) if is_json_request(): form_ret = forms.utils.jsonify(form) user_ret = profile_utils.serialize_user(current_user, full=False) else: form_ret = form user_ret = current_user return { 'form': form_ret, 'user': user_ret, 'signOutUrl': sign_out_url }
def claim_user_registered(auth, node, **kwargs): """ View that prompts user to enter their password in order to claim being a contributor on a project. A user must be logged in. """ current_user = auth.user sign_out_url = cas.get_logout_url(service_url=cas.get_login_url( service_url=request.url)) if not current_user: return redirect(sign_out_url) # Logged in user should not be a contributor the project if hasattr(node, 'is_contributor') and node.is_contributor(current_user): data = { 'message_short': 'Already a contributor', 'message_long': ('The logged-in user is already a contributor to this ' 'project. Would you like to <a href="{}">log out</a>?' ).format(sign_out_url) } raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data) # Logged in user is already a member of the OSF Group if hasattr(node, 'is_member') and node.is_member(current_user): data = { 'message_short': 'Already a member', 'message_long': ('The logged-in user is already a member of this OSF Group. ' 'Would you like to <a href="{}">log out</a>?' ).format(sign_out_url) } raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data) uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token'] unreg_user = OSFUser.load(uid) if not verify_claim_token(unreg_user, token, pid=node._primary_key): error_data = { 'message_short': 'Invalid url.', 'message_long': 'The token in the URL is invalid or has expired.' } raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data) # Store the unreg_user data on the session in case the user registers # a new account session.data['unreg_user'] = {'uid': uid, 'pid': pid, 'token': token} session.save() # If a user is already validated though external auth, it is OK to claim should_claim = check_external_auth(auth.user) form = PasswordForm(request.form) if request.method == 'POST': if form.validate(): if current_user.check_password(form.password.data): should_claim = True else: status.push_status_message(language.LOGIN_FAILED, kind='warning', trust=False) else: forms.push_errors_to_status(form.errors) if should_claim: node.replace_contributor(old=unreg_user, new=current_user) node.save() if isinstance(node, OSFGroup): status.push_status_message( 'You are now a member of this OSFGroup.', kind='success', trust=False) else: status.push_status_message( 'You are now a contributor to this project.', kind='success', trust=False) return redirect(node.url) if is_json_request(): form_ret = forms.utils.jsonify(form) user_ret = profile_utils.serialize_user(current_user, full=False) else: form_ret = form user_ret = current_user return {'form': form_ret, 'user': user_ret, 'signOutUrl': sign_out_url}
def auth_url(self): ret = self.oauth_flow.start('force_reapprove=true') session.save() return ret
def update_counter(cls, page, node_info): cleaned_page = cls.clean_page(page) date = timezone.now() date_string = date.strftime('%Y/%m/%d') visited_by_date = session.data.get('visited_by_date', { 'date': date_string, 'pages': [] }) with transaction.atomic(): model_instance, created = cls.objects.select_for_update( ).get_or_create(_id=cleaned_page) # if they visited something today if date_string == visited_by_date['date']: # if they haven't visited this page today if cleaned_page not in visited_by_date['pages']: # if the model_instance has today in it if date_string in model_instance.date.keys(): # increment the number of unique visitors for today model_instance.date[date_string]['unique'] += 1 else: # set the number of unique visitors for today to 1 model_instance.date[date_string] = dict(unique=1) # if they haven't visited something today else: # set their visited by date to blank visited_by_date['date'] = date_string visited_by_date['pages'] = [] # if the model_instance has today in it if date_string in model_instance.date.keys(): # increment the number of unique visitors for today model_instance.date[date_string]['unique'] += 1 else: # set the number of unique visitors to 1 model_instance.date[date_string] = dict(unique=1) # update their sessions visited_by_date['pages'].append(cleaned_page) session.data['visited_by_date'] = visited_by_date if date_string in model_instance.date.keys(): if 'total' not in model_instance.date[date_string].keys(): model_instance.date[date_string].update(total=0) model_instance.date[date_string]['total'] += 1 else: model_instance.date[date_string] = dict(total=1) # if a download counter is being updated, only perform the update # if the user who is downloading isn't a contributor to the project page_type = cleaned_page.split(':')[0] if page_type in ('download', 'view') and node_info: if node_info['contributors'].filter( guids___id__isnull=False, guids___id=session.data.get('auth_user_id')).exists(): model_instance.save() return visited = session.data.get('visited', []) if page not in visited: model_instance.unique += 1 visited.append(page) session.data['visited'] = visited session.save() model_instance.total += 1 model_instance.save()
def update_counter(cls, resource, file, version, action, node_info): if version is not None: page = '{0}:{1}:{2}:{3}'.format(action, resource._id, file._id, version) else: page = '{0}:{1}:{2}'.format(action, resource._id, file._id) cleaned_page = cls.clean_page(page) date = timezone.now() date_string = date.strftime('%Y/%m/%d') visited_by_date = session.data.get('visited_by_date', { 'date': date_string, 'pages': [] }) with transaction.atomic(): # Temporary backwards compat - when creating new PageCounters, temporarily keep writing to _id field. # After we're sure this is stable, we can stop writing to the _id field, and query on # resource/file/action/version model_instance, created = cls.objects.select_for_update( ).get_or_create(_id=cleaned_page, resource=resource, file=file, action=action, version=version) # if they visited something today if date_string == visited_by_date['date']: # if they haven't visited this page today if cleaned_page not in visited_by_date['pages']: # if the model_instance has today in it if date_string in model_instance.date.keys(): # increment the number of unique visitors for today model_instance.date[date_string]['unique'] += 1 else: # set the number of unique visitors for today to 1 model_instance.date[date_string] = dict(unique=1) # if they haven't visited something today else: # set their visited by date to blank visited_by_date['date'] = date_string visited_by_date['pages'] = [] # if the model_instance has today in it if date_string in model_instance.date.keys(): # increment the number of unique visitors for today model_instance.date[date_string]['unique'] += 1 else: # set the number of unique visitors to 1 model_instance.date[date_string] = dict(unique=1) # update their sessions visited_by_date['pages'].append(cleaned_page) session.data['visited_by_date'] = visited_by_date if date_string in model_instance.date.keys(): if 'total' not in model_instance.date[date_string].keys(): model_instance.date[date_string].update(total=0) model_instance.date[date_string]['total'] += 1 else: model_instance.date[date_string] = dict(total=1) # if a download counter is being updated, only perform the update # if the user who is downloading isn't a contributor to the project page_type = cleaned_page.split(':')[0] if page_type in ('download', 'view') and node_info: if node_info['contributors'].filter( guids___id__isnull=False, guids___id=session.data.get('auth_user_id')).exists(): model_instance.save() return visited = session.data.get('visited', []) if page not in visited: model_instance.unique += 1 visited.append(page) session.data['visited'] = visited session.save() model_instance.total += 1 model_instance.save()