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, response=None)

            session = get_session()
            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 osfstorage_download(file_node, payload, **kwargs):
    # Set user ID in session data for checking if user is contributor
    # to project.
    user_id = payload.get('user')
    if user_id:
        current_session = get_session()
        current_session.data['auth_user_id'] = user_id
        current_session.save()

    if not request.args.get('version'):
        version_id = None
    else:
        try:
            version_id = int(request.args['version'])
        except ValueError:
            raise make_error(
                http_status.HTTP_400_BAD_REQUEST,
                message_short='Version must be an integer if not specified')

    version = file_node.get_version(version_id, required=True)
    file_version_thru = version.get_basefilenode_version(file_node)
    name = file_version_thru.version_name if file_version_thru else file_node.name

    return {
        'data': {
            'name': name,
            'path': version.location_hash,
        },
        'settings': {
            osf_storage_settings.WATERBUTLER_RESOURCE:
            version.location[osf_storage_settings.WATERBUTLER_RESOURCE],
        },
    }
Example #3
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") as ctx:

            # make sure the user is logged in
            authenticate(user=user, response=None)

            session = get_session()
            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 #4
0
    def test_start_flow(self):
        # Request temporary credentials from provider, provide auth redirect
        responses.add(responses.POST,
                      'http://mock1a.com/request',
                      body='{"oauth_token_secret": "temp_secret", '
                      '"oauth_token": "temp_token", '
                      '"oauth_callback_confirmed": "true"}',
                      status=200,
                      content_type='application/json')

        with self.app.app.test_request_context('/oauth/connect/mock1a/'):

            # make sure the user is logged in
            authenticate(user=self.user, response=None)

            # auth_url is a property method - it calls out to the external
            #   service to get a temporary key and secret before returning the
            #   auth url
            url = self.provider.auth_url

            # The URL to which the user would be redirected
            assert_equal(url, "http://mock1a.com/auth?oauth_token=temp_token")

            session = get_session()

            # Temporary credentials are added to the session
            creds = session.data['oauth_states'][self.provider.short_name]
            assert_equal(creds['token'], 'temp_token')
            assert_equal(creds['secret'], 'temp_secret')
Example #5
0
    def test_start_flow(self):
        # Request temporary credentials from provider, provide auth redirect
        httpretty.register_uri(httpretty.POST, 'http://mock1a.com/request',
                  body='{"oauth_token_secret": "temp_secret", '
                       '"oauth_token": "temp_token", '
                       '"oauth_callback_confirmed": "true"}',
                  status=200,
                  content_type='application/json')

        with self.app.app.test_request_context('/oauth/connect/mock1a/'):

            # make sure the user is logged in
            authenticate(user=self.user, access_token=None, response=None)

            # auth_url is a property method - it calls out to the external
            #   service to get a temporary key and secret before returning the
            #   auth url
            url = self.provider.auth_url

            # The URL to which the user would be redirected
            assert_equal(url, "http://mock1a.com/auth?oauth_token=temp_token")

            session = get_session()

            # Temporary credentials are added to the session
            creds = session.data['oauth_states'][self.provider.short_name]
            assert_equal(creds['token'], 'temp_token')
            assert_equal(creds['secret'], 'temp_secret')
Example #6
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 = get_session()
            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 #7
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 = get_session()
            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 #8
0
def osfstorage_download(file_node, payload, node_addon, **kwargs):
    # Set user ID in session data for checking if user is contributor
    # to project.
    user_id = payload.get("user")
    if user_id:
        current_session = get_session()
        current_session.data["auth_user_id"] = user_id

    if not request.args.get("version"):
        version_id = None
    else:
        try:
            version_id = int(request.args["version"])
        except ValueError:
            raise make_error(httplib.BAD_REQUEST, message_short="Version must be an integer if not specified")

    version = file_node.get_version(version_id, required=True)

    if request.args.get("mode") not in ("render",):
        utils.update_analytics(node_addon.owner, file_node._id, int(version.identifier) - 1)

    return {
        "data": {"name": file_node.name, "path": version.location_hash},
        "settings": {
            osf_storage_settings.WATERBUTLER_RESOURCE: version.location[osf_storage_settings.WATERBUTLER_RESOURCE]
        },
    }
