예제 #1
0
    def test_does_not_need_refresh(self):
        self.provider.refresh_time = 1
        external_account = ExternalAccountFactory(
            provider='mock2',
            provider_id='mock_provider_id',
            provider_name='Mock Provider',
            oauth_key='old_key',
            oauth_secret='old_secret',
            refresh_token='old_refresh',
            expires_at=datetime.utcfromtimestamp(time.time() + 200),
        )

        # mock a successful call to the provider to refresh tokens
        httpretty.register_uri(
            httpretty.POST,
            self.provider.auto_refresh_url,
             body=json.dumps({
                'err_msg': 'Should not be hit'
            }),
            status=500
        )

        # .reload() has the side effect of rounding the microsends down to 3 significant figures 
        # (e.g. DT(YMDHMS, 365420) becomes DT(YMDHMS, 365000)), 
        # but must occur after possible refresh to reload tokens.
        # Doing so before allows the `old_expiry == EA.expires_at` comparison to work.
        external_account.reload()  
        old_expiry = external_account.expires_at
        self.provider.account = external_account
        self.provider.refresh_oauth_key(force=False)
        external_account.reload()

        assert_equal(external_account.oauth_key, 'old_key')
        assert_equal(external_account.refresh_token, 'old_refresh')
        assert_equal(external_account.expires_at, old_expiry)
예제 #2
0
    def test_does_need_refresh(self):
        external_account = ExternalAccountFactory(
            provider='mock2',
            provider_id='mock_provider_id',
            provider_name='Mock Provider',
            oauth_key='old_key',
            oauth_secret='old_secret',
            expires_at=datetime.utcfromtimestamp(time.time() - 200),
        )

        # mock a successful call to the provider to refresh tokens
        httpretty.register_uri(
            httpretty.POST,
            self.provider.auto_refresh_url,
             body=json.dumps({
                'access_token': 'refreshed_access_token',
                'expires_in': 3600,
                'refresh_token': 'refreshed_refresh_token'
            })
        )
        
        old_expiry = external_account.expires_at
        self.provider.account = external_account
        self.provider.refresh_oauth_key(force=False)
        external_account.reload()

        assert_equal(external_account.oauth_key, 'refreshed_access_token')
        assert_equal(external_account.refresh_token, 'refreshed_refresh_token')
        assert_not_equal(external_account.expires_at, old_expiry)
        assert_true(external_account.expires_at > old_expiry)
예제 #3
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
        )
예제 #4
0
 def test_fetch_access_token_with_token_not_expired(self):
     external_account = ExternalAccountFactory()
     self.user.external_accounts.append(external_account)
     external_account.expires_at = datetime.utcnow() + relativedelta.relativedelta(minutes=6)
     external_account.oauth_key = "fake-token"
     external_account.save()
     self.node_settings.set_auth(external_account, self.user)
     assert_equal(self.node_settings.fetch_access_token(), "fake-token")
예제 #5
0
    def test_callback_wrong_user(self):
        # Reject temporary credentials not assigned to the user
        #
        # This prohibits users from associating their external account with
        # another user's OSF account by using XSS or similar attack vector to
        # complete the OAuth flow using the logged-in user but their own account
        # on the external service.
        #
        # If the OSF were to allow login via OAuth with the provider in question,
        # this would allow attackers to hijack OSF accounts with a simple script
        # injection.

        # 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()
        account = ExternalAccountFactory(
            provider="mock1a",
            provider_name='Mock 1A',
            oauth_key="temp_key",
            oauth_secret="temp_secret",
            temporary=True
        )
        account.save()
        # associate this ExternalAccount instance with the user
        user.external_accounts.append(account)
        user.save()

        malicious_user = UserFactory()

        # Fake a request context for the callback
        with self.app.app.test_request_context(
                path="/oauth/callback/mock1a/",
                query_string="oauth_token=temp_key&oauth_verifier=mock_verifier"
        ):
            # make sure the user is logged in
            authenticate(user=malicious_user, access_token=None, response=None)

            with assert_raises(PermissionsError):
                # do the key exchange
                self.provider.auth_callback(user=malicious_user)
예제 #6
0
    def setUp(self):
        super(TestGoogleDriveUserSettings, self).setUp()
        self.node = ProjectFactory()
        self.user = self.node.creator

        self.external_account = ExternalAccountFactory()

        self.user.external_accounts.append(self.external_account)
        self.user.save()

        self.user_settings = self.user.get_or_add_addon("googledrive")
