def test_account_setup(app, example_cern, models_fixture): """Test account setup after login.""" with app.test_client() as c: ioc = app.extensions['oauthlib.client'] # Ensure remote apps have been loaded (due to before first request) resp = c.get(url_for('invenio_oauthclient.login', remote_app='cern')) assert resp.status_code == 302 example_response, example_token, example_account_info = example_cern mock_response(app.extensions['oauthlib.client'], 'cern', example_token) mock_remote_get(ioc, 'cern', example_response) resp = c.get(url_for( 'invenio_oauthclient.authorized', remote_app='cern', code='test', state=get_state('cern'))) assert resp.status_code == 302 assert resp.location == ('http://localhost/account/settings/' 'linkedaccounts/') assert len(g.identity.provides) == 7 datastore = app.extensions['invenio-accounts'].datastore user = datastore.find_user(email='*****@*****.**') assert user with app.test_request_context(): resp = disconnect_handler(ioc.remote_apps['cern']) assert resp.status_code >= 300 login_user(user) assert len(g.identity.provides) == 7 disconnect_handler(ioc.remote_apps['cern'])
async def send(request, **_): if "resource" not in request.query: # availability probe return mock_response(status_code=400, json_payload={}) return mock_response(status_code=code, json_payload={"error": error_message})
async def test_azure_arc(tmpdir): """Azure Arc 2019-11-01""" access_token = "****" api_version = "2019-11-01" expires_on = 42 identity_endpoint = "http://localhost:42/token" imds_endpoint = "http://localhost:42" scope = "scope" secret_key = "XXXX" key_file = tmpdir.mkdir("key").join("key_file.key") key_file.write(secret_key) assert key_file.read() == secret_key key_path = os.path.join(key_file.dirname, key_file.basename) transport = async_validating_transport( requests=[ Request( base_url=identity_endpoint, method="GET", required_headers={"Metadata": "true"}, required_params={ "api-version": api_version, "resource": scope }, ), Request( base_url=identity_endpoint, method="GET", required_headers={ "Metadata": "true", "Authorization": "Basic {}".format(secret_key) }, required_params={ "api-version": api_version, "resource": scope }, ), ], responses=[ # first response gives path to authentication key mock_response(status_code=401, headers={ "WWW-Authenticate": "Basic realm={}".format(key_path) }), mock_response( json_payload={ "access_token": access_token, "expires_on": expires_on, "resource": scope, "token_type": "Bearer", }), ], ) with mock.patch( "os.environ", { EnvironmentVariables.IDENTITY_ENDPOINT: identity_endpoint, EnvironmentVariables.IMDS_ENDPOINT: imds_endpoint }, ): token = await ManagedIdentityCredential(transport=transport ).get_token(scope) assert token.token == access_token assert token.expires_on == expires_on
async def test_app_service_user_assigned_identity(): """App Service 2017-09-01: MSI_ENDPOINT, MSI_SECRET set""" expected_token = "****" expires_on = 42 client_id = "some-guid" endpoint = "http://localhost:42/token" secret = "expected-secret" scope = "scope" param_name, param_value = "foo", "bar" transport = async_validating_transport( requests=[ Request( base_url=endpoint, method="GET", required_headers={ "secret": secret, "User-Agent": USER_AGENT }, required_params={ "api-version": "2017-09-01", "clientid": client_id, "resource": scope }, ), Request( base_url=endpoint, method="GET", required_headers={ "secret": secret, "User-Agent": USER_AGENT }, required_params={ "api-version": "2017-09-01", "resource": scope, param_name: param_value }, ), ], responses=[ mock_response( json_payload={ "access_token": expected_token, "expires_on": "01/01/1970 00:00:{} +00:00".format( expires_on), "resource": scope, "token_type": "Bearer", }) ] * 2, ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, { EnvironmentVariables.MSI_ENDPOINT: endpoint, EnvironmentVariables.MSI_SECRET: secret }, clear=True, ): credential = ManagedIdentityCredential(client_id=client_id, transport=transport) token = await credential.get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on credential = ManagedIdentityCredential( client_id=client_id, transport=transport, identity_config={param_name: param_value}) token = await credential.get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on
async def test_prefers_app_service_2017_09_01(): """When the environment is configured for both App Service versions, the credential should prefer 2017-09-01 Support for 2019-08-01 was removed due to https://github.com/Azure/azure-sdk-for-python/issues/14670. This test should be removed when that support is added back. """ access_token = "****" expires_on = 42 expected_token = AccessToken(access_token, expires_on) url = "http://localhost:42/token" secret = "expected-secret" scope = "scope" transport = async_validating_transport( requests=[ Request( url, method="GET", required_headers={ "secret": secret, "User-Agent": USER_AGENT }, required_params={ "api-version": "2017-09-01", "resource": scope }, ) ] * 2, responses=[ mock_response( json_payload={ "access_token": access_token, "expires_on": "01/01/1970 00:00:{} +00:00".format( expires_on), # linux format "resource": scope, "token_type": "Bearer", }), mock_response( json_payload={ "access_token": access_token, "expires_on": "1/1/1970 12:00:{} AM +00:00".format( expires_on), # windows format "resource": scope, "token_type": "Bearer", }), ], ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, { EnvironmentVariables.IDENTITY_ENDPOINT: url, EnvironmentVariables.IDENTITY_HEADER: secret, EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: secret, }, clear=True, ): credential = ManagedIdentityCredential(transport=transport) token = await credential.get_token(scope) assert token == expected_token assert token.expires_on == expires_on credential = ManagedIdentityCredential(transport=transport) token = await credential.get_token(scope) assert token == expected_token assert token.expires_on == expires_on
async def mock_send(request, **kwargs): actual = urlparse(request.url) assert actual.scheme == "https" assert actual.netloc == expected_netloc assert actual.path.startswith("/" + tenant_id) return mock_response(json_payload={"token_type": "Bearer", "expires_in": 42, "access_token": access_token})
def test_authorized_already_authenticated(models_fixture, example_github): """Test authorized callback with sign-up.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore login_manager = app.login_manager existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) @login_manager.user_loader def load_user(user_id): return user @app.route('/foo_login') def login(): login_user(user) return 'Logged In' with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email='*****@*****.**') with app.test_client() as client: # make a fake login (using my login function) client.get('/foo_login', follow_redirects=True) # Ensure remote apps have been loaded (due to before first # request) client.get( url_for('invenio_oauthclient.login', remote_app='github')) # Mock access token request mock_response(app.extensions['oauthlib.client'], 'github', example_github) # User then goes to 'Linked accounts' and clicks 'Connect' resp = client.get( url_for('invenio_oauthclient.login', remote_app='github', next='/someurl/')) assert resp.status_code == 302 # User authorized the requests and is redirected back resp = client.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) # Assert database state (Sign-up complete) u = User.query.filter_by(email=existing_email).one() remote = RemoteAccount.query.filter_by(user_id=u.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() # Disconnect link resp = client.get( url_for('invenio_oauthclient.disconnect', remote_app='github')) assert resp.status_code == 302 # User exists u = User.query.filter_by(email=existing_email).one() assert 0 == UserIdentity.query.filter_by(method='orcid', id_user=u.id, id='githubuser').count() assert RemoteAccount.query.filter_by(user_id=u.id).count() == 0 assert RemoteToken.query.count() == 0
def test_authorized_already_authenticated(models_fixture, example_github): """Test authorized callback with sign-up.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore login_manager = app.login_manager existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) @login_manager.user_loader def load_user(user_id): return user @app.route('/foo_login') def login(): login_user(user) return 'Logged In' with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email='*****@*****.**') with app.test_client() as client: # make a fake login (using my login function) client.get('/foo_login', follow_redirects=True) # Ensure remote apps have been loaded (due to before first # request) client.get(url_for('invenio_oauthclient.login', remote_app='github')) # Mock access token request mock_response(app.extensions['oauthlib.client'], 'github', example_github) # User then goes to 'Linked accounts' and clicks 'Connect' resp = client.get( url_for('invenio_oauthclient.login', remote_app='github', next='/someurl/') ) assert resp.status_code == 302 # User authorized the requests and is redirected back resp = client.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) # Assert database state (Sign-up complete) u = User.query.filter_by(email=existing_email).one() remote = RemoteAccount.query.filter_by(user_id=u.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() # Disconnect link resp = client.get( url_for('invenio_oauthclient.disconnect', remote_app='github')) assert resp.status_code == 302 # User exists u = User.query.filter_by(email=existing_email).one() assert 0 == UserIdentity.query.filter_by( method='orcid', id_user=u.id, id='githubuser' ).count() assert RemoteAccount.query.filter_by(user_id=u.id).count() == 0 assert RemoteToken.query.count() == 0
def mock_send(request, **_): if not request.body: return get_discovery_response() assert request.url.startswith("https://localhost/" + expected_tenant_id) return mock_response(json_payload=build_aad_response(access_token="*"))
def test_token_exchange(tmpdir): exchange_token = "exchange-token" token_file = tmpdir.join("token") token_file.write(exchange_token) access_token = "***" authority = "https://localhost" default_client_id = "default_client_id" tenant = "tenant_id" scope = "scope" success_response = mock_response( json_payload={ "access_token": access_token, "expires_in": 3600, "ext_expires_in": 3600, "expires_on": int(time.time()) + 3600, "not_before": int(time.time()), "resource": scope, "token_type": "Bearer", }) transport = validating_transport( requests=[ Request( base_url=authority, method="POST", required_data={ "client_assertion": exchange_token, "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_id": default_client_id, "grant_type": "client_credentials", "scope": scope, }, ) ], responses=[success_response], ) mock_environ = { EnvironmentVariables.AZURE_AUTHORITY_HOST: authority, EnvironmentVariables.AZURE_CLIENT_ID: default_client_id, EnvironmentVariables.AZURE_TENANT_ID: tenant, EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE: token_file.strpath, } # credential should default to AZURE_CLIENT_ID with mock.patch.dict("os.environ", mock_environ, clear=True): credential = ManagedIdentityCredential(transport=transport) token = credential.get_token(scope) assert token.token == access_token # client_id kwarg should override AZURE_CLIENT_ID nondefault_client_id = "non" + default_client_id transport = validating_transport( requests=[ Request( base_url=authority, method="POST", required_data={ "client_assertion": exchange_token, "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_id": nondefault_client_id, "grant_type": "client_credentials", "scope": scope, }, ) ], responses=[success_response], ) with mock.patch.dict("os.environ", mock_environ, clear=True): credential = ManagedIdentityCredential(client_id=nondefault_client_id, transport=transport) token = credential.get_token(scope) assert token.token == access_token # AZURE_CLIENT_ID may not have a value, in which case client_id is required transport = validating_transport( requests=[ Request( base_url=authority, method="POST", required_data={ "client_assertion": exchange_token, "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_id": nondefault_client_id, "grant_type": "client_credentials", "scope": scope, }, ) ], responses=[success_response], ) with mock.patch.dict( "os.environ", { EnvironmentVariables.AZURE_AUTHORITY_HOST: authority, EnvironmentVariables.AZURE_TENANT_ID: tenant, EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE: token_file.strpath, }, clear=True, ): with pytest.raises(ValueError): ManagedIdentityCredential() credential = ManagedIdentityCredential(client_id=nondefault_client_id, transport=transport) token = credential.get_token(scope) assert token.token == access_token
def test_authorized_signup_username_already_exists(app, example_github, user): """Test authorized callback with sign-up.""" example_email = '*****@*****.**' with app.test_client() as c: # User login with email 'info' with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email=example_email) # Ensure remote apps have been loaded (due to before first # request) resp = c.get(url_for('invenio_oauthclient.login', remote_app='github')) assert resp.status_code == 302 mock_response(app.extensions['oauthlib.client'], 'github', example_github) # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ( 'http://localhost' + url_for('invenio_oauthclient.signup', remote_app='github') ) # User fills form to register resp = c.post( resp.headers['Location'], data={ 'email': example_email, 'password': '******', 'profile.username': '******', 'profile.full_name': 'pluto', } ) assert resp.status_code == 302 # Assert database state (Sign-up complete) my_user = User.query.filter_by(email=example_email).one() remote = RemoteAccount.query.filter_by(user_id=my_user.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() assert my_user.active # Disconnect link # should not work, because it's the user's only means of login resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='github') ) assert resp.status_code == 400 my_user = User.query.filter_by(email=example_email).one() assert 1 == UserIdentity.query.filter_by( method='github', id_user=my_user.id, id='githubuser' ).count() # set a password for the user my_user.password = hash_password("1234") db.session.commit() # Disconnect again resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='github')) assert resp.status_code == 302 my_user = User.query.filter_by(email=example_email).one() assert 0 == UserIdentity.query.filter_by( method='github', id_user=my_user.id, id='githubuser' ).count() assert RemoteAccount.query.filter_by( user_id=my_user.id).count() == 0 assert RemoteToken.query.count() == 0 assert User.query.count() == 2
def test_authorized_signup(userprofiles_app, example_orcid, orcid_bio): """Test authorized callback with sign-up.""" app = userprofiles_app example_data, example_account_info = example_orcid example_email = '*****@*****.**' with app.test_client() as c: # Ensure remote apps have been loaded (due to before first # request) c.get(url_for('invenio_oauthclient.login', remote_app='orcid')) mock_response(app.extensions['oauthlib.client'], 'orcid', example_data) # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='orcid', code='test', state=get_state('orcid'))) assert resp.status_code == 302 assert resp.location == ( 'http://localhost' + url_for('invenio_oauthclient.signup', remote_app='orcid') ) # User load sign-up page. resp = c.get(url_for('invenio_oauthclient.signup', remote_app='orcid')) assert resp.status_code == 200 account_info = session[token_session_key('orcid') + '_account_info'] data = { 'email': example_email, 'password': '******', 'profile.username': '******', 'profile.full_name': account_info['user']['profile']['full_name'], } # Mock request to ORCID to get user bio. httpretty.enable() httpretty.register_uri( httpretty.GET, 'http://orcid.org/{0}/orcid-bio'.format(example_data['orcid']), body=orcid_bio, content_type='application/orcid+json; qs=2;charset=UTF-8', ) # User fills form to register resp = c.post( url_for('invenio_oauthclient.signup', remote_app='orcid'), data=data, ) assert resp.status_code == 302 httpretty.disable() # Assert database state (Sign-up complete) user = User.query.filter_by(email=example_email).one() UserIdentity.query.filter_by( method='orcid', id_user=user.id, id=example_data['orcid'] ).one() # FIXME see contrib/orcid.py line 167 assert user.profile.full_name == 'Josiah Carberry' # assert user.given_names == 'Josiah' # assert user.family_name == 'Carberry' # check that the user's email is not yet validated assert user.active # check that the validation email has been sent # assert hasattr(locmem, 'outbox') and len(locmem.outbox) == 1 # Disconnect link resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='orcid')) assert resp.status_code == 302 # User exists user = User.query.filter_by(email=example_email).one() # UserIdentity removed. assert 0 == UserIdentity.query.filter_by( method='orcid', id_user=user.id, id=example_data['orcid'] ).count() assert RemoteAccount.query.filter_by(user_id=user.id).count() == 0 assert RemoteToken.query.count() == 0
def test_authorized_already_authenticated(models_fixture, example_orcid, orcid_bio): """Test authorized callback with sign-up.""" app = models_fixture datastore = app.extensions['invenio-accounts'].datastore login_manager = app.login_manager example_data, example_account_info = example_orcid existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) @login_manager.user_loader def load_user(user_id): return user @app.route('/foo_login') def login(): login_user(user) return 'Logged In' with app.test_client() as client: # make a fake login (using my login function) client.get('/foo_login', follow_redirects=True) # Ensure remote apps have been loaded (due to before first # request) client.get(url_for('invenio_oauthclient.login', remote_app='orcid')) # Mock access token request mock_response(app.extensions['oauthlib.client'], 'orcid', example_data) # Mock request to ORCID to get user bio. httpretty.enable() httpretty.register_uri( httpretty.GET, 'https://pub.orcid.org/v1.2/{0}/orcid-bio'.format( example_data['orcid']), body=orcid_bio, content_type='application/orcid+json; qs=2;charset=UTF-8', ) # User then goes to 'Linked accounts' and clicks 'Connect' resp = client.get( url_for('invenio_oauthclient.login', remote_app='orcid', next='/someurl/') ) assert resp.status_code == 302 # User authorized the requests and is redirected back resp = client.get( url_for('invenio_oauthclient.authorized', remote_app='orcid', code='test', state=get_state('orcid'))) httpretty.disable() # Assert database state (Sign-up complete) u = User.query.filter_by(email=existing_email).one() UserIdentity.query.filter_by( method='orcid', id_user=u.id, id=example_data['orcid'] ).one() # FIXME see contrib/orcid.py line 167 # assert u.given_names == 'Josiah' # assert u.family_name == 'Carberry' # Disconnect link resp = client.get( url_for('invenio_oauthclient.disconnect', remote_app='orcid')) assert resp.status_code == 302 # User exists u = User.query.filter_by(email=existing_email).one() # UserIdentity removed. assert 0 == UserIdentity.query.filter_by( method='orcid', id_user=u.id, id=example_data['orcid'] ).count()
def test_authorized_signup_valid_user(app, example_github): """Test authorized callback with sign-up.""" example_email = '*****@*****.**' with app.test_client() as c: # User login with email 'info' with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email='*****@*****.**') # Ensure remote apps have been loaded (due to before first # request) resp = c.get(url_for('invenio_oauthclient.login', remote_app='github')) assert resp.status_code == 302 mock_response(app.extensions['oauthlib.client'], 'github', example_github) # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ('http://localhost/account/settings/' + 'linkedaccounts/') # Assert database state (Sign-up complete) user = User.query.filter_by(email=example_email).one() remote = RemoteAccount.query.filter_by(user_id=user.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() assert user.active # Disconnect link resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='github')) assert resp.status_code == 302 # User exists user = User.query.filter_by(email=example_email).one() assert 0 == UserIdentity.query.filter_by( method='orcid', id_user=user.id, id='githubuser' ).count() assert RemoteAccount.query.filter_by(user_id=user.id).count() == 0 assert RemoteToken.query.count() == 0 # User login with another email ('info2') with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email='*****@*****.**') # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ( 'http://localhost/' + 'account/settings/linkedaccounts/' ) # check that exist only one account user = User.query.filter_by(email=example_email).one() assert User.query.count() == 1
def send(request, **_): parsed = urlparse(request.url) tenant = parsed.path.split("/")[1] token = expected_token if tenant == expected_tenant else expected_token * 2 return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**"))
def test_account_setup(app_rest, example_cern, models_fixture): """Test account setup after login.""" with app_rest.test_client() as c: ioc = app_rest.extensions['oauthlib.client'] # Ensure remote apps have been loaded (due to before first request) resp = c.get( url_for('invenio_oauthclient.rest_login', remote_app='cern')) assert resp.status_code == 302 example_response, example_token, example_account_info = example_cern mock_response(app_rest.extensions['oauthlib.client'], 'cern', example_token) mock_remote_get(ioc, 'cern', example_response) resp = c.get( url_for('invenio_oauthclient.rest_authorized', remote_app='cern', code='test', state=get_state('cern'))) assert resp.status_code == 302 expected_url_args = { "message": "Successfully authorized.", "code": 200, } check_response_redirect_url_args(resp, expected_url_args) assert len(g.identity.provides) == 7 datastore = app_rest.extensions['invenio-accounts'].datastore user = datastore.find_user(email='*****@*****.**') user.password = hash_password("1234") assert user with app_rest.test_request_context(): resp = disconnect_rest_handler(ioc.remote_apps['cern']) assert resp.status_code >= 300 # simulate login (account_info fetch) g.oauth_logged_in_with_remote = ioc.remote_apps['cern'] login_user(user) assert isinstance(g.identity, Identity) assert g.identity.provides == set([ UserNeed(4), UserNeed('*****@*****.**'), RoleNeed('*****@*****.**'), RoleNeed('*****@*****.**'), RoleNeed('*****@*****.**'), RoleNeed('*****@*****.**'), RoleNeed('*****@*****.**'), ]) logout_user() assert isinstance(g.identity, AnonymousIdentity) # NOTE: Wrong role, g.identity.provides = {Need(['id', 4])} read more # https://github.com/inveniosoftware/invenio-access/blob/e28e76d5361a29202b94d498f1968454c24c5c80/tests/test_loaders.py#L47 assert len(g.identity.provides) == 1 assert "cern_resource" not in session assert OAUTHCLIENT_CERN_SESSION_KEY not in session # Login again to test the disconnect handler g.oauth_logged_in_with_remote = ioc.remote_apps['cern'] login_user(user) assert isinstance(g.identity, Identity) assert len(g.identity.provides) == 7 disconnect_rest_handler(ioc.remote_apps['cern'])
async def send(*_, **__): return mock_response(json_payload=build_aad_response( access_token="**"))
def test_authenticate(): client_id = "client-id" environment = "localhost" issuer = "https://" + environment tenant_id = "some-tenant" authority = issuer + "/" + tenant_id access_token = "***" scope = "scope" # mock AAD response with id token object_id = "object-id" home_tenant = "home-tenant-id" username = "******" id_token = build_id_token(aud=client_id, iss=issuer, object_id=object_id, tenant_id=home_tenant, username=username) auth_response = build_aad_response(uid=object_id, utid=home_tenant, access_token=access_token, refresh_token="**", id_token=id_token) transport = validating_transport( requests=[Request(url_substring=issuer)] * 3, responses=[get_discovery_response(authority)] * 2 + [mock_response(json_payload=auth_response)], ) # mock local server fakes successful authentication by immediately returning a well-formed response oauth_state = "state" auth_code_response = {"code": "authorization-code", "state": [oauth_state]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) with patch(InteractiveBrowserCredential.__module__ + ".uuid.uuid4", lambda: oauth_state): with patch(WEBBROWSER_OPEN, lambda _: True): credential = InteractiveBrowserCredential( _cache=TokenCache(), authority=environment, client_id=client_id, server_class=server_class, tenant_id=tenant_id, transport=transport, ) record = credential.authenticate(scopes=(scope, )) assert record.authority == environment assert record.home_account_id == object_id + "." + home_tenant assert record.tenant_id == home_tenant assert record.username == username # credential should have a cached access token for the scope used in authenticate with patch( WEBBROWSER_OPEN, Mock(side_effect=Exception( "credential should authenticate silently"))): token = credential.get_token(scope) assert token.token == access_token
async def test_same_username_different_tenants(): """two cached accounts, same username, different tenants""" access_token_a = "access-token-a" access_token_b = "access-token-b" refresh_token_a = "refresh-token-a" refresh_token_b = "refresh-token-b" upn = "spam@eggs" tenant_a = "tenant-a" tenant_b = "tenant-b" account_a = get_account_event(username=upn, uid="another-guid", utid=tenant_a, refresh_token=refresh_token_a) account_b = get_account_event(username=upn, uid="more-guid", utid=tenant_b, refresh_token=refresh_token_b) cache = populated_cache(account_a, account_b) # with no tenant specified the credential can't select an identity transport = Mock( side_effect=Exception()) # (so it shouldn't use the network) credential = SharedTokenCacheCredential(username=upn, _cache=cache, transport=transport) with pytest.raises(CredentialUnavailableError) as ex: await credential.get_token("scope") assert ex.value.message.startswith( MULTIPLE_MATCHING_ACCOUNTS[:MULTIPLE_MATCHING_ACCOUNTS.index("{")]) assert upn in ex.value.message # with tenant specified, the credential should auth the matching account scope = "scope" transport = async_validating_transport( requests=[ Request(required_data={ "refresh_token": refresh_token_a, "scope": scope }) ], responses=[ mock_response(json_payload=build_aad_response( access_token=access_token_a)) ], ) credential = SharedTokenCacheCredential(tenant_id=tenant_a, _cache=cache, transport=transport) token = await credential.get_token(scope) assert token.token == access_token_a transport = async_validating_transport( requests=[ Request(required_data={ "refresh_token": refresh_token_b, "scope": scope }) ], responses=[ mock_response(json_payload=build_aad_response( access_token=access_token_b)) ], ) credential = SharedTokenCacheCredential(tenant_id=tenant_b, _cache=cache, transport=transport) token = await credential.get_token(scope) assert token.token == access_token_b
def mock_send(request, **kwargs): validate_url(request.url) return mock_response(json_payload={"token_type": "Bearer", "expires_in": 42, "access_token": "***"})
def test_authorized_signup_valid_user(app, example_github): """Test authorized callback with sign-up.""" example_email = '*****@*****.**' with app.test_client() as c: # User login with email 'info' with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email='*****@*****.**') # Ensure remote apps have been loaded (due to before first # request) resp = c.get( url_for('invenio_oauthclient.login', remote_app='github')) assert resp.status_code == 302 mock_response(app.extensions['oauthlib.client'], 'github', example_github) # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ('http://localhost/account/settings/' + 'linkedaccounts/') # Assert database state (Sign-up complete) user = User.query.filter_by(email=example_email).one() remote = RemoteAccount.query.filter_by(user_id=user.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() assert user.active # Disconnect link resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='github')) assert resp.status_code == 302 # User exists user = User.query.filter_by(email=example_email).one() assert 0 == UserIdentity.query.filter_by(method='orcid', id_user=user.id, id='githubuser').count() assert RemoteAccount.query.filter_by(user_id=user.id).count() == 0 assert RemoteToken.query.count() == 0 # User login with another email ('info2') with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email='*****@*****.**') # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ('http://localhost/' + 'account/settings/linkedaccounts/') # check that exist only one account user = User.query.filter_by(email=example_email).one() assert User.query.count() == 1
def mock_send(request, **kwargs): token = expected_tokens[request.data["resource"]] return mock_response(json_payload=token)
async def test_cloud_shell_user_assigned_identity(): """Cloud Shell environment: only MSI_ENDPOINT set""" expected_token = "****" expires_on = 42 client_id = "some-guid" endpoint = "http://localhost:42/token" scope = "scope" param_name, param_value = "foo", "bar" transport = async_validating_transport( requests=[ Request( base_url=endpoint, method="POST", required_headers={ "Metadata": "true", "User-Agent": USER_AGENT }, required_data={ "client_id": client_id, "resource": scope }, ), Request( base_url=endpoint, method="POST", required_headers={ "Metadata": "true", "User-Agent": USER_AGENT }, required_data={ "resource": scope, param_name: param_value }, ), ], responses=[ mock_response( json_payload={ "access_token": expected_token, "expires_in": 0, "expires_on": expires_on, "not_before": int(time.time()), "resource": scope, "token_type": "Bearer", }) ] * 2, ) with mock.patch.dict(MANAGED_IDENTITY_ENVIRON, {EnvironmentVariables.MSI_ENDPOINT: endpoint}, clear=True): credential = ManagedIdentityCredential(client_id=client_id, transport=transport) token = await credential.get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on credential = ManagedIdentityCredential( transport=transport, identity_config={param_name: param_value}) token = await credential.get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on
def test_authorized_already_authenticated(app, models_fixture, example_orcid, orcid_bio): """Test authorized callback with sign-up.""" datastore = app.extensions['invenio-accounts'].datastore login_manager = app.login_manager example_data, example_account_info = example_orcid existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) @login_manager.user_loader def load_user(user_id): return user @app.route('/foo_login') def login(): login_user(user) return 'Logged In' with app.test_client() as client: # make a fake login (using my login function) client.get('/foo_login', follow_redirects=True) # Ensure remote apps have been loaded (due to before first # request) client.get(url_for('invenio_oauthclient.login', remote_app='orcid')) # Mock access token request mock_response(app.extensions['oauthlib.client'], 'orcid', example_data) # Mock request to ORCID to get user bio. httpretty.enable() httpretty.register_uri( httpretty.GET, 'https://pub.orcid.org/v1.2/{0}/orcid-bio'.format( example_data['orcid']), body=orcid_bio, content_type='application/orcid+json; qs=2;charset=UTF-8', ) # User then goes to 'Linked accounts' and clicks 'Connect' resp = client.get( url_for('invenio_oauthclient.login', remote_app='orcid', next='/someurl/')) assert resp.status_code == 302 # User authorized the requests and is redirected back resp = client.get( url_for('invenio_oauthclient.authorized', remote_app='orcid', code='test', state=get_state('orcid'))) httpretty.disable() # Assert database state (Sign-up complete) u = User.query.filter_by(email=existing_email).one() UserIdentity.query.filter_by(method='orcid', id_user=u.id, id=example_data['orcid']).one() # FIXME see contrib/orcid.py line 167 # assert u.given_names == 'Josiah' # assert u.family_name == 'Carberry' # Disconnect link resp = client.get( url_for('invenio_oauthclient.disconnect', remote_app='orcid')) assert resp.status_code == 302 # User exists u = User.query.filter_by(email=existing_email).one() # UserIdentity removed. assert 0 == UserIdentity.query.filter_by( method='orcid', id_user=u.id, id=example_data['orcid']).count()
async def test_app_service_2017_09_01(): """test parsing of App Service MSI 2017-09-01's eccentric platform-dependent expires_on strings""" access_token = "****" expires_on = 42 expected_token = AccessToken(access_token, expires_on) url = "http://localhost:42/token" secret = "expected-secret" scope = "scope" transport = async_validating_transport( requests=[ Request( url, method="GET", required_headers={ "secret": secret, "User-Agent": USER_AGENT }, required_params={ "api-version": "2017-09-01", "resource": scope }, ) ] * 2, responses=[ mock_response( json_payload={ "access_token": access_token, "expires_on": "01/01/1970 00:00:{} +00:00".format( expires_on), # linux format "resource": scope, "token_type": "Bearer", }), mock_response( json_payload={ "access_token": access_token, "expires_on": "1/1/1970 12:00:{} AM +00:00".format( expires_on), # windows format "resource": scope, "token_type": "Bearer", }), ], ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, { EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: secret }, clear=True, ): token = await ManagedIdentityCredential(transport=transport ).get_token(scope) assert token == expected_token assert token.expires_on == expires_on token = await ManagedIdentityCredential(transport=transport ).get_token(scope) assert token == expected_token assert token.expires_on == expires_on
def test_authorized_signup(app_with_userprofiles, example_orcid, orcid_bio): """Test authorized callback with sign-up.""" app = app_with_userprofiles example_data, example_account_info = example_orcid example_email = '*****@*****.**' with app.test_client() as c: # Ensure remote apps have been loaded (due to before first # request) c.get(url_for('invenio_oauthclient.login', remote_app='orcid')) mock_response(app.extensions['oauthlib.client'], 'orcid', example_data) # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='orcid', code='test', state=get_state('orcid'))) assert resp.status_code == 302 assert resp.location == ( 'http://localhost' + url_for('invenio_oauthclient.signup', remote_app='orcid')) # User load sign-up page. resp = c.get(url_for('invenio_oauthclient.signup', remote_app='orcid')) assert resp.status_code == 200 account_info = session[token_session_key('orcid') + '_account_info'] data = { 'email': example_email, 'password': '******', 'profile.username': '******', 'profile.full_name': account_info['user']['profile']['full_name'], } # Mock request to ORCID to get user bio. httpretty.enable() httpretty.register_uri( httpretty.GET, 'http://orcid.org/{0}/orcid-bio'.format(example_data['orcid']), body=orcid_bio, content_type='application/orcid+json; qs=2;charset=UTF-8', ) # User fills form to register resp = c.post( url_for('invenio_oauthclient.signup', remote_app='orcid'), data=data, ) assert resp.status_code == 302 httpretty.disable() # Assert database state (Sign-up complete) user = User.query.filter_by(email=example_email).one() UserIdentity.query.filter_by(method='orcid', id_user=user.id, id=example_data['orcid']).one() # FIXME see contrib/orcid.py line 167 assert user.profile.full_name == 'Josiah Carberry' # assert user.given_names == 'Josiah' # assert user.family_name == 'Carberry' # check that the user's email is not yet validated assert user.active # check that the validation email has been sent # assert hasattr(locmem, 'outbox') and len(locmem.outbox) == 1 # Disconnect link # should not work, because it's the user's only means of login resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='orcid')) assert resp.status_code == 400 user = User.query.filter_by(email=example_email).one() assert 1 == UserIdentity.query.filter_by( method='orcid', id_user=user.id, id=example_data['orcid']).count() # set a password for the user user.password = hash_password("1234") db.session.commit() # Disconnect again resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='orcid')) assert resp.status_code == 302 # User exists user = User.query.filter_by(email=example_email).one() # UserIdentity removed. assert 0 == UserIdentity.query.filter_by( method='orcid', id_user=user.id, id=example_data['orcid']).count() assert RemoteAccount.query.filter_by(user_id=user.id).count() == 0 assert RemoteToken.query.count() == 0
async def test_azure_ml(): """Azure ML: MSI_ENDPOINT, MSI_SECRET set (like App Service 2017-09-01 but with a different response format)""" expected_token = AccessToken("****", int(time.time()) + 3600) url = "http://localhost:42/token" secret = "expected-secret" scope = "scope" client_id = "client" transport = async_validating_transport( requests=[ Request( url, method="GET", required_headers={ "secret": secret, "User-Agent": USER_AGENT }, required_params={ "api-version": "2017-09-01", "resource": scope }, ), Request( url, method="GET", required_headers={ "secret": secret, "User-Agent": USER_AGENT }, required_params={ "api-version": "2017-09-01", "resource": scope, "clientid": client_id }, ), ], responses=[ mock_response( json_payload={ "access_token": expected_token.token, "expires_in": 3600, "expires_on": expected_token.expires_on, "resource": scope, "token_type": "Bearer", }) ] * 2, ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, { EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: secret }, clear=True, ): credential = ManagedIdentityCredential(transport=transport) token = await credential.get_token(scope) assert token.token == expected_token.token assert token.expires_on == expected_token.expires_on credential = ManagedIdentityCredential(transport=transport, client_id=client_id) token = await credential.get_token(scope) assert token.token == expected_token.token assert token.expires_on == expected_token.expires_on
def test_authorized_already_authenticated(app, models_fixture, example_globus): """Test authorized callback with sign-up.""" datastore = app.extensions['invenio-accounts'].datastore login_manager = app.login_manager existing_email = '*****@*****.**' user = datastore.find_user(email=existing_email) @login_manager.user_loader def load_user(user_id): return user @app.route('/foo_login') def login(): login_user(user) return 'Logged In' with app.test_client() as client: # make a fake login (using my login function) client.get('/foo_login', follow_redirects=True) # Ensure remote apps have been loaded (due to before first request) client.get(url_for('invenio_oauthclient.login', remote_app='globus')) ioc = app.extensions['oauthlib.client'] example_info, example_token, example_account_id = example_globus mock_response(app.extensions['oauthlib.client'], 'globus', example_token) example_info.update(example_account_id) oauth_resp = OAuthResponse(resp=None, content=json.dumps(example_info), content_type='application/json') mock_remote_get(ioc, 'globus', oauth_resp) # User then goes to 'Linked accounts' and clicks 'Connect' resp = client.get( url_for('invenio_oauthclient.login', remote_app='globus', next='/someurl/')) assert resp.status_code == 302 # User authorized the requests and is redirected back resp = client.get( url_for('invenio_oauthclient.authorized', remote_app='globus', code='test', state=_get_state())) # Assert database state (Sign-up complete) u = User.query.filter_by(email=existing_email).one() remote = RemoteAccount.query.filter_by(user_id=u.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() # Disconnect link resp = client.get( url_for('invenio_oauthclient.disconnect', remote_app='globus')) assert resp.status_code == 302 # User exists u = User.query.filter_by(email=existing_email).one() assert 0 == UserIdentity.query.filter_by(method='globus', id_user=u.id, id='globususer').count() assert RemoteAccount.query.filter_by(user_id=u.id).count() == 0 assert RemoteToken.query.count() == 0
def test_interactive_credential(mock_open): mock_open.side_effect = _validate_auth_request_url oauth_state = "state" client_id = "client-id" expected_refresh_token = "refresh-token" expected_token = "access-token" expires_in = 3600 authority = "authority" tenant_id = "tenant_id" endpoint = "https://{}/{}".format(authority, tenant_id) discovery_response = get_discovery_response(endpoint=endpoint) transport = validating_transport( requests=[Request(url_substring=endpoint)] * 3 + [ Request( authority=authority, url_substring=endpoint, required_data={"refresh_token": expected_refresh_token} ) ], responses=[ discovery_response, # instance discovery discovery_response, # tenant discovery mock_response( json_payload=build_aad_response( access_token=expected_token, expires_in=expires_in, refresh_token=expected_refresh_token, uid="uid", utid=tenant_id, id_token=build_id_token(aud=client_id, object_id="uid", tenant_id=tenant_id, iss=endpoint), token_type="Bearer", ) ), mock_response( json_payload=build_aad_response(access_token=expected_token, expires_in=expires_in, token_type="Bearer") ), ], ) # mock local server fakes successful authentication by immediately returning a well-formed response auth_code_response = {"code": "authorization-code", "state": [oauth_state]} server_class = Mock(return_value=Mock(wait_for_redirect=lambda: auth_code_response)) credential = InteractiveBrowserCredential( authority=authority, tenant_id=tenant_id, client_id=client_id, server_class=server_class, transport=transport, instance_discovery=False, validate_authority=False, _cache=TokenCache(), ) # The credential's auth code request includes a uuid which must be included in the redirect. Patching to # set the uuid requires less code here than a proper mock server. with patch("azure.identity._credentials.browser.uuid.uuid4", lambda: oauth_state): token = credential.get_token("scope") assert token.token == expected_token assert mock_open.call_count == 1 # token should be cached, get_token shouldn't prompt again token = credential.get_token("scope") assert token.token == expected_token assert mock_open.call_count == 1 # As of MSAL 1.0.0, applications build a new client every time they redeem a refresh token. # Here we patch the private method they use for the sake of test coverage. # TODO: this will probably break when this MSAL behavior changes app = credential._get_app() app._build_client = lambda *_: app.client # pylint:disable=protected-access now = time.time() # expired access token -> credential should use refresh token instead of prompting again with patch("time.time", lambda: now + expires_in): token = credential.get_token("scope") assert token.token == expected_token assert mock_open.call_count == 1 # ensure all expected requests were sent assert transport.send.call_count == 4
def test_authorized_signup_valid_user(app, example_globus): """Test authorized callback with sign-up.""" with app.test_client() as c: # User login with email 'info' ioc = app.extensions['oauthlib.client'] # Ensure remote apps have been loaded (due to before first request) resp = c.get(url_for('invenio_oauthclient.login', remote_app='globus')) assert resp.status_code == 302 example_info, example_token, example_account_id = example_globus mock_response(app.extensions['oauthlib.client'], 'globus', example_token) example_info.update(example_account_id) oauth_resp = OAuthResponse(resp=None, content=json.dumps(example_info), content_type='application/json') mock_remote_get(ioc, 'globus', oauth_resp) # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='globus', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ('http://localhost/account/settings/' + 'linkedaccounts/') # Assert database state (Sign-up complete) user = User.query.filter_by(email='*****@*****.**').one() remote = RemoteAccount.query.filter_by(user_id=user.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() assert user.active # Disconnect link # should not work, because it's the user's only means of login resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='globus')) assert resp.status_code == 400 user = User.query.filter_by(email='*****@*****.**').one() assert 1 == UserIdentity.query.filter_by( method='globus', id_user=user.id, ).count() # set a password for the user user.password = hash_password("1234") db.session.commit() # Disconnect again resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='globus')) assert resp.status_code == 302 # User exists user = User.query.filter_by(email='*****@*****.**').one() assert 0 == UserIdentity.query.filter_by( method='globus', id_user=user.id, ).count() assert RemoteAccount.query.filter_by(user_id=user.id).count() == 0 assert RemoteToken.query.count() == 0 # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='globus', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ('http://localhost/' + 'account/settings/linkedaccounts/') # check that exist only one account user = User.query.filter_by(email='*****@*****.**').one() assert User.query.count() == 1
def send(request, **_): parsed = urlparse(request.url) tenant = parsed.path.split("/")[1] assert tenant in (first_tenant, second_tenant), 'unexpected tenant "{}"'.format(tenant) token = first_token if tenant == first_tenant else second_token return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**"))
async def test_default_credential_authority(): authority = "authority.com" expected_access_token = "***" response = mock_response( json_payload={ "access_token": expected_access_token, "expires_in": 0, "expires_on": 42, "not_before": 0, "resource": "scope", "token_type": "Bearer", }) async def exercise_credentials(authority_kwarg, expected_authority=None): expected_authority = expected_authority or authority_kwarg async def send(request, **_): url = urlparse(request.url) assert url.scheme == "https", "Unexpected scheme '{}'".format( url.scheme) assert url.netloc == expected_authority, "Expected authority '{}', actual was '{}'".format( expected_authority, url.netloc) return response # environment credential configured with client secret should respect authority environment = { EnvironmentVariables.AZURE_CLIENT_ID: "client_id", EnvironmentVariables.AZURE_CLIENT_SECRET: "secret", EnvironmentVariables.AZURE_TENANT_ID: "tenant_id", } with patch("os.environ", environment): transport = Mock(send=send) if authority_kwarg: credential = DefaultAzureCredential(authority=authority_kwarg, transport=transport) else: credential = DefaultAzureCredential(transport=transport) access_token, _ = await credential.get_token("scope") assert access_token == expected_access_token # managed identity credential should ignore authority with patch("os.environ", {EnvironmentVariables.MSI_ENDPOINT: "https://some.url"}): transport = Mock(send=asyncio.coroutine(lambda *_, **__: response)) if authority_kwarg: credential = DefaultAzureCredential(authority=authority_kwarg, transport=transport) else: credential = DefaultAzureCredential(transport=transport) access_token, _ = await credential.get_token("scope") assert access_token == expected_access_token # shared cache credential should respect authority upn = os.environ.get(EnvironmentVariables.AZURE_USERNAME, "spam@eggs") # preferring environment values to tenant = os.environ.get(EnvironmentVariables.AZURE_TENANT_ID, "tenant") # prevent failure during live runs account = get_account_event(username=upn, uid="guid", utid=tenant, authority=authority_kwarg) cache = populated_cache(account) with patch.object(SharedTokenCacheCredential, "supported"): credential = DefaultAzureCredential(_cache=cache, authority=authority_kwarg, transport=Mock(send=send)) access_token, _ = await credential.get_token("scope") assert access_token == expected_access_token # all credentials not representing managed identities should use a specified authority or default to public cloud await exercise_credentials("authority.com") await exercise_credentials(None, KnownAuthorities.AZURE_PUBLIC_CLOUD)
def test_auth_code_credential(): client_id = "client id" tenant_id = "tenant" expected_code = "auth code" redirect_uri = "https://localhost" expected_access_token = "access" expected_refresh_token = "refresh" expected_scope = "scope" auth_response = build_aad_response(access_token=expected_access_token, refresh_token=expected_refresh_token) transport = validating_transport( requests=[ Request( # first call should redeem the auth code url_substring=tenant_id, required_data={ "client_id": client_id, "code": expected_code, "grant_type": "authorization_code", "redirect_uri": redirect_uri, "scope": expected_scope, }, ), Request( # third call should redeem the refresh token url_substring=tenant_id, required_data={ "client_id": client_id, "grant_type": "refresh_token", "refresh_token": expected_refresh_token, "scope": expected_scope, }, ), ], responses=[mock_response(json_payload=auth_response)] * 2, ) cache = msal.TokenCache() credential = AuthorizationCodeCredential( client_id=client_id, tenant_id=tenant_id, authorization_code=expected_code, redirect_uri=redirect_uri, transport=transport, cache=cache, ) # first call should redeem the auth code token = credential.get_token(expected_scope) assert token.token == expected_access_token assert transport.send.call_count == 1 # no auth code -> credential should return cached token token = credential.get_token(expected_scope) assert token.token == expected_access_token assert transport.send.call_count == 1 # no auth code, no cached token -> credential should redeem refresh token cached_access_token = cache.find(cache.CredentialType.ACCESS_TOKEN)[0] cache.remove_at(cached_access_token) token = credential.get_token(expected_scope) assert token.token == expected_access_token assert transport.send.call_count == 2
async def send(request, **_): assert request.data["refresh_token"] == invalid_token return mock_response(json_payload={"error": "invalid_grant"}, status_code=400)
async def test_same_tenant_different_usernames(): """two cached accounts, same tenant, different usernames""" access_token_a = "access-token-a" access_token_b = "access-token-b" refresh_token_a = "refresh-token-a" refresh_token_b = "refresh-token-b" upn_a = "spam@eggs" upn_b = "eggs@spam" tenant_id = "the-tenant" account_a = get_account_event(username=upn_a, uid="another-guid", utid=tenant_id, refresh_token=refresh_token_a) account_b = get_account_event(username=upn_b, uid="more-guid", utid=tenant_id, refresh_token=refresh_token_b) cache = populated_cache(account_a, account_b) # with no username specified the credential can't select an identity transport = Mock( side_effect=Exception()) # (so it shouldn't use the network) credential = SharedTokenCacheCredential(tenant_id=tenant_id, _cache=cache, transport=transport) with pytest.raises(ClientAuthenticationError) as ex: await credential.get_token("scope") # error message should indicate multiple matching accounts, and list discovered accounts assert ex.value.message.startswith( MULTIPLE_MATCHING_ACCOUNTS[:MULTIPLE_MATCHING_ACCOUNTS.index("{")]) discovered_accounts = ex.value.message.splitlines()[-1] assert discovered_accounts.count(tenant_id) == 2 assert upn_a in discovered_accounts and upn_b in discovered_accounts # with a username specified, the credential should auth the matching account scope = "scope" transport = async_validating_transport( requests=[ Request(required_data={ "refresh_token": refresh_token_b, "scope": scope }) ], responses=[ mock_response(json_payload=build_aad_response( access_token=access_token_a)) ], ) credential = SharedTokenCacheCredential(username=upn_b, _cache=cache, transport=transport) token = await credential.get_token(scope) assert token.token == access_token_a transport = async_validating_transport( requests=[ Request(required_data={ "refresh_token": refresh_token_a, "scope": scope }) ], responses=[ mock_response(json_payload=build_aad_response( access_token=access_token_a)) ], ) credential = SharedTokenCacheCredential(username=upn_a, _cache=cache, transport=transport) token = await credential.get_token(scope) assert token.token == access_token_a
def test_authorized_signup_username_already_exists(app, example_github, user): """Test authorized callback with sign-up.""" example_email = '*****@*****.**' with app.test_client() as c: # User login with email 'info' with mock.patch('github3.login') as MockLogin: MockLogin.return_value = MockGh(email=example_email) # Ensure remote apps have been loaded (due to before first # request) resp = c.get(url_for('invenio_oauthclient.login', remote_app='github')) assert resp.status_code == 302 mock_response(app.extensions['oauthlib.client'], 'github', example_github) # User authorized the requests and is redirect back resp = c.get( url_for('invenio_oauthclient.authorized', remote_app='github', code='test', state=_get_state())) assert resp.status_code == 302 assert resp.location == ( 'http://localhost' + url_for('invenio_oauthclient.signup', remote_app='github') ) # User fills form to register resp = c.post( resp.headers['Location'], data={ 'email': example_email, 'password': '******', 'profile.username': '******', 'profile.full_name': 'pluto', } ) assert resp.status_code == 302 # Assert database state (Sign-up complete) my_user = User.query.filter_by(email=example_email).one() remote = RemoteAccount.query.filter_by(user_id=my_user.id).one() RemoteToken.query.filter_by(id_remote_account=remote.id).one() assert my_user.active # Disconnect link resp = c.get( url_for('invenio_oauthclient.disconnect', remote_app='github')) assert resp.status_code == 302 # User exists my_user = User.query.filter_by(email=example_email).one() assert 0 == UserIdentity.query.filter_by( method='orcid', id_user=my_user.id, id='githubuser' ).count() assert RemoteAccount.query.filter_by( user_id=my_user.id).count() == 0 assert RemoteToken.query.count() == 0 assert User.query.count() == 2