Example #9
0
def osfstorage_download(file_node, payload, node_addon, **kwargs):
    # Set user ID in session data for checking if user is contributor
    # to project.
    user_id = payload.get('user')
    if user_id:
        current_session = get_session()
        current_session.data['auth_user_id'] = user_id
        current_session.save()

    if not request.args.get('version'):
        version_id = None
    else:
        try:
            version_id = int(request.args['version'])
        except ValueError:
            raise make_error(httplib.BAD_REQUEST, message_short='Version must be an integer if not specified')

    version = file_node.get_version(version_id, required=True)

    if request.args.get('mode') not in ('render', ):
        utils.update_analytics(node_addon.owner, file_node._id, int(version.identifier) - 1)

    return {
        'data': {
            'name': file_node.name,
            'path': version.location_hash,
        },
        'settings': {
            osf_storage_settings.WATERBUTLER_RESOURCE: version.location[osf_storage_settings.WATERBUTLER_RESOURCE],
        },
    }
Example #10
0
def osfstorage_download(file_node, payload, node_addon, **kwargs):
    # Set user ID in session data for checking if user is contributor
    # to project.
    user_id = payload.get('user')
    if user_id:
        current_session = get_session()
        current_session.data['auth_user_id'] = user_id

    if not request.args.get('version'):
        version_id = None
    else:
        try:
            version_id = int(request.args['version'])
        except ValueError:
            raise make_error(
                httplib.BAD_REQUEST,
                message_short='Version must be an integer if not specified')

    version = file_node.get_version(version_id, required=True)

    if request.args.get('mode') not in ('render', ):
        utils.update_analytics(node_addon.owner, file_node._id,
                               int(version.identifier) - 1)

    return {
        'data': {
            'name': file_node.name,
            'path': version.location_hash,
        },
        'settings': {
            osf_storage_settings.WATERBUTLER_RESOURCE:
            version.location[osf_storage_settings.WATERBUTLER_RESOURCE],
        },
    }
