from atlas_um import consts from atlas_um.helpers.tokens import get_uid_str from atlas_um.settings import Settings from atlas_um.tokens.services import ( CreateBearerTokenService, CreateRefreshTokenService, RotateTokensService, ) from atlas_um.pgdb import DNAAccountActivity, Product from tests.atlas_um import factories class TestCreateBearerTokenService: def test(self, pgdb_session, mocker, faker): fake_token = faker.pystr() mocked_token_class = mocker.patch( "atlas_um.tokens.services.DNABearerToken" ) mocked_token_class.return_value.encode.return_value = fake_token resource_group = factories.ResourceGroupFactory.build() dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.add(resource_group) pgdb_session.commit() res = CreateBearerTokenService.execute(dna_account, resource_group) assert res.is_right assert mocked_token_class.call_args_list == [ mocker.call(dna_account, resource_group, None) ] assert res.value == fake_token class TestRefreshTokenService: def test_without_resource_group_success(self, pgdb_session, redis_session): dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.commit() res = CreateRefreshTokenService.execute(dna_account) assert res.is_right token = res.value session_key = f"{consts.DNA_REFRESH_TOKEN_SESSION_KEY_PREFIX}:{token}" family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" assert ( redis_session.ttl(session_key) == Settings.DEFAULT_REFRESH_TOKEN_TTL ) assert ( redis_session.ttl(family_key) == Settings.DEFAULT_REFRESH_TOKEN_TTL ) assert ( redis_session.ttl(token_key) == Settings.DEFAULT_REFRESH_TOKEN_TTL ) assert ( redis_session.get(session_key) == consts.DNA_REFRESH_TOKEN_SESSION_EMPTY_VALUE ) assert redis_session.get(family_key) == token assert redis_session.get(token_key) == str(dna_account.id) def test_with_resource_group_success( self, pgdb_session, redis_session, faker ): expected_ttl = faker.pyint(min_value=1) expected_active_ttl = faker.pyint(min_value=1) resource_group = factories.ResourceGroupFactory.build( session_ttl=expected_ttl, active_session_ttl=expected_active_ttl ) dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.add(resource_group) pgdb_session.commit() res = CreateRefreshTokenService.execute(dna_account, resource_group) assert res.is_right token = res.value session_key = f"{consts.DNA_REFRESH_TOKEN_SESSION_KEY_PREFIX}:{token}" family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" assert redis_session.ttl(session_key) == expected_ttl assert redis_session.ttl(family_key) == expected_active_ttl assert redis_session.ttl(token_key) == expected_active_ttl assert redis_session.get(session_key) == str(resource_group.id) assert redis_session.get(family_key) == token assert redis_session.get(token_key) == str(dna_account.id) class TestRotateTokensService: def test_success_no_resource_group( self, pgdb_session, redis_session, mocker, faker ): dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.commit() test_dna_bearer_token = faker.pystr() mocked_token_init = mocker.patch( "atlas_um.tokens.bearer_tokens.DNABearerToken.__init__" ) mocked_token_init.return_value = None mocker.patch( "atlas_um.tokens.bearer_tokens.DNABearerToken.encode" ).return_value = test_dna_bearer_token token = get_uid_str() session_key = f"{consts.DNA_REFRESH_TOKEN_SESSION_KEY_PREFIX}:{token}" family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" redis_session.setex( session_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, consts.DNA_REFRESH_TOKEN_SESSION_EMPTY_VALUE, ) redis_session.setex( family_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, token ) redis_session.setex( token_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) res = RotateTokensService.execute(token) assert res.is_right assert redis_session.get(token_key) is None new_token, access_token, _ = res.value new_family_key = ( f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{new_token}" ) new_token_key = ( f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{new_token}" ) assert redis_session.get(new_family_key) == token assert redis_session.get(new_token_key) == str(dna_account.id) assert mocked_token_init.call_args_list == [ mocker.call(dna_account, None) ] assert access_token == test_dna_bearer_token def test_success_with_resource_group( self, pgdb_session, redis_session, mocker, faker ): resource_group = factories.ResourceGroupFactory.build( session_ttl=faker.pyint(min_value=1), active_session_ttl=faker.pyint(min_value=1), ) dna_account = factories.DNAAccountFactory.build() pgdb_session.add(resource_group) pgdb_session.add(dna_account) pgdb_session.commit() test_dna_bearer_token = faker.pystr() mocked_token_init = mocker.patch( "atlas_um.tokens.bearer_tokens.DNABearerToken.__init__" ) mocked_token_init.return_value = None mocker.patch( "atlas_um.tokens.bearer_tokens.DNABearerToken.encode" ).return_value = test_dna_bearer_token token = get_uid_str() session_key = f"{consts.DNA_REFRESH_TOKEN_SESSION_KEY_PREFIX}:{token}" family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" redis_session.setex( session_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, resource_group.id ) redis_session.setex( family_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, token ) redis_session.setex( token_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) res = RotateTokensService.execute(token) assert res.is_right assert redis_session.get(token_key) is None new_token, access_token, _ = res.value new_family_key = ( f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{new_token}" ) new_token_key = ( f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{new_token}" ) assert redis_session.get(new_family_key) == token assert redis_session.get(new_token_key) == str(dna_account.id) assert mocked_token_init.call_args_list == [ mocker.call(dna_account, resource_group) ] assert access_token == test_dna_bearer_token def test_update_last_activity( self, pgdb_session, redis_session, mocker, faker ): resource_group = factories.ResourceGroupFactory.build( session_ttl=faker.pyint(min_value=1), active_session_ttl=faker.pyint(min_value=1), ) dna_account = factories.DNAAccountFactory.build() pgdb_session.add(resource_group) pgdb_session.add(dna_account) pgdb_session.commit() test_dna_bearer_token = faker.pystr() mocker.patch( "atlas_um.tokens.bearer_tokens.DNABearerToken.encode" ).return_value = test_dna_bearer_token token = get_uid_str() session_key = f"{consts.DNA_REFRESH_TOKEN_SESSION_KEY_PREFIX}:{token}" family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" redis_session.setex( session_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, resource_group.id ) redis_session.setex( family_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, token ) redis_session.setex( token_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) account_activity = DNAAccountActivity.register_for_resource_group( dna_account, resource_group, ) last_activity = account_activity.last_activity # rotation token must update last_activity of account res = RotateTokensService.execute(token) product = Product(resource_group=resource_group, account=dna_account) assert res.is_right assert last_activity assert product.last_activity > last_activity def test_failure_no_session(self, pgdb_session, redis_session): dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.commit() token = get_uid_str() family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" redis_session.setex( family_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, token ) redis_session.setex( token_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) wrong_token = get_uid_str() res = RotateTokensService.execute(wrong_token) assert res.is_left def test_failure_invalid_session_value(self, pgdb_session, redis_session): dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.commit() token = get_uid_str() session_key = f"{consts.DNA_REFRESH_TOKEN_SESSION_KEY_PREFIX}:{token}" family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" redis_session.setex( session_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, "invalid" ) redis_session.setex( family_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, token ) redis_session.setex( token_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) wrong_token = get_uid_str() res = RotateTokensService.execute(wrong_token) assert res.is_left def test_failure_no_family(self, pgdb_session, redis_session): dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.commit() token = get_uid_str() family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" redis_session.setex( family_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, token ) redis_session.setex( token_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) wrong_token = get_uid_str() res = RotateTokensService.execute(wrong_token) assert res.is_left def test_failure_no_token(self, pgdb_session, redis_session): dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.commit() token1 = get_uid_str() family_key1 = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token1}" token_key1 = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token1}:{token1}" redis_session.setex( family_key1, Settings.DEFAULT_REFRESH_TOKEN_TTL, token1 ) redis_session.set( token_key1, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) token2 = get_uid_str() family_key2 = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token2}" token_key2 = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token1}:{token2}" redis_session.setex( family_key2, Settings.DEFAULT_REFRESH_TOKEN_TTL, token1 ) redis_session.rename(token_key1, token_key2) res = RotateTokensService.execute(token1) assert res.is_left assert redis_session.get(token_key1) is None assert redis_session.get(family_key1) == token1 def test_update_last_activity_for_subdomain_services( self, pgdb_session, mocker, faker, redis_session, ): resource_group_hint_name = faker.pystr() resource_group_hint = factories.ResourceGroupFactory.build( namespace_url=resource_group_hint_name, session_ttl=faker.pyint(min_value=1), active_session_ttl=faker.pyint(min_value=1), ) dna_account = factories.DNAAccountFactory.build() pgdb_session.add(resource_group_hint) pgdb_session.add(dna_account) pgdb_session.commit() test_dna_bearer_token = faker.pystr() mocker.patch( "atlas_um.tokens.bearer_tokens.DNABearerToken.encode" ).return_value = test_dna_bearer_token token = get_uid_str() session_key = f"{consts.DNA_REFRESH_TOKEN_SESSION_KEY_PREFIX}:{token}" family_key = f"{consts.DNA_REFRESH_TOKEN_FAMILY_KEY_PREFIX}:{token}" token_key = f"{consts.DNA_REFRESH_TOKEN_KEY_PREFIX}:{token}:{token}" redis_session.setex( session_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, "no_resource_group", ) redis_session.setex( family_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, token ) redis_session.setex( token_key, Settings.DEFAULT_REFRESH_TOKEN_TTL, dna_account.id ) account_activity = DNAAccountActivity.register_for_resource_group( dna_account, resource_group_hint, ) last_activity = account_activity.last_activity # rotation token must update last_activity of hint resource group res = RotateTokensService.execute(token, resource_group_hint_name) product = Product( resource_group=resource_group_hint, account=dna_account, ) assert res.is_right assert last_activity assert product.last_activity > last_activity def test_refresh_chain_no_resource_group(pgdb_session): dna_account = factories.DNAAccountFactory.build() pgdb_session.add(dna_account) pgdb_session.commit() token = CreateRefreshTokenService.execute(dna_account).value res = RotateTokensService.execute(token) assert res.is_right res = RotateTokensService.execute(res.value[0]) assert res.is_right stollen_token = res.value[0] res = RotateTokensService.execute(res.value[0]) assert res.is_right res = RotateTokensService.execute(stollen_token) assert res.is_left def test_refresh_chain_with_resource_group(pgdb_session): resource_group = factories.ResourceGroupFactory() dna_account = factories.DNAAccountFactory.build() pgdb_session.add(resource_group) pgdb_session.add(dna_account) pgdb_session.commit() token = CreateRefreshTokenService.execute( dna_account, resource_group ).value res = RotateTokensService.execute(token) assert res.is_right res = RotateTokensService.execute(res.value[0]) assert res.is_right stollen_token = res.value[0] res = RotateTokensService.execute(res.value[0]) assert res.is_right res = RotateTokensService.execute(stollen_token) assert res.is_left