Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Tests for emulator support in auth, user mgmt and token gen
To minimize modification of tests, the app fixture and instrumentation
have been modified to use a global dict of URLs, which are then
monkey-patched based on fixture parameters. Essentially, all tests using
the app fixture are run twice, once with the emulated endpoint and once
without.
  • Loading branch information
muru committed Mar 20, 2021
commit 69b8784f71e0ecd968e5a34b374d033e568e72bb
79 changes: 48 additions & 31 deletions tests/test_auth_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@
import firebase_admin
from firebase_admin import auth
from firebase_admin import exceptions
from firebase_admin import _auth_providers
from tests import testutils

USER_MGT_URL_PREFIX = 'https://identitytoolkit.googleapis.com/v2beta1/projects/mock-project-id'
ID_TOOLKIT_URL = 'https://identitytoolkit.googleapis.com/v2beta1'
EMULATOR_HOST_ENV_VAR = 'FIREBASE_AUTH_EMULATOR_HOST'
AUTH_EMULATOR_HOST = 'localhost:9099'
EMULATED_ID_TOOLKIT_URL = 'http://{}/identitytoolkit.googleapis.com/v2beta1'.format(
AUTH_EMULATOR_HOST)
URL_PROJECT_SUFFIX = '/projects/mock-project-id'
USER_MGT_URLS = {
'ID_TOOLKIT': ID_TOOLKIT_URL,
'PREFIX': ID_TOOLKIT_URL + URL_PROJECT_SUFFIX,
}
OIDC_PROVIDER_CONFIG_RESPONSE = testutils.resource('oidc_provider_config.json')
SAML_PROVIDER_CONFIG_RESPONSE = testutils.resource('saml_provider_config.json')
LIST_OIDC_PROVIDER_CONFIGS_RESPONSE = testutils.resource('list_oidc_provider_configs.json')
Expand All @@ -39,20 +47,26 @@
INVALID_PROVIDER_IDS = [None, True, False, 1, 0, list(), tuple(), dict(), '']


@pytest.fixture(scope='module')
def user_mgt_app():
@pytest.fixture(scope='module', params=[{'emulated': False}, {'emulated': True}])
def user_mgt_app(request):
monkeypatch = pytest.MonkeyPatch()
if request.param['emulated']:
monkeypatch.setenv(EMULATOR_HOST_ENV_VAR, AUTH_EMULATOR_HOST)
monkeypatch.setitem(USER_MGT_URLS, 'ID_TOOLKIT', EMULATED_ID_TOOLKIT_URL)
monkeypatch.setitem(USER_MGT_URLS, 'PREFIX', EMULATED_ID_TOOLKIT_URL + URL_PROJECT_SUFFIX)
app = firebase_admin.initialize_app(testutils.MockCredential(), name='providerConfig',
options={'projectId': 'mock-project-id'})
yield app
firebase_admin.delete_app(app)
monkeypatch.undo()


def _instrument_provider_mgt(app, status, payload):
client = auth._get_client(app)
provider_manager = client._provider_manager
recorder = []
provider_manager.http_client.session.mount(
_auth_providers.ProviderConfigClient.PROVIDER_CONFIG_URL,
USER_MGT_URLS['ID_TOOLKIT'],
testutils.MockAdapter(payload, status, recorder))
return recorder

Expand Down Expand Up @@ -90,7 +104,7 @@ def test_get(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}{1}'.format(USER_MGT_URL_PREFIX, '/oauthIdpConfigs/oidc.provider')
assert req.url == '{0}{1}'.format(USER_MGT_URLS['PREFIX'], '/oauthIdpConfigs/oidc.provider')