Example #11
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.append(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 = get_session()
            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(
            user_a.external_accounts,
            user_b.external_accounts,
        )

        assert_equal(
            ExternalAccount.find().count(),
            1
        )
Example #12
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.append(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, response=None)

            session = get_session()
            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(
            user_a.external_accounts,
            user_b.external_accounts,
        )

        assert_equal(ExternalAccount.find().count(), 1)
Example #13
0
def external_login_email_get():
    """
    Landing view for first-time oauth-login user to enter their email address.
    HTTP Method: GET
    """

    form = ResendConfirmationForm(request.form)
    session = get_session()
    if not session.is_external_first_login:
        raise HTTPError(http.UNAUTHORIZED)

    external_id_provider = session.data['auth_user_external_id_provider']

    return {'form': form, 'external_id_provider': external_id_provider}
Example #14
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.
        """

        session = get_session()

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

        return url
Example #15
0
def get_request_and_user_id():
    """
    Fetch a request and user id from either a Django or Flask request.
    """
    # TODO: This should be consolidated into framework
    from framework.sessions import get_session

    req = get_current_request()
    user_id = None
    if isinstance(req, FlaskRequest):
        session = get_session()
        user_id = session.data.get('auth_user_id')
    elif hasattr(req, 'user'):
        # admin module can return a user w/o an id
        user_id = getattr(req.user, '_id', None)
    return req, user_id
Example #16
0
def get_request_and_user_id():
    """
    Fetch a request and user id from either a Django or Flask request.
    """
    # TODO: This should be consolidated into framework
    from framework.sessions import get_session

    req = get_cache_key()
    user_id = None
    if isinstance(req, FlaskRequest):
        session = get_session()
        user_id = session.data.get('auth_user_id')
    elif hasattr(req, 'user'):
        # admin module can return a user w/o an id
        user_id = getattr(req.user, '_id', None)
    return req, user_id
Example #17
0
    def test_func(self):
        """check user permissions"""
        institution_id = None
        addon_name = self.kwargs.get('addon_name')
        session_data = {}
        try:
            session = get_session()
            session_data = session.data
        except RuntimeError:
            print('Unable to access session data')

        if 'oauth_states' in session_data:
            institution_id = int(session_data['oauth_states'][addon_name]['institution_id'])
        elif 'institution_id' in self.kwargs:
            institution_id = int(self.kwargs.get('institution_id'))
        return self.has_auth(institution_id)
Example #18
0
def external_login_email_get():
    """
    Landing view for first-time oauth-login user to enter their email address.
    HTTP Method: GET
    """

    form = ResendConfirmationForm(request.form)
    session = get_session()
    if not session.is_external_first_login:
        raise HTTPError(http.UNAUTHORIZED)

    external_id_provider = session.data['auth_user_external_id_provider']

    return {
        'form': form,
        'external_id_provider': external_id_provider
    }
Example #19
0
    def auth_url(self):
        """The URL to begin the OAuth dance.

        Accessing this property will create an ``ExternalAccount`` if one is not
        already attached to the instance, in order to store the state token and
        scope."""

        session = get_session()

        # 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 account instance to be available in 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,
            )

            response = oauth.fetch_request_token(self.request_token_url)

            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)

        return url
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
        httpretty.register_uri(
            httpretty.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 = get_session()
            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.find_one()
        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_start_flow(self):
        # Generate the appropriate URL and state token

        with self.app.app.test_request_context("/oauth/connect/mock2/"):

            # make sure the user is logged in
            authenticate(user=self.user, access_token=None, response=None)

            # auth_url is a property method - it calls out to the external
            #   service to get a temporary key and secret before returning the
            #   auth url
            url = self.provider.auth_url

            session = get_session()

            # Temporary credentials are added to the session
            creds = session.data['oauth_states'][self.provider.short_name]
            assert_in('state', creds)

            # The URL to which the user would be redirected
            parsed = urlparse.urlparse(url)
            params = urlparse.parse_qs(parsed.query)

            # check parameters
            assert_equal(
                params,
                {
                    'state': [creds['state']],
                    'response_type': ['code'],
                    'client_id': [self.provider.client_id],
                    'redirect_uri': [
                        web_url_for('oauth_callback',
                                    service_name=self.provider.short_name,
                                    _absolute=True)
                    ]
                }
            )

            # check base URL
            assert_equal(
                url.split("?")[0],
                "https://mock2.com/auth",
            )
Example #22
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.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, response=None)

            session = get_session()
            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.find_one()
        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 #23
0
    def test_start_flow(self):
        # Generate the appropriate URL and state token

        with self.app.app.test_request_context("/oauth/connect/mock2/") as ctx:

            # make sure the user is logged in
            authenticate(user=self.user, response=None)

            # auth_url is a property method - it calls out to the external
            #   service to get a temporary key and secret before returning the
            #   auth url
            url = self.provider.auth_url

            session = get_session()

            # Temporary credentials are added to the session
            creds = session.data['oauth_states'][self.provider.short_name]
            assert_in('state', creds)

            # The URL to which the user would be redirected
            parsed = urlparse.urlparse(url)
            params = urlparse.parse_qs(parsed.query)

            # check parameters
            assert_equal(
                params, {
                    'state': [creds['state']],
                    'response_type': ['code'],
                    'client_id': [self.provider.client_id],
                    'redirect_uri': [
                        web_url_for('oauth_callback',
                                    service_name=self.provider.short_name,
                                    _absolute=True)
                    ]
                })

            # check base URL
            assert_equal(
                url.split("?")[0],
                "https://mock2.com/auth",
            )
Example #24
0
def external_login_email_post():
    """
    View to handle email submission for first-time oauth-login user.
    HTTP Method: POST
    """

    form = ResendConfirmationForm(request.form)
    session = get_session()
    if not session.is_external_first_login:
        raise HTTPError(http.UNAUTHORIZED)

    external_id_provider = session.data['auth_user_external_id_provider']
    external_id = session.data['auth_user_external_id']
    fullname = session.data['auth_user_fullname']
    service_url = session.data['service_url']

    # TODO: @cslzchen use user tags instead of destination
    destination = 'dashboard'
    for campaign in campaigns.get_campaigns():
        if campaign != 'institution':
            # Handle different url encoding schemes between `furl` and `urlparse/urllib`.
            # OSF use `furl` to parse service url during service validation with CAS. However, `web_url_for()` uses
            # `urlparse/urllib` to generate service url. `furl` handles `urlparser/urllib` generated urls while ` but
            # not vice versa.
            campaign_url = furl.furl(campaigns.campaign_url_for(campaign)).url
            external_campaign_url = furl.furl(
                campaigns.external_campaign_url_for(campaign)).url
            if campaigns.is_proxy_login(campaign):
                # proxy campaigns: OSF Preprints and branded ones
                if check_service_url_with_proxy_campaign(
                        str(service_url), campaign_url, external_campaign_url):
                    destination = campaign
                    # continue to check branded preprints even service url matches osf preprints
                    if campaign != 'osf-preprints':
                        break
            elif service_url.startswith(campaign_url):
                # osf campaigns: OSF Prereg and ERPC
                destination = campaign
                break

    if form.validate():
        clean_email = form.email.data
        user = get_user(email=clean_email)
        external_identity = {
            external_id_provider: {
                external_id: None,
            },
        }
        try:
            ensure_external_identity_uniqueness(external_id_provider,
                                                external_id, user)
        except ValidationError as e:
            raise HTTPError(http.FORBIDDEN, e.message)
        if user:
            # 1. update user oauth, with pending status
            external_identity[external_id_provider][external_id] = 'LINK'
            if external_id_provider in user.external_identity:
                user.external_identity[external_id_provider].update(
                    external_identity[external_id_provider])
            else:
                user.external_identity.update(external_identity)
            # 2. add unconfirmed email and send confirmation email
            user.add_unconfirmed_email(clean_email,
                                       external_identity=external_identity)
            user.save()
            send_confirm_email(user,
                               clean_email,
                               external_id_provider=external_id_provider,
                               external_id=external_id,
                               destination=destination)
            # 3. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_LINK_SUCCESS.format(
                external_id_provider=external_id_provider, email=user.username)
            kind = 'success'
            # 4. remove session and osf cookie
            remove_session(session)
        else:
            # 1. create unconfirmed user with pending status
            external_identity[external_id_provider][external_id] = 'CREATE'
            user = OSFUser.create_unconfirmed(
                username=clean_email,
                password=None,
                fullname=fullname,
                external_identity=external_identity,
                campaign=None)
            # TODO: [#OSF-6934] update social fields, verified social fields cannot be modified
            user.save()
            # 3. send confirmation email
            send_confirm_email(user,
                               user.username,
                               external_id_provider=external_id_provider,
                               external_id=external_id,
                               destination=destination)
            # 4. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_CREATE_SUCCESS.format(
                external_id_provider=external_id_provider, email=user.username)
            kind = 'success'
            # 5. remove session
            remove_session(session)
        status.push_status_message(message, kind=kind, trust=False)
    else:
        forms.push_errors_to_status(form.errors)

    # Don't go anywhere
    return {'form': form, 'external_id_provider': external_id_provider}
Example #25
0
def get_session_data():
    try:
        return get_session().data
    except (RuntimeError, AttributeError):
        return {}
Example #26
0
    def auth_callback(self, user):
        """Exchange temporary credentials for permanent credentials

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

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

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

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

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

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

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

            try:
                response = OAuth2Session(
                    self.client_id,
                    redirect_uri=web_url_for(
                        'oauth_callback',
                        service_name=self.short_name,
                        _absolute=True
                    ),
                ).fetch_token(
                    self.callback_url,
                    client_secret=self.client_secret,
                    code=request.args.get('code'),
                )
            except (MissingTokenError, RequestsHTTPError):
                raise HTTPError(http.SERVICE_UNAVAILABLE)

        # pre-set as many values as possible for the ``ExternalAccount``
        info = self._default_handle_callback(response)
        # call the hook for subclasses to parse values from the response
        info.update(self.handle_callback(response))

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

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

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

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

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

        self.account.save()

        # add it to the user's list of ``ExternalAccounts``
        if self.account not in user.external_accounts:
            user.external_accounts.append(self.account)
            user.save()
Example #27
0
def external_login_email_post():
    """
    View to handle email submission for first-time oauth-login user.
    HTTP Method: POST
    """

    form = ResendConfirmationForm(request.form)
    session = get_session()
    if not session.is_external_first_login:
        raise HTTPError(http.UNAUTHORIZED)

    external_id_provider = session.data['auth_user_external_id_provider']
    external_id = session.data['auth_user_external_id']
    fullname = session.data['auth_user_fullname']

    if form.validate():
        clean_email = form.email.data
        user = get_user(email=clean_email)
        external_identity = {
            external_id_provider: {
                external_id: None,
            },
        }
        try:
            ensure_external_identity_uniqueness(external_id_provider, external_id, user)
        except ValidationError as e:
            raise HTTPError(http.FORBIDDEN, e.message)
        if user:
            # 1. update user oauth, with pending status
            external_identity[external_id_provider][external_id] = 'LINK'
            if external_id_provider in user.external_identity:
                user.external_identity[external_id_provider].update(external_identity[external_id_provider])
            else:
                user.external_identity.update(external_identity)
            # 2. add unconfirmed email and send confirmation email
            user.add_unconfirmed_email(clean_email, external_identity=external_identity)
            user.save()
            send_confirm_email(user, clean_email, external_id_provider=external_id_provider, external_id=external_id)
            # 3. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_LINK_SUCCESS.format(
                external_id_provider=external_id_provider,
                email=user.username
            )
            kind = 'success'
            # 4. remove session and osf cookie
            remove_session(session)
        else:
            # 1. create unconfirmed user with pending status
            external_identity[external_id_provider][external_id] = 'CREATE'
            user = User.create_unconfirmed(
                username=clean_email,
                password=str(uuid.uuid4()),
                fullname=fullname,
                external_identity=external_identity,
                campaign=None
            )
            # TODO: [#OSF-6934] update social fields, verified social fields cannot be modified
            user.save()
            # 3. send confirmation email
            send_confirm_email(user, user.username, external_id_provider=external_id_provider, external_id=external_id)
            # 4. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_CREATE_SUCCESS.format(
                external_id_provider=external_id_provider,
                email=user.username
            )
            kind = 'success'
            # 5. remove session
            remove_session(session)
        status.push_status_message(message, kind=kind, trust=False)
    else:
        forms.push_errors_to_status(form.errors)

    # Don't go anywhere
    return {
        'form': form,
        'external_id_provider': external_id_provider
    }