예제 #7
0
    def test_fetch_access_token_with_token_expired(self, mock_refresh):
        external_account = ExternalAccountFactory()
        self.user.external_accounts.append(external_account)
        external_account.expires_at = datetime.utcnow() + relativedelta.relativedelta(minutes=4)
        external_account.oauth_key = "fake-token"
        external_account.refresh_token = "refresh-fake-token"
        external_account.save()

        fake_token = {"access_token": "new-access-token", "refresh_token": "new-refresh-token", "expires_at": 1234.5}
        mock_refresh.return_value = fake_token
        self.node_settings.set_auth(external_account, self.user)
        self.node_settings.fetch_access_token()
        mock_refresh.assert_called_once()
        assert_equal(external_account.oauth_key, "new-access-token")
        assert_equal(external_account.refresh_token, "new-refresh-token")
        assert_equal(external_account.expires_at, datetime.utcfromtimestamp(1234.5))
예제 #8
0
    def test_disconnect(self):
        # Disconnect an external account from a user
        external_account = ExternalAccountFactory(
            provider='mock2',
            provider_id='mock_provider_id',
            provider_name='Mock Provider',
        )
        self.user.external_accounts.append(external_account)
        self.user.save()

        # If the external account isn't attached, this test has no meaning
        assert_equal(ExternalAccount.find().count(), 1)
        assert_in(
            external_account,
            self.user.external_accounts,
        )

        response = self.app.delete(
            api_url_for('oauth_disconnect',
                        external_account_id=external_account._id),
            auth=self.user.auth
        )

        # Request succeeded
        assert_equal(
            response.status_code,
            http.OK,
        )

        self.user.reload()
        # external_account.reload()

        # External account has been disassociated with the user
        assert_not_in(
            external_account,
            self.user.external_accounts,
        )

        # External account is still in the database
        assert_equal(ExternalAccount.find().count(), 1)
예제 #9
0
    def test_force_refresh_with_expired_credentials(self):
        external_account = ExternalAccountFactory(
            provider='mock2',
            provider_id='mock_provider_id',
            provider_name='Mock Provider',
            oauth_key='old_key',
            oauth_secret='old_secret',
            expires_at=datetime.utcfromtimestamp(
                time.time() -
                10000)  # Causes has_expired_credentials to be True
        )
        self.provider.account = external_account

        # mock a failing call to the provider to refresh tokens
        httpretty.register_uri(httpretty.POST,
                               self.provider.auto_refresh_url,
                               body=json.dumps({
                                   'error': 'invalid_grant',
                               }),
                               status=401)

        with assert_raises(OAuth2Error):
            ret = self.provider.refresh_oauth_key(force=True)
예제 #10
0
class TestGoogleDriveUserSettings(OsfTestCase):
    def setUp(self):
        super(TestGoogleDriveUserSettings, self).setUp()
        self.node = ProjectFactory()
        self.user = self.node.creator

        self.external_account = ExternalAccountFactory()

        self.user.external_accounts.append(self.external_account)
        self.user.save()

        self.user_settings = self.user.get_or_add_addon("googledrive")

    def tearDown(self):
        super(TestGoogleDriveUserSettings, self).tearDown()
        self.user_settings.remove()
        self.external_account.remove()
        self.node.remove()
        self.user.remove()

    def test_grant_oauth_access_no_metadata(self):
        self.user_settings.grant_oauth_access(node=self.node, external_account=self.external_account)
        self.user_settings.save()

        assert_equal(self.user_settings.oauth_grants, {self.node._id: {self.external_account._id: {}}})

    def test_grant_oauth_access_metadata(self):
        self.user_settings.grant_oauth_access(
            node=self.node, external_account=self.external_account, metadata={"folder": "fake_folder_id"}
        )
        self.user_settings.save()

        assert_equal(
            self.user_settings.oauth_grants, {self.node._id: {self.external_account._id: {"folder": "fake_folder_id"}}}
        )

    def test_verify_oauth_access_no_metadata(self):
        self.user_settings.grant_oauth_access(node=self.node, external_account=self.external_account)
        self.user_settings.save()

        account_has_access = self.user_settings.verify_oauth_access(
            node=self.node, external_account=self.external_account
        )

        factory_account_has_access = self.user_settings.verify_oauth_access(
            node=self.node, external_account=ExternalAccountFactory()
        )

        assert_true(account_has_access)
        assert_false(factory_account_has_access)

    def test_verify_oauth_access_metadata(self):
        self.user_settings.grant_oauth_access(
            node=self.node, external_account=self.external_account, metadata={"folder": "fake_folder_id"}
        )
        self.user_settings.save()

        correct_meta_access = self.user_settings.verify_oauth_access(
            node=self.node, external_account=self.external_account, metadata={"folder": "fake_folder_id"}
        )

        incorrect_meta_no_access = self.user_settings.verify_oauth_access(
            node=self.node, external_account=self.external_account, metadata={"folder": "another_folder_id"}
        )

        assert_true(correct_meta_access)
        assert_false(incorrect_meta_no_access)