@pytest.mark.parametrize('invalid_opts', [
{'provider_id': None}, {'provider_id': ''}, {'provider_id': 'saml.provider'},
Expand All @@ -116,7 +130,7 @@ def test_create(self, user_mgt_app):
req = recorder[0]
assert req.method == 'POST'
assert req.url == '{0}/oauthIdpConfigs?oauthIdpConfigId=oidc.provider'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == self.OIDC_CONFIG_REQUEST

Expand All @@ -136,7 +150,7 @@ def test_create_minimal(self, user_mgt_app):
req = recorder[0]
assert req.method == 'POST'
assert req.url == '{0}/oauthIdpConfigs?oauthIdpConfigId=oidc.provider'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == want

Expand All @@ -156,7 +170,7 @@ def test_create_empty_values(self, user_mgt_app):
req = recorder[0]
assert req.method == 'POST'
assert req.url == '{0}/oauthIdpConfigs?oauthIdpConfigId=oidc.provider'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == want

Expand Down Expand Up @@ -186,7 +200,7 @@ def test_update(self, user_mgt_app):
assert req.method == 'PATCH'
mask = ['clientId', 'displayName', 'enabled', 'issuer']
assert req.url == '{0}/oauthIdpConfigs/oidc.provider?updateMask={1}'.format(
USER_MGT_URL_PREFIX, ','.join(mask))
USER_MGT_URLS['PREFIX'], ','.join(mask))
got = json.loads(req.body.decode())
assert got == self.OIDC_CONFIG_REQUEST

Expand All @@ -201,7 +215,7 @@ def test_update_minimal(self, user_mgt_app):
req = recorder[0]
assert req.method == 'PATCH'
assert req.url == '{0}/oauthIdpConfigs/oidc.provider?updateMask=displayName'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == {'displayName': 'oidcProviderName'}

Expand All @@ -217,7 +231,7 @@ def test_update_empty_values(self, user_mgt_app):
assert req.method == 'PATCH'
mask = ['displayName', 'enabled']
assert req.url == '{0}/oauthIdpConfigs/oidc.provider?updateMask={1}'.format(
USER_MGT_URL_PREFIX, ','.join(mask))
USER_MGT_URLS['PREFIX'], ','.join(mask))
got = json.loads(req.body.decode())
assert got == {'displayName': None, 'enabled': False}

