Example #1
0
    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,
            )
Example #2
0
    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')
Example #3
0
    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,
            )
Example #4
0
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.'}
Example #5
0
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
Example #6
0
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
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
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.'
    }
Example #10
0
    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
Example #11
0
    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')
Example #12
0
    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
Example #13
0
    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
        )
Example #14
0
    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
        )
Example #15
0
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
Example #16
0
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
Example #17
0
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()
Example #18
0
    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
Example #19
0
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',
    }
Example #20
0
    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')
Example #21
0
    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')
Example #22
0
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()
Example #23
0
    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))
Example #24
0
    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))
Example #25
0
    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
Example #26
0
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
Example #27
0
    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()
Example #28
0
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
    }
Example #29
0
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}
Example #30
0
 def auth_url(self):
     ret = self.oauth_flow.start('force_reapprove=true')
     session.save()
     return ret
Example #31
0
    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()
Example #32
0
    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()
Example #33
0
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
Example #34
0
 def auth_url(self):
     ret = self.oauth_flow.start('force_reapprove=true')
     session.save()
     return ret