Example #28
0
def external_login_email_post():
    """
    View to handle email submission for first-time oauth-login user.
    HTTP Method: POST
    """

    form = ResendConfirmationForm(request.form)
    session = get_session()
    if not session.is_external_first_login:
        raise HTTPError(http.UNAUTHORIZED)

    external_id_provider = session.data['auth_user_external_id_provider']
    external_id = session.data['auth_user_external_id']
    fullname = session.data['auth_user_fullname']
    service_url = session.data['service_url']

    destination = 'dashboard'
    for campaign in campaigns.get_campaigns():
        if campaign != 'institution':
            # Handle different url encoding schemes between `furl` and `urlparse/urllib`.
            # OSF use `furl` to parse service url during service validation with CAS. However, `web_url_for()` uses
            # `urlparse/urllib` to generate service url. `furl` handles `urlparser/urllib` generated urls while ` but
            # not vice versa.
            campaign_url = furl.furl(campaigns.campaign_url_for(campaign)).url
            if campaigns.is_proxy_login(campaign):
                # proxy campaigns: OSF Preprints and branded ones
                if check_service_url_with_proxy_campaign(service_url, campaign_url):
                    destination = campaign
                    # continue to check branded preprints even service url matches osf preprints
                    if campaign != 'osf-preprints':
                        break
            elif service_url.startswith(campaign_url):
                # osf campaigns: OSF Prereg and ERPC
                destination = campaign
                break

    if form.validate():
        clean_email = form.email.data
        user = get_user(email=clean_email)
        external_identity = {
            external_id_provider: {
                external_id: None,
            },
        }
        try:
            ensure_external_identity_uniqueness(external_id_provider, external_id, user)
        except ValidationError as e:
            raise HTTPError(http.FORBIDDEN, e.message)
        if user:
            # 1. update user oauth, with pending status
            external_identity[external_id_provider][external_id] = 'LINK'
            if external_id_provider in user.external_identity:
                user.external_identity[external_id_provider].update(external_identity[external_id_provider])
            else:
                user.external_identity.update(external_identity)
            # 2. add unconfirmed email and send confirmation email
            user.add_unconfirmed_email(clean_email, external_identity=external_identity)
            user.save()
            send_confirm_email(
                user,
                clean_email,
                external_id_provider=external_id_provider,
                external_id=external_id,
                destination=destination
            )
            # 3. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_LINK_SUCCESS.format(
                external_id_provider=external_id_provider,
                email=user.username
            )
            kind = 'success'
            # 4. remove session and osf cookie
            remove_session(session)
        else:
            # 1. create unconfirmed user with pending status
            external_identity[external_id_provider][external_id] = 'CREATE'
            user = User.create_unconfirmed(
                username=clean_email,
                password=str(uuid.uuid4()),
                fullname=fullname,
                external_identity=external_identity,
                campaign=None
            )
            # TODO: [#OSF-6934] update social fields, verified social fields cannot be modified
            user.save()
            # 3. send confirmation email
            send_confirm_email(
                user,
                user.username,
                external_id_provider=external_id_provider,
                external_id=external_id,
                destination=destination
            )
            # 4. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_CREATE_SUCCESS.format(
                external_id_provider=external_id_provider,
                email=user.username
            )
            kind = 'success'
            # 5. remove session
            remove_session(session)
        status.push_status_message(message, kind=kind, trust=False)
    else:
        forms.push_errors_to_status(form.errors)

    # Don't go anywhere
    return {
        'form': form,
        'external_id_provider': external_id_provider
    }