Expand All @@ -236,7 +250,7 @@ def test_delete(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'DELETE'
assert req.url == '{0}{1}'.format(USER_MGT_URL_PREFIX, '/oauthIdpConfigs/oidc.provider')
assert req.url == '{0}{1}'.format(USER_MGT_URLS['PREFIX'], '/oauthIdpConfigs/oidc.provider')

@pytest.mark.parametrize('arg', [None, 'foo', list(), dict(), 0, -1, 101, False])
def test_invalid_max_results(self, user_mgt_app, arg):
Expand All @@ -259,7 +273,7 @@ def test_list_single_page(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}{1}'.format(USER_MGT_URL_PREFIX, '/oauthIdpConfigs?pageSize=100')
assert req.url == '{0}{1}'.format(USER_MGT_URLS['PREFIX'], '/oauthIdpConfigs?pageSize=100')

def test_list_multiple_pages(self, user_mgt_app):
sample_response = json.loads(OIDC_PROVIDER_CONFIG_RESPONSE)
Expand All @@ -277,7 +291,7 @@ def test_list_multiple_pages(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/oauthIdpConfigs?pageSize=10'.format(USER_MGT_URL_PREFIX)
assert req.url == '{0}/oauthIdpConfigs?pageSize=10'.format(USER_MGT_URLS['PREFIX'])

# Page 2 (also the last page)
response = {'oauthIdpConfigs': configs[2:]}
Expand All @@ -289,7 +303,7 @@ def test_list_multiple_pages(self, user_mgt_app):
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/oauthIdpConfigs?pageSize=10&pageToken=token'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])

def test_paged_iteration(self, user_mgt_app):
sample_response = json.loads(OIDC_PROVIDER_CONFIG_RESPONSE)
Expand All @@ -310,7 +324,7 @@ def test_paged_iteration(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/oauthIdpConfigs?pageSize=100'.format(USER_MGT_URL_PREFIX)
assert req.url == '{0}/oauthIdpConfigs?pageSize=100'.format(USER_MGT_URLS['PREFIX'])

# Page 2 (also the last page)
response = {'oauthIdpConfigs': configs[2:]}
Expand All @@ -322,7 +336,7 @@ def test_paged_iteration(self, user_mgt_app):
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/oauthIdpConfigs?pageSize=100&pageToken=token'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])

with pytest.raises(StopIteration):
next(iterator)
Expand Down Expand Up @@ -421,7 +435,8 @@ def test_get(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}{1}'.format(USER_MGT_URL_PREFIX, '/inboundSamlConfigs/saml.provider')
assert req.url == '{0}{1}'.format(USER_MGT_URLS['PREFIX'],
'/inboundSamlConfigs/saml.provider')

@pytest.mark.parametrize('invalid_opts', [
{'provider_id': None}, {'provider_id': ''}, {'provider_id': 'oidc.provider'},
Expand Down Expand Up @@ -451,7 +466,7 @@ def test_create(self, user_mgt_app):
req = recorder[0]
assert req.method == 'POST'
assert req.url == '{0}/inboundSamlConfigs?inboundSamlConfigId=saml.provider'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == self.SAML_CONFIG_REQUEST

Expand All @@ -471,7 +486,7 @@ def test_create_minimal(self, user_mgt_app):
req = recorder[0]
assert req.method == 'POST'
assert req.url == '{0}/inboundSamlConfigs?inboundSamlConfigId=saml.provider'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == want

Expand All @@ -491,7 +506,7 @@ def test_create_empty_values(self, user_mgt_app):
req = recorder[0]
assert req.method == 'POST'
assert req.url == '{0}/inboundSamlConfigs?inboundSamlConfigId=saml.provider'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == want

Expand Down Expand Up @@ -528,7 +543,7 @@ def test_update(self, user_mgt_app):
'idpConfig.ssoUrl', 'spConfig.callbackUri', 'spConfig.spEntityId',
]
assert req.url == '{0}/inboundSamlConfigs/saml.provider?updateMask={1}'.format(
USER_MGT_URL_PREFIX, ','.join(mask))
USER_MGT_URLS['PREFIX'], ','.join(mask))
got = json.loads(req.body.decode())
assert got == self.SAML_CONFIG_REQUEST

Expand All @@ -543,7 +558,7 @@ def test_update_minimal(self, user_mgt_app):
req = recorder[0]
assert req.method == 'PATCH'
assert req.url == '{0}/inboundSamlConfigs/saml.provider?updateMask=displayName'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])
got = json.loads(req.body.decode())
assert got == {'displayName': 'samlProviderName'}

Expand All @@ -559,7 +574,7 @@ def test_update_empty_values(self, user_mgt_app):
assert req.method == 'PATCH'
mask = ['displayName', 'enabled']
assert req.url == '{0}/inboundSamlConfigs/saml.provider?updateMask={1}'.format(
USER_MGT_URL_PREFIX, ','.join(mask))
USER_MGT_URLS['PREFIX'], ','.join(mask))
got = json.loads(req.body.decode())
assert got == {'displayName': None, 'enabled': False}

Expand All @@ -578,7 +593,8 @@ def test_delete(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'DELETE'
assert req.url == '{0}{1}'.format(USER_MGT_URL_PREFIX, '/inboundSamlConfigs/saml.provider')
assert req.url == '{0}{1}'.format(USER_MGT_URLS['PREFIX'],
'/inboundSamlConfigs/saml.provider')

def test_config_not_found(self, user_mgt_app):
_instrument_provider_mgt(user_mgt_app, 500, CONFIG_NOT_FOUND_RESPONSE)
Expand Down Expand Up @@ -613,7 +629,8 @@ def test_list_single_page(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}{1}'.format(USER_MGT_URL_PREFIX, '/inboundSamlConfigs?pageSize=100')
assert req.url == '{0}{1}'.format(USER_MGT_URLS['PREFIX'],
'/inboundSamlConfigs?pageSize=100')

def test_list_multiple_pages(self, user_mgt_app):
sample_response = json.loads(SAML_PROVIDER_CONFIG_RESPONSE)
Expand All @@ -631,7 +648,7 @@ def test_list_multiple_pages(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/inboundSamlConfigs?pageSize=10'.format(USER_MGT_URL_PREFIX)
assert req.url == '{0}/inboundSamlConfigs?pageSize=10'.format(USER_MGT_URLS['PREFIX'])

# Page 2 (also the last page)
response = {'inboundSamlConfigs': configs[2:]}
Expand All @@ -643,7 +660,7 @@ def test_list_multiple_pages(self, user_mgt_app):
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/inboundSamlConfigs?pageSize=10&pageToken=token'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])

def test_paged_iteration(self, user_mgt_app):
sample_response = json.loads(SAML_PROVIDER_CONFIG_RESPONSE)
Expand All @@ -664,7 +681,7 @@ def test_paged_iteration(self, user_mgt_app):
assert len(recorder) == 1
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/inboundSamlConfigs?pageSize=100'.format(USER_MGT_URL_PREFIX)
assert req.url == '{0}/inboundSamlConfigs?pageSize=100'.format(USER_MGT_URLS['PREFIX'])

# Page 2 (also the last page)
response = {'inboundSamlConfigs': configs[2:]}
Expand All @@ -676,7 +693,7 @@ def test_paged_iteration(self, user_mgt_app):
req = recorder[0]
assert req.method == 'GET'
assert req.url == '{0}/inboundSamlConfigs?pageSize=100&pageToken=token'.format(
USER_MGT_URL_PREFIX)
USER_MGT_URLS['PREFIX'])

with pytest.raises(StopIteration):
next(iterator)
Expand Down
30 changes: 24 additions & 6 deletions tests/test_token_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@
'NonEmptyDictToken': {'a': 1},
}

