def test_get_create_remote_token(models_fixture): """Test create remote token.""" app = models_fixture existing_email = "*****@*****.**" datastore = app.extensions['invenio-accounts'].datastore user = datastore.find_user(email=existing_email) t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret") assert t assert t.token() == ('mytoken', 'mysecret') acc = RemoteAccount.get(user.id, "dev") assert acc assert t.remote_account.id == acc.id assert t.token_type == '' t2 = RemoteToken.create( user.id, "dev", "mytoken2", "mysecret2", token_type='t2' ) assert t2.remote_account.id == acc.id assert t2.token_type == 't2' t3 = RemoteToken.get(user.id, "dev") t4 = RemoteToken.get(user.id, "dev", token_type="t2") assert t4.token() != t3.token() assert RemoteToken.query.count() == 2 acc.delete() assert RemoteToken.query.count() == 0
def test_get_create_remote_token(models_fixture, example): """Test create remote token.""" app = models_fixture existing_email = "*****@*****.**" datastore = app.extensions['invenio-accounts'].datastore user = datastore.find_user(email=existing_email) t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret") assert t assert t.token() == ('mytoken', 'mysecret') acc = RemoteAccount.get(user.id, "dev") assert acc assert t.remote_account.id == acc.id assert t.token_type == '' t2 = RemoteToken.create(user.id, "dev", "mytoken2", "mysecret2", token_type='t2') assert t2.remote_account.id == acc.id assert t2.token_type == 't2' t3 = RemoteToken.get(user.id, "dev") t4 = RemoteToken.get(user.id, "dev", token_type="t2") assert t4.token() != t3.token() assert RemoteToken.query.count() == 2 acc.delete() assert RemoteToken.query.count() == 0
def test_get_create_remote_token(app, models_fixture): """Test create remote token.""" existing_email = '*****@*****.**' datastore = app.extensions['invenio-accounts'].datastore user = datastore.find_user(email=existing_email) t = RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret') assert t assert t.token() == ('mytoken', 'mysecret') acc = RemoteAccount.get(user.id, 'dev') assert acc assert t.remote_account.id == acc.id assert t.token_type == '' t2 = RemoteToken.create( user.id, 'dev', 'mytoken2', 'mysecret2', token_type='t2' ) assert t2.remote_account.id == acc.id assert t2.token_type == 't2' t3 = RemoteToken.get(user.id, 'dev') t4 = RemoteToken.get(user.id, 'dev', token_type='t2') assert t4.token() != t3.token() assert RemoteToken.query.count() == 2 acc.delete() assert RemoteToken.query.count() == 0
def token_setter(remote, token, secret='', token_type='', extra_data=None, user=None): """Set token for user. :param remote: The remote application. :param token: The token to set. :param token_type: The token type. (Default: ``''``) :param extra_data: Extra information. (Default: ``None``) :param user: The user owner of the remote token. If it's not defined, the current user is used automatically. (Default: ``None``) :returns: A :class:`invenio_oauthclient.models.RemoteToken` instance or ``None``. """ session[token_session_key(remote.name)] = (token, secret) user = user or current_user # Save token if user is not anonymous (user exists but can be not active at # this moment) if not user.is_anonymous: uid = user.id cid = remote.consumer_key # Check for already existing token t = RemoteToken.get(uid, cid, token_type=token_type) if t: t.update_token(token, secret) else: t = RemoteToken.create( uid, cid, token, secret, token_type=token_type, extra_data=extra_data ) return t return None
def test_existent_token(self): # Create existing token: RemoteToken, RemoteAccount, UserIdentity. with db.session.begin_nested(): # Create RemoteToken and RemoteAccount. RemoteToken.create( user_id=self.user.id, client_id=current_app.config['ORCID_APP_CREDENTIALS']['consumer_key'], token=self.token, secret=None, extra_data={ 'orcid': self.orcid, 'full_name': self.name, 'allow_push': True, } ) user_identity = UserIdentity( id=self.orcid, method='orcid', id_user=self.user.id ) db.session.add(user_identity) with mock.patch('inspirehep.modules.orcid.tasks.oauth_link_external_id') as mock_oauth_link_external_id: # Mocking `oauth_link_external_id` is necessary because when running # with `isolated_app` it raises # "FlushError: New instance ... with identity key (...) conflicts with persistent instance ..." # rather than the standard and expected `IntegrityError` (which # is raised instead when run without `isolated_app`). mock_oauth_link_external_id.side_effect = AlreadyLinkedError(self.user, self.orcid) _link_user_and_token(self.user, self.name, self.orcid, self.token) self._assert_remote_account_and_remote_token_and_user_identity()
def test_get_create(self): from invenio_oauthclient.models import RemoteAccount, RemoteToken t = RemoteToken.create(self.u1, "dev", "mytoken", "mysecret") assert t assert t.token() == ('mytoken', 'mysecret') acc = RemoteAccount.get(self.u1, "dev") assert acc assert t.remote_account.id == acc.id assert t.token_type == '' t2 = RemoteToken.create( self.u1, "dev", "mytoken2", "mysecret2", token_type='t2' ) assert t2.remote_account.id == acc.id assert t2.token_type == 't2' t3 = RemoteToken.get(self.u1, "dev") t4 = RemoteToken.get(self.u1, "dev", token_type="t2") assert t4.token() != t3.token() assert RemoteToken.query.count() == 2 acc.delete() assert RemoteToken.query.count() == 0
def test_existent_token_for_same_user_but_different_orcid(self): # Create existing token: RemoteToken, RemoteAccount, UserIdentity. other_orcid = 'otherorcid' with db.session.begin_nested(): # Create RemoteToken and RemoteAccount. RemoteToken.create( user_id=self.user.id, client_id=current_app.config['ORCID_APP_CREDENTIALS']['consumer_key'], token=self.token, secret=None, extra_data={ 'orcid': other_orcid, 'full_name': self.name, 'allow_push': True, } ) user_identity = UserIdentity( id=other_orcid, method='orcid', id_user=self.user.id ) db.session.add(user_identity) with pytest.raises(RemoteTokenOrcidMismatch): _link_user_and_token(self.user, self.name, self.orcid, self.token)
def test_get_create_remote_token(app, example): """Test create remote token.""" existing_email = "*****@*****.**" datastore = app.extensions["invenio-accounts"].datastore user = datastore.find_user(email=existing_email) t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret") assert t assert t.token() == ("mytoken", "mysecret") acc = RemoteAccount.get(user.id, "dev") assert acc assert t.remote_account.id == acc.id assert t.token_type == "" t2 = RemoteToken.create(user.id, "dev", "mytoken2", "mysecret2", token_type="t2") assert t2.remote_account.id == acc.id assert t2.token_type == "t2" t3 = RemoteToken.get(user.id, "dev") t4 = RemoteToken.get(user.id, "dev", token_type="t2") assert t4.token() != t3.token() assert RemoteToken.query.count() == 2 acc.delete() assert RemoteToken.query.count() == 0
def test_get_create(self): from invenio_oauthclient.models import RemoteAccount, RemoteToken t = RemoteToken.create(self.u1, "dev", "mytoken", "mysecret") assert t assert t.token() == ('mytoken', 'mysecret') acc = RemoteAccount.get(self.u1, "dev") assert acc assert t.remote_account.id == acc.id assert t.token_type == '' t2 = RemoteToken.create(self.u1, "dev", "mytoken2", "mysecret2", token_type='t2') assert t2.remote_account.id == acc.id assert t2.token_type == 't2' t3 = RemoteToken.get(self.u1, "dev") t4 = RemoteToken.get(self.u1, "dev", token_type="t2") assert t4.token() != t3.token() assert RemoteToken.query.count() == 2 acc.delete() assert RemoteToken.query.count() == 0
def test_get_regression(self): from invenio_oauthclient.models import RemoteToken t3 = RemoteToken.create(self.u2, "dev", "mytoken", "mysecret") t4 = RemoteToken.create(self.u3, "dev", "mytoken", "mysecret") assert RemoteToken.get(self.u2, "dev").remote_account.user_id == t3.remote_account.user_id assert RemoteToken.get(self.u3, "dev").remote_account.user_id == t4.remote_account.user_id
def test_get_regression(self): from invenio_oauthclient.models import RemoteToken t3 = RemoteToken.create(self.u2, "dev", "mytoken", "mysecret") t4 = RemoteToken.create(self.u3, "dev", "mytoken", "mysecret") assert RemoteToken.get(self.u2, "dev").remote_account.user_id == \ t3.remote_account.user_id assert RemoteToken.get(self.u3, "dev").remote_account.user_id == \ t4.remote_account.user_id
def _link_user_and_token(user, name, orcid, token): """Create a link between a user and token, if possible. Args: user (invenio_oauthclient.models.User): an existing user object to connect the token to orcid (string): user's ORCID identifier token (string): OAUTH token for the user Returns: str: the ORCID associated with the new token if we created one, or the ORCID associated with the token whose ``allow_push`` flag changed state. """ result = None try: # Link user and ORCID oauth_link_external_id(user, { 'id': orcid, 'method': 'orcid' }) except AlreadyLinkedError: # User already has their ORCID linked pass # Check whether there are already tokens associated with this # ORCID identifier. tokens = RemoteToken.query.join(RemoteAccount).join(User)\ .join(UserIdentity).filter(UserIdentity.id == orcid).all() if tokens: # Force the allow_push. with db.session.begin_nested(): for token in tokens: if not token.remote_account.extra_data['allow_push']: result = orcid token.remote_account.extra_data['allow_push'] = True else: # If not, create and put the token entry with db.session.begin_nested(): result = orcid RemoteToken.create( user_id=user.id, client_id=get_value(current_app.config, 'ORCID_APP_CREDENTIALS.consumer_key'), token=token, secret=None, extra_data={ 'orcid': orcid, 'full_name': name, 'allow_push': True, } ) return result
def test_utilities(models_fixture): """Test utilities.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore assert obj_or_import_string('invenio_oauthclient.errors') # User existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Authenticate assert not _get_external_id({}) assert not oauth_authenticate('dev', user, require_existing_link=True) _security.confirmable = True _security.login_without_confirmation = False user.confirmed_at = None assert not oauth_authenticate('dev', user) # Tokens t = RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret') assert \ RemoteToken.get(user.id, 'dev', access_token='mytoken') == \ RemoteToken.get_by_token('dev', 'mytoken') assert oauth_get_user('dev', access_token=t.access_token) == user assert \ oauth_get_user('dev', account_info={ 'user': { 'email': existing_email } }) == user # Link user to external id external_id = {'id': '123', 'method': 'test_method'} oauth_link_external_id(user, external_id) with pytest.raises(AlreadyLinkedError): oauth_link_external_id(user, external_id) assert oauth_get_user('dev', account_info={ 'external_id': external_id['id'], 'external_method': external_id['method'] }) == user # Cleanup oauth_unlink_external_id(external_id) acc = RemoteAccount.get(user.id, 'dev') acc.delete()
def test_token_getter(remote, models_fixture, app): """Test token getter on response from OAuth server.""" datastore = app.extensions['invenio-accounts'].datastore existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Missing RemoteToken oauth_authenticate('dev', user) assert not token_getter(remote) # Populated RemoteToken RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret') oauth_authenticate('dev', user) assert token_getter(remote) == ('mytoken', 'mysecret')
def test_get_regression(app, example): """Test regression.""" datastore = app.extensions["invenio-accounts"].datastore email2 = "*****@*****.**" email3 = "*****@*****.**" user2 = datastore.find_user(email=email2) user3 = datastore.find_user(email=email3) t3 = RemoteToken.create(user2.id, "dev", "mytoken", "mysecret") t4 = RemoteToken.create(user3.id, "dev", "mytoken", "mysecret") assert RemoteToken.get(user2.id, "dev").remote_account.user_id == t3.remote_account.user_id assert RemoteToken.get(user3.id, "dev").remote_account.user_id == t4.remote_account.user_id
def test_token_getter(remote, models_fixture): """Test token getter on response from OAuth server.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Missing RemoteToken oauth_authenticate('dev', user) assert not token_getter(remote) # Populated RemoteToken RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret') oauth_authenticate('dev', user) assert token_getter(remote) == ('mytoken', 'mysecret')
def remote_account(app, user_without_role): """Create a remote token from user data.""" remote_account = RemoteAccount.create(1, 'dev', dict()) remote_token = RemoteToken.create(user_without_role.id, 'dev', 'token', 'secret') return remote_account, remote_token
def mock_user(app, request): def teardown(app): with app.app_context(): user = User.query.filter_by(id=2).first() token = RemoteToken.query.filter_by(access_token='123').first() user_identity = UserIdentity.query.filter_by( id='0000-0001-9412-8627', method='orcid').first() remote_account = RemoteAccount.query.filter_by(user_id=2).first() with db.session.begin_nested(): db.session.delete(token) db.session.delete(user_identity) db.session.delete(remote_account) db.session.delete(user) db.session.commit() request.addfinalizer(lambda: teardown(app)) user = User(id=2, ) token = RemoteToken(id_remote_account=1, access_token='123') user_identity = UserIdentity(id='0000-0001-9412-8627', id_user='******', method='orcid') remote_account = RemoteAccount(id=1, user_id=2, extra_data={}, client_id=1, user=user) with app.app_context(): with db.session.begin_nested(): db.session.add(user) db.session.add(user_identity) db.session.add(remote_account) db.session.add(token) db.session.commit() return MockUser(app)
def token_getter(remote, token=''): """Retrieve OAuth access token. Used by flask-oauthlib to get the access token when making requests. :param remote: The remote application. :param token: Type of token to get. Data passed from ``oauth.request()`` to identify which token to retrieve. (Default: ``''``) :returns: The token. """ session_key = token_session_key(remote.name) if session_key not in session and current_user.is_authenticated: # Fetch key from token store if user is authenticated, and the key # isn't already cached in the session. remote_token = RemoteToken.get( current_user.get_id(), remote.consumer_key, token_type=token, ) if remote_token is None: return None # Store token and secret in session session[session_key] = remote_token.token() return session.get(session_key, None)
def create_user(orcid, email, name, consumer_key, token=None, allow_push=False): user = User() user.email = email with db.session.begin_nested(): db.session.add(user) oauth_link_external_id(user, {'id': orcid, 'method': 'orcid'}) if token: with db.session.begin_nested(): db.session.add( RemoteToken.create(user_id=user.id, client_id=consumer_key, token=token, secret=None, extra_data={ 'orcid': orcid, 'full_name': name, 'allow_push': allow_push, }))
def access_token(self): """Return OAuth access token.""" if self.user_id: return RemoteToken.get( self.user_id, self.remote.consumer_key ).access_token return self.remote.get_request_token()[0]
def test_rebuilding_access_tokens(app, models_fixture): """Test rebuilding access tokens with random new SECRET_KEY.""" old_secret_key = app.secret_key datastore = app.extensions['invenio-accounts'].datastore existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Creating a new remote token and commiting to the db test_token = 'mytoken' token_type = 'testing' with db.session.begin_nested(): rt = RemoteToken.create(user.id, 'testkey', test_token, app.secret_key, token_type) db.session.add(rt) db.session.commit() # Changing application SECRET_KEY app.secret_key = 'NEW_SECRET_KEY' db.session.expunge_all() # Asserting the decoding error occurs with the stale SECRET_KEY if sys.version_info[0] < 3: # python 2 remote_token = RemoteToken.query.first() assert remote_token.access_token != test_token else: # python 3 with pytest.raises(ValueError): RemoteToken.query.first() db.session.expunge_all() rebuild_access_tokens(old_secret_key) remote_token = RemoteToken.query.filter_by(token_type=token_type).first() # Asserting the access_token is not changed after rebuilding assert remote_token.access_token == test_token
def get_api(user_id=None): """Get an authenticated GitHub API interface.""" if user_id: access_token = RemoteToken.get(user_id, get_client_id()).access_token else: access_token = get_remote().get_request_token()[0] return init_api(access_token)
def _link_user_and_token(user, name, orcid, token): """Create a link between a user and token, if possible. Args: user (invenio_oauthclient.models.User): an existing user object to connect the token to orcid (string): user's ORCID identifier token (string): OAUTH token for the user """ try: # Link user and ORCID oauth_link_external_id(user, { 'id': orcid, 'method': 'orcid' }) except AlreadyLinkedError: # User already has their ORCID linked pass # Is there already a token associated with this ORCID identifier? if RemoteToken.query.join(RemoteAccount).join(User).join(UserIdentity).filter(UserIdentity.id == orcid).count(): return # If not, create and put the token entry with db.session.begin_nested(): db.session.add(RemoteToken.create( user_id=user.id, client_id=get_value(current_app.config, 'ORCID_APP_CREDENTIALS.consumer_key'), token=token, secret=None, extra_data={ 'orcid': orcid, 'full_name': name, 'allow_push': True, } ))
def create_user(orcid, email, name, consumer_key, token=None, allow_push=False): user = User() user.email = email with db.session.begin_nested(): db.session.add(user) oauth_link_external_id(user, {"id": orcid, "method": "orcid"}) if token: with db.session.begin_nested(): db.session.add( RemoteToken.create( user_id=user.id, client_id=consumer_key, token=token, secret=None, extra_data={ "orcid": orcid, "full_name": name, "allow_push": allow_push, }, ))
def create_user(orcid, email, name, consumer_key, token=None, allow_push=False): user = User() user.email = email with db.session.begin_nested(): db.session.add(user) oauth_link_external_id(user, { 'id': orcid, 'method': 'orcid' }) if token: with db.session.begin_nested(): db.session.add( RemoteToken.create( user_id=user.id, client_id=consumer_key, token=token, secret=None, extra_data={ 'orcid': orcid, 'full_name': name, 'allow_push': allow_push, } ) )
def build_fs(self, current_user, credentials, root=None, callback_url=None, request=None, session=None): """Build google drive filesystem.""" url = url_for('oauthclient.login', remote_app='google_drive') client_id = oauth.remote_apps['google_drive'].consumer_key client_secret = oauth.remote_apps['google_drive'].consumer_secret user_id = current_user.get_id() token = RemoteToken.get(user_id, client_id) if token is not None: credentials = { 'access_token': token.access_token, 'client_id': client_id, 'client_secret': client_secret, 'refresh_token': token.remote_account.extra_data.get('refresh_token'), 'token_expiry': None, 'token_uri': 'https://accounts.google.com/o/oauth2/token' } try: filesystem = GoogleDriveFS(root, credentials) filesystem.about() return filesystem except Exception: raise CloudRedirectUrl(url, __name__) else: raise CloudRedirectUrl(url, __name__)
def test_signup_handler(remote, app_rest, models_fixture): """Test signup handler.""" datastore = app_rest.extensions['invenio-accounts'].datastore existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Already authenticated login_user(user) assert current_user.is_authenticated resp1 = signup_handler(remote) expected_url_args = {"message": "Successfully signed up.", "code": 200} check_response_redirect_url_args(resp1, expected_url_args) logout_user() assert not current_user.is_authenticated # No OAuth token resp2 = signup_handler(remote) expected_url_args = {"message": "Token not found.", "code": 400} check_response_redirect_url_args(resp2, expected_url_args) # Not coming from authorized request token = RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret') token_setter(remote, token, 'mysecret') with pytest.raises(BuildError): signup_handler(remote)
def test_get_regression(app, models_fixture): """Test regression.""" datastore = app.extensions['invenio-accounts'].datastore email2 = '*****@*****.**' email3 = '*****@*****.**' user2 = datastore.find_user(email=email2) user3 = datastore.find_user(email=email3) t3 = RemoteToken.create(user2.id, 'dev', 'mytoken', 'mysecret') t4 = RemoteToken.create(user3.id, 'dev', 'mytoken', 'mysecret') assert RemoteToken.get(user2.id, 'dev').remote_account.user_id == \ t3.remote_account.user_id assert RemoteToken.get(user3.id, 'dev').remote_account.user_id == \ t4.remote_account.user_id
def test_get_regression(app, example): """Test regression.""" datastore = app.extensions['invenio-accounts'].datastore email2 = "*****@*****.**" email3 = "*****@*****.**" user2 = datastore.find_user(email=email2) user3 = datastore.find_user(email=email3) t3 = RemoteToken.create(user2.id, "dev", "mytoken", "mysecret") t4 = RemoteToken.create(user3.id, "dev", "mytoken", "mysecret") assert RemoteToken.get(user2.id, "dev").remote_account.user_id == \ t3.remote_account.user_id assert RemoteToken.get(user3.id, "dev").remote_account.user_id == \ t4.remote_account.user_id
def test_utilities(models_fixture): """Test utilities.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore assert obj_or_import_string('invenio_oauthclient.errors') # User existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Authenticate assert not _get_external_id({}) assert not oauth_authenticate('dev', user, require_existing_link=True) _security.confirmable = True _security.login_without_confirmation = False user.confirmed_at = None assert not oauth_authenticate('dev', user) # Tokens t = RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret') assert \ RemoteToken.get(user.id, 'dev', access_token='mytoken') == \ RemoteToken.get_by_token('dev', 'mytoken') assert oauth_get_user('dev', access_token=t.access_token) == user assert \ oauth_get_user('dev', account_info={'user': {'email': existing_email}}) == user # Link user to external id external_id = {'id': '123', 'method': 'test_method'} oauth_link_external_id(user, external_id) with pytest.raises(AlreadyLinkedError): oauth_link_external_id(user, external_id) assert oauth_get_user('dev', account_info={ 'external_id': external_id['id'], 'external_method': external_id['method'] }) == user # Cleanup oauth_unlink_external_id(external_id) acc = RemoteAccount.get(user.id, 'dev') acc.delete()
def remote_account_fixture(app, models_fixture): """Create a remote token from user data.""" datastore = app.extensions['security'].datastore user = datastore.find_user(email='*****@*****.**') remote_account = RemoteAccount.create(1, 'dev', dict()) remote_token = RemoteToken.create(user.id, 'dev', 'token', 'secret') return remote_account, remote_token
def test_get_regression(models_fixture): """Test regression.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore email2 = '*****@*****.**' email3 = '*****@*****.**' user2 = datastore.find_user(email=email2) user3 = datastore.find_user(email=email3) t3 = RemoteToken.create(user2.id, 'dev', 'mytoken', 'mysecret') t4 = RemoteToken.create(user3.id, 'dev', 'mytoken', 'mysecret') assert RemoteToken.get(user2.id, 'dev').remote_account.user_id == \ t3.remote_account.user_id assert RemoteToken.get(user3.id, 'dev').remote_account.user_id == \ t4.remote_account.user_id
def test_utilities(models_fixture): """Test utilities.""" app = models_fixture datastore = app.extensions["invenio-accounts"].datastore assert obj_or_import_string("invenio_oauthclient.errors") # User existing_email = "*****@*****.**" user = datastore.find_user(email=existing_email) # Authenticate assert not _get_external_id({}) assert not oauth_authenticate("dev", user, require_existing_link=True) _security.confirmable = True _security.login_without_confirmation = False user.confirmed_at = None assert not oauth_authenticate("dev", user) # Tokens t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret") assert RemoteToken.get(user.id, "dev", access_token="mytoken") == RemoteToken.get_by_token("dev", "mytoken") assert oauth_get_user("dev", access_token=t.access_token) == user assert oauth_get_user("dev", account_info={"user": {"email": existing_email}}) == user # Link user to external id external_id = {"id": "123", "method": "test_method"} oauth_link_external_id(user, external_id) with pytest.raises(AlreadyLinkedError): oauth_link_external_id(user, external_id) assert ( oauth_get_user("dev", account_info={"external_id": external_id["id"], "external_method": external_id["method"]}) == user ) # Cleanup oauth_unlink_external_id(external_id) acc = RemoteAccount.get(user.id, "dev") acc.delete()
def session_token(self): """Return OAuth session token.""" session_token = None if self.user_id is not None: session_token = token_getter(self.remote) if session_token: token = RemoteToken.get(self.user_id, self.remote.consumer_key, access_token=session_token[0]) return token return None
def get_token(user_id=None): """Retrieve token for linked GitHub account.""" session_token = None if user_id is None: session_token = token_getter(get_remote()) if session_token: token = RemoteToken.get( current_user.get_id(), get_client_id(), access_token=session_token[0] ) return token return None
def test_repr(app, models_fixture): """Test representation of RemoteAccount and RemoteToken.""" datastore = app.extensions['invenio-accounts'].datastore user = datastore.find_user(email='*****@*****.**') assert 'Remote Token <token_type=type access_token=****oken>' == \ repr(RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret', token_type='type')) assert 'Remote Account <id=1, user_id=1>' == \ repr(RemoteAccount.get(user.id, 'dev'))
def test_repr(models_fixture): """Test representation of RemoteAccount adn RemoteToken.""" datastore = models_fixture.extensions['invenio-accounts'].datastore user = datastore.find_user(email='*****@*****.**') assert 'Remote Token <token_type=type access_token=mytoken>' == \ repr(RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret', token_type='type')) assert 'Remote Account <id=1, user_id=1>' == \ repr(RemoteAccount.get(user.id, 'dev'))
def session_token(self): """Return OAuth session token.""" session_token = None if self.user_id is not None: session_token = token_getter(self.remote) if session_token: token = RemoteToken.get( self.user_id, self.remote.consumer_key, access_token=session_token[0] ) return token return None
def token_setter(remote, token, secret='', token_type='', extra_data=None, user=None): """Set token for user. :param remote: The remote application. :param token: The token to set. :param token_type: The token type. (Default: ``''``) :param extra_data: Extra information. (Default: ``None``) :param user: The user owner of the remote token. If it's not defined, the current user is used automatically. (Default: ``None``) :returns: A :class:`invenio_oauthclient.models.RemoteToken` instance or ``None``. """ session[token_session_key(remote.name)] = (token, secret) user = user or current_user # Save token if user is not anonymous (user exists but can be not active at # this moment) if not user.is_anonymous: uid = user.id cid = remote.consumer_key # Check for already existing token t = RemoteToken.get(uid, cid, token_type=token_type) if t: t.update_token(token, secret) else: t = RemoteToken.create(uid, cid, token, secret, token_type=token_type, extra_data=extra_data) return t return None
def remote_token(app, db, tester_id): """Create a remove token for accessing GitHub API.""" from invenio_oauthclient.models import RemoteToken # Create GitHub link token = RemoteToken.create( tester_id, GitHubAPI.remote.consumer_key, 'test', '', ) db.session.commit() return token
def remote_token(app, db, tester_id): """Create a remove token for accessing GitHub API.""" from invenio_oauthclient.models import RemoteToken from invenio_github.helpers import get_client_id # Create GitHub link token = RemoteToken.create( tester_id, get_client_id(), 'test', '', ) db.session.commit() return token
def _create(cls, model_class, role="superuser", orcid=None, email=None, allow_push=None, token=None, *args, **kwargs): ds = current_app.extensions["invenio-accounts"].datastore role = ds.find_or_create_role(role) user = ds.create_user( id=fake.random_number(digits=8, fix_len=True), email=fake.email() if not email else email, password=hash_password(fake.password()), active=True, roles=[role], ) if orcid: user_orcid_id = UserIdentity(id=orcid, method="orcid", id_user=user.get_id()) db.session.add(user_orcid_id) RemoteToken.create( user_id=user.get_id(), client_id="orcid", token=token, secret=None, extra_data={ "orcid": orcid, "allow_push": allow_push }, ) return user
def disconnect(remote): """Disconnect callback handler for GitHub. This is a test! """ # User must be authenticated if not current_user.is_authenticated(): return current_app.login_manager.unauthorized() token = RemoteToken.get(current_user.get_id(), remote.consumer_key) if token: disconnect_github.delay(remote.name, token.access_token, token.remote_account.extra_data) token.remote_account.delete() return redirect(url_for('oauthclient_settings.index'))
def disconnect(remote): """Disconnect callback handler for GitHub. This is a test! """ # User must be authenticated if not current_user.is_authenticated(): return current_app.login_manager.unauthorized() token = RemoteToken.get(current_user.get_id(), remote.consumer_key) if token: disconnect_github.delay( remote.name, token.access_token, token.remote_account.extra_data ) token.remote_account.delete() return redirect(url_for('oauthclient_settings.index'))
def disconnect(remote): """Disconnect callback handler for GitHub.""" # User must be authenticated if not current_user.is_authenticated: return current_app.login_manager.unauthorized() external_method = 'github' external_ids = [i.id for i in current_user.external_identifiers if i.method == external_method] if external_ids: oauth_unlink_external_id(dict(id=external_ids[0], method=external_method)) user_id = current_user.get_id() token = RemoteToken.get(user_id, remote.consumer_key) if token: extra_data = token.remote_account.extra_data # Delete the token that we issued for GitHub to deliver webhooks webhook_token_id = extra_data.get('tokens', {}).get('webhook') ProviderToken.query.filter_by(id=webhook_token_id).delete() # Disable GitHub webhooks from our side db_repos = Repository.query.filter_by(user_id=user_id).all() # Keep repositories with hooks to pass to the celery task later on repos_with_hooks = [(r.github_id, r.hook) for r in db_repos if r.hook] for repo in db_repos: try: Repository.disable(user_id=user_id, github_id=repo.github_id, name=repo.name) except NoResultFound: # If the repository doesn't exist, no action is necessary pass db.session.commit() # Send Celery task for webhooks removal and token revocation disconnect_github.delay(token.access_token, repos_with_hooks) # Delete the RemoteAccount (along with the associated RemoteToken) token.remote_account.delete() return redirect(url_for('invenio_oauthclient_settings.index'))
def disconnect(remote): """Disconnect callback handler for GitHub.""" # User must be authenticated if not current_user.is_authenticated: return current_app.login_manager.unauthorized() external_method = 'github' external_ids = [i.id for i in current_user.external_identifiers if i.method == external_method] if external_ids: oauth_unlink_external_id(dict(id=external_ids[0], method=external_method)) user_id = int(current_user.get_id()) token = RemoteToken.get(user_id, remote.consumer_key) if token: extra_data = token.remote_account.extra_data # Delete the token that we issued for GitHub to deliver webhooks webhook_token_id = extra_data.get('tokens', {}).get('webhook') ProviderToken.query.filter_by(id=webhook_token_id).delete() # Disable GitHub webhooks from our side db_repos = Repository.query.filter_by(user_id=user_id).all() # Keep repositories with hooks to pass to the celery task later on repos_with_hooks = [(r.github_id, r.hook) for r in db_repos if r.hook] for repo in db_repos: try: Repository.disable(user_id=user_id, github_id=repo.github_id, name=repo.name) except NoResultFound: # If the repository doesn't exist, no action is necessary pass db.session.commit() # Send Celery task for webhooks removal and token revocation disconnect_github.delay(token.access_token, repos_with_hooks) # Delete the RemoteAccount (along with the associated RemoteToken) token.remote_account.delete() return redirect(url_for('invenio_oauthclient_settings.index'))
def test_account_setup(app, example_cern, models_fixture): """Test account setup after login.""" client = app.test_client() ioc = app.extensions['oauthlib.client'] # Ensure remote apps have been loaded (due to before first request) client.get(url_for("invenio_oauthclient.login", remote_app='cern')) example_response, example_token, example_account_info = example_cern res = get_dict_from_response(example_response) mock_remote_get(ioc, 'cern', example_response) app = models_fixture datastore = app.extensions['invenio-accounts'].datastore user = datastore.find_user(email="*****@*****.**") token = RemoteToken.create( user.id, 'client_id', example_token['access_token'], 'secret', token_type=example_token['token_type'] ) account_setup(ioc.remote_apps['cern'], token, None)
def test_signup_handler(remote, app, models_fixture): """Test signup handler.""" datastore = app.extensions['invenio-accounts'].datastore existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Already authenticated login_user(user) assert current_user.is_authenticated resp1 = signup_handler(remote) check_redirect_location(resp1, '/') logout_user() assert not current_user.is_authenticated # No OAuth token resp2 = signup_handler(remote) check_redirect_location(resp2, '/') # Not coming from authorized request token = RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret') token_setter(remote, token, 'mysecret') with pytest.raises(BuildError): signup_handler(remote)
def test_rebuilding_access_tokens(models_fixture): """Test rebuilding access tokens with random new SECRET_KEY.""" app = models_fixture old_secret_key = app.secret_key datastore = app.extensions['invenio-accounts'].datastore existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Creating a new remote token and commiting to the db test_token = 'mytoken' token_type = 'testing' with db.session.begin_nested(): rt = RemoteToken.create(user.id, 'testkey', test_token, app.secret_key, token_type) db.session.add(rt) db.session.commit() # Changing application SECRET_KEY app.secret_key = 'NEW_SECRET_KEY' db.session.expunge_all() # Asserting the decoding error occurs with the stale SECRET_KEY if sys.version_info[0] < 3: # python 2 remote_token = RemoteToken.query.first() assert remote_token.access_token != test_token else: # python 3 with pytest.raises(UnicodeDecodeError): RemoteToken.query.first() db.session.expunge_all() rebuild_access_tokens(old_secret_key) remote_token = RemoteToken.query.filter_by(token_type=token_type).first() # Asserting the access_token is not changed after rebuilding assert remote_token.access_token == test_token
def build_fs(self, current_user, credentials, root=None, callback_url=None, request=None, session=None): """Build dropbox filesystem.""" url = url_for('oauthclient.login', remote_app='dropbox') client_id = oauth.remote_apps['dropbox'].consumer_key user_id = current_user.get_id() token = RemoteToken.get(user_id, client_id) if token is not None: credentials = {'access_token': token.access_token} try: filesystem = DropboxFS(root, credentials) filesystem.about() return filesystem except ResourceNotFoundError: if(root != "/"): filesystem = DropboxFS("/", credentials) filesystem.makedir(root, recursive=True) filesystem = DropboxFS(root, credentials) return filesystem except Exception: raise CloudRedirectUrl(url, __name__) else: raise CloudRedirectUrl(url, __name__)
def test_signup_handler(remote, models_fixture): """Test signup handler.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) # Already authenticated login_user(user) assert current_user.is_authenticated resp1 = signup_handler(remote) check_redirect_location(resp1, '/') logout_user() assert not current_user.is_authenticated # No OAuth token resp2 = signup_handler(remote) check_redirect_location(resp2, '/') # Not coming from authorized request token = RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret') token_setter(remote, token, 'mysecret') with pytest.raises(BuildError): signup_handler(remote)
def test_token_getter_setter(self, session, save_session): from invenio_oauthclient.models import RemoteToken from invenio_oauthclient.handlers import token_getter from invenio_oauthclient.client import oauth # Mock user user = MagicMock() user.get_id = MagicMock(return_value=1) user.is_authenticated = MagicMock(return_value=True) # Mock session id session.sid = '1234' with patch('flask_login._get_user', return_value=user): with self.app.test_client() as c: # First call login to be redirected res = c.get(url_for("oauthclient.login", remote_app='full')) assert res.status_code == 302 assert res.location.startswith( oauth.remote_apps['full'].authorize_url ) state = parse_qs(urlparse(res.location).query)['state'][0] # Mock resposen class self.mock_response(app='full') # Imitate that the user authorized our request in the remote # application. c.get(url_for( "oauthclient.authorized", remote_app='full', code='test', state=state, )) # Assert if everything is as it should be. from flask import session as flask_session assert flask_session['oauth_token_full'] == \ ('test_access_token', '') t = RemoteToken.get(1, "fullid") assert t.remote_account.client_id == 'fullid' assert t.access_token == 'test_access_token' assert RemoteToken.query.count() == 1 # Mock a new authorized request self.mock_response(app='full', data={ "access_token": "new_access_token", "scope": "", "token_type": "bearer" }) c.get(url_for( "oauthclient.authorized", remote_app='full', code='test', state=state )) t = RemoteToken.get(1, "fullid") assert t.access_token == 'new_access_token' assert RemoteToken.query.count() == 1 val = token_getter(oauth.remote_apps['full']) assert val == ('new_access_token', '') # Disconnect account res = c.get(url_for( "oauthclient.disconnect", remote_app='full', )) assert res.status_code == 302 assert res.location.endswith( url_for('oauthclient_settings.index') ) # Assert that remote account have been removed. t = RemoteToken.get(1, "fullid") assert t is None
def test_token_getter_setter(monkeypatch): """Test token getter setter.""" # Mock session id monkeypatch.setattr('flask_login._create_identifier', lambda: '1234') monkeypatch.setattr( 'invenio_oauthclient.views.client._create_identifier', lambda: '1234') app = setup_app() oauth = app.extensions['oauthlib.client'] # Mock user user = MagicMock() user.id = 1 user.get_id = MagicMock(return_value=1) user.is_anonymous = False with patch('flask_login._get_user', return_value=user): with app.test_client() as c: # First call login to be redirected res = c.get(url_for("invenio_oauthclient.login", remote_app='full')) assert res.status_code == 302 assert res.location.startswith( oauth.remote_apps['full'].authorize_url ) state = parse_qs(urlparse(res.location).query)['state'][0] # Mock resposen class mock_response(app.extensions['oauthlib.client'], 'full') # Imitate that the user authorized our request in the remote # application. c.get(url_for( "invenio_oauthclient.authorized", remote_app='full', code='test', state=state, )) # Assert if everything is as it should be. from flask import session as flask_session assert flask_session['oauth_token_full'] == \ ('test_access_token', '') t = RemoteToken.get(1, "fullid") assert t.remote_account.client_id == 'fullid' assert t.access_token == 'test_access_token' assert RemoteToken.query.count() == 1 # Mock a new authorized request mock_response(app.extensions['oauthlib.client'], 'full', data={ "access_token": "new_access_token", "scope": "", "token_type": "bearer" }) c.get(url_for( "invenio_oauthclient.authorized", remote_app='full', code='test', state=state )) t = RemoteToken.get(1, "fullid") assert t.access_token == 'new_access_token' assert RemoteToken.query.count() == 1 val = token_getter( app.extensions['oauthlib.client'].remote_apps['full']) assert val == ('new_access_token', '') # Disconnect account res = c.get(url_for( "invenio_oauthclient.disconnect", remote_app='full', )) assert res.status_code == 302 assert res.location.endswith( url_for('invenio_oauthclient_settings.index') ) # Assert that remote account have been removed. t = RemoteToken.get(1, "fullid") assert t is None
def _link_user_and_token(user, name, orcid, access_token): """Create a link between a user and token, if possible. Args: user (invenio_oauthclient.models.User): an existing user object to connect the token to orcid (string): user's ORCID identifier access_token (string): OAUTH token for the user Returns: str: the ORCID associated with the token newly created/enabled. """ try: # Link user and ORCID oauth_link_external_id(user, { 'id': orcid, 'method': 'orcid' }) # Note: AlreadyLinkedError becomes FlushError when testing with isolated_app. except (AlreadyLinkedError, FlushError): # User already has their ORCID linked pass # Search for existing tokens. # Note: there can be only 1 RemoteToken per given RemoteAccount. existing_remote_token = RemoteToken.query.join(RemoteAccount).filter_by( user=user).one_or_none() # If not existing_remote_token, create one. if not existing_remote_token: with db.session.begin_nested(): RemoteToken.create( user_id=user.id, client_id=get_value(current_app.config, 'ORCID_APP_CREDENTIALS.consumer_key'), token=access_token, secret=None, extra_data={ 'orcid': orcid, 'full_name': name, 'allow_push': True, } ) return orcid # If there is an existing_remote_token: # ensure it is associated to this ORCID and # set allow_push to True. # # Get the ORCID associated with this token. user_identity = UserIdentity.query.filter_by(user=user, method='orcid').one_or_none() # Ensure data consistency. if not user_identity: msg = 'No UserIdentity for user={}, method="orcid", while' \ ' instead there is a RemoteAccount={} and RemoteToken={}' raise Exception(msg.format( user, existing_remote_token.remote_account, existing_remote_token)) if user_identity.id != existing_remote_token.remote_account.extra_data['orcid']: msg = 'UserIdentity={} and RemoteToken={} ORCID mismatch: {} != {}' raise Exception(msg.format( user_identity, existing_remote_token, user_identity.id, existing_remote_token.remote_account.extra_data['orcid'] )) if existing_remote_token.remote_account.extra_data['orcid'] != orcid: raise RemoteTokenOrcidMismatch(user, [existing_remote_token.remote_account.extra_data['orcid'], orcid]) # Force the allow_push. with db.session.begin_nested(): if not existing_remote_token.remote_account.extra_data['allow_push']: existing_remote_token.remote_account.extra_data['allow_push'] = True return orcid return None