Example #29
0
def get_session_data():
    try:
        return get_session().data
    except (RuntimeError, AttributeError):
        return {}
Example #30
0
def external_login_email_post():
    """
    View to handle email submission for first-time oauth-login user.
    HTTP Method: POST
    """

    form = ResendConfirmationForm(request.form)
    session = get_session()
    if not session.is_external_first_login:
        raise HTTPError(http.UNAUTHORIZED)

    external_id_provider = session.data['auth_user_external_id_provider']
    external_id = session.data['auth_user_external_id']
    fullname = session.data['auth_user_fullname']

    if form.validate():
        clean_email = form.email.data
        user = get_user(email=clean_email)
        external_identity = {
            external_id_provider: {
                external_id: None,
            },
        }
        try:
            ensure_external_identity_uniqueness(external_id_provider, external_id, user)
        except ValidationError as e:
            raise HTTPError(http.FORBIDDEN, e.message)
        if user:
            # 1. update user oauth, with pending status
            external_identity[external_id_provider][external_id] = 'LINK'
            if external_id_provider in user.external_identity:
                user.external_identity[external_id_provider].update(external_identity[external_id_provider])
            else:
                user.external_identity.update(external_identity)
            # 2. add unconfirmed email and send confirmation email
            user.add_unconfirmed_email(clean_email, external_identity=external_identity)
            user.save()
            send_confirm_email(user, clean_email, external_id_provider=external_id_provider, external_id=external_id)
            # 3. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_LINK_SUCCESS.format(
                external_id_provider=external_id_provider,
                email=user.username
            )
            kind = 'success'
            # 4. remove session and osf cookie
            remove_session(session)
        else:
            # 1. create unconfirmed user with pending status
            external_identity[external_id_provider][external_id] = 'CREATE'
            user = User.create_unconfirmed(
                username=clean_email,
                password=str(uuid.uuid4()),
                fullname=fullname,
                external_identity=external_identity,
                campaign=None
            )
            # TODO: [#OSF-6934] update social fields, verified social fields cannot be modified
            user.save()
            # 3. send confirmation email
            send_confirm_email(user, user.username, external_id_provider=external_id_provider, external_id=external_id)
            # 4. notify user
            message = language.EXTERNAL_LOGIN_EMAIL_CREATE_SUCCESS.format(
                external_id_provider=external_id_provider,
                email=user.username
            )
            kind = 'success'
            # 5. remove session
            remove_session(session)
        status.push_status_message(message, kind=kind, trust=False)
    else:
        forms.push_errors_to_status(form.errors)

    # Don't go anywhere
    return {
        'form': form,
        'external_id_provider': external_id_provider
    }
Example #31
0
    def auth_callback(self, user):
        session = get_session()

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

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

            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:
                raise HTTPError(http.SERVICE_UNAVAILABLE)

        info = self._default_handle_callback(response)
        info.update(self.handle_callback(response))

        try:
            self.account = ExternalAccount(
                provider=self.short_name,
                provider_id=info['provider_id'],
            )
            self.account.save()
        except KeyExistsException:
            self.account = ExternalAccount.find_one(
                Q('provider', 'eq', self.short_name) &
                Q('provider_id', 'eq', info['provider_id'])
            )
            assert self.account is not None

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

        if self.account not in user.external_accounts:
            user.external_accounts.append(self.account)
            user.save()
Example #32
0
def get_session_data():
    try:
        return get_session().data
    except RuntimeError:
        return {}