ID_TOOLKIT_URL = 'https://identitytoolkit.googleapis.com/v1'
EMULATOR_HOST_ENV_VAR = 'FIREBASE_AUTH_EMULATOR_HOST'
AUTH_EMULATOR_HOST = 'localhost:9099'
EMULATED_ID_TOOLKIT_URL = 'http://{}/identitytoolkit.googleapis.com/v1'.format(AUTH_EMULATOR_HOST)
TOKEN_MGT_URLS = {
'ID_TOOLKIT': ID_TOOLKIT_URL,
}

# Fixture for mocking a HTTP server
httpserver = plugin.httpserver

Expand Down Expand Up @@ -121,7 +129,7 @@ def _instrument_user_manager(app, status, payload):
user_manager = client._user_manager
recorder = []
user_manager.http_client.session.mount(
_token_gen.TokenGenerator.ID_TOOLKIT_URL,
TOKEN_MGT_URLS['ID_TOOLKIT'],
testutils.MockAdapter(payload, status, recorder))
return user_manager, recorder

Expand All @@ -133,23 +141,33 @@ def _overwrite_iam_request(app, request):
client = auth._get_client(app)
client._token_generator.request = request

@pytest.fixture(scope='module')
def auth_app():
@pytest.fixture(scope='module', params=[{'emulated': False}, {'emulated': True}])
def auth_app(request):
"""Returns an App initialized with a mock service account credential.

This can be used in any scenario where the private key is required. Use user_mgt_app
for everything else.
"""
monkeypatch = pytest.MonkeyPatch()
if request.param['emulated']:
monkeypatch.setenv(EMULATOR_HOST_ENV_VAR, AUTH_EMULATOR_HOST)
monkeypatch.setitem(TOKEN_MGT_URLS, 'ID_TOOLKIT', EMULATED_ID_TOOLKIT_URL)
app = firebase_admin.initialize_app(MOCK_CREDENTIAL, name='tokenGen')
yield app
firebase_admin.delete_app(app)

@pytest.fixture(scope='module')
def user_mgt_app():
monkeypatch.undo()

@pytest.fixture(scope='module', params=[{'emulated': False}, {'emulated': True}])
def user_mgt_app(request):
monkeypatch = pytest.MonkeyPatch()
if request.param['emulated']:
monkeypatch.setenv(EMULATOR_HOST_ENV_VAR, AUTH_EMULATOR_HOST)
monkeypatch.setitem(TOKEN_MGT_URLS, 'ID_TOOLKIT', EMULATED_ID_TOOLKIT_URL)
app = firebase_admin.initialize_app(testutils.MockCredential(), name='userMgt',
options={'projectId': 'mock-project-id'})
yield app
firebase_admin.delete_app(app)
monkeypatch.undo()

@pytest.fixture
def env_var_app(request):
Expand Down
Loading