Support multiple secrets for ADMIN_CLIENT_SECRETS

This will allow us to accept two different ones and therefore allow us
to rotate the secret that the admin client is sending to the API

Due to how the notifications-python-client throws exceptions, we run
into exactly the same issue with not being able to distinguish if a
`TokenDecodeError` is thrown because the token was encrypted with a
different secret key or if because there was a different error when
decoding. I've copied the TODO from `requires_auth` as this is exactly
the same issue.

I've also added a test case for functionality that was missing for an
out of date admin token (old IAT).
This commit is contained in:
David McDonald
2020-02-19 17:50:00 +00:00
parent 52d3df49d4
commit 7246306447
3 changed files with 64 additions and 21 deletions

View File

@@ -123,7 +123,30 @@ def test_admin_auth_should_not_allow_request_with_no_iat(client, sample_api_key)
request.headers = {'Authorization': 'Bearer {}'.format(token)}
with pytest.raises(AuthError) as exc:
requires_admin_auth()
assert exc.value.short_message == "Invalid token: could not decode your API token"
assert exc.value.short_message == "Unauthorized: admin authentication token not found"
def test_admin_auth_should_not_allow_request_with_old_iat(client):
iss = current_app.config['ADMIN_CLIENT_USER_NAME']
secret = current_app.config['ADMIN_CLIENT_SECRETS'][0]
# code copied from notifications_python_client.authentication.py::create_jwt_token
headers = {
"typ": 'JWT',
"alg": 'HS256'
}
claims = {
'iss': iss,
'iat': int(time.time()) - 60
}
token = jwt.encode(payload=claims, key=secret, headers=headers).decode()
request.headers = {'Authorization': 'Bearer {}'.format(token)}
with pytest.raises(AuthError) as exc:
requires_admin_auth()
assert exc.value.short_message == "Invalid token: expired, check that your system clock is accurate"
def test_auth_should_not_allow_request_with_extra_claims(client, sample_api_key):
@@ -188,11 +211,28 @@ def test_should_allow_valid_token_for_request_with_path_params_for_public_url(cl
def test_should_allow_valid_token_for_request_with_path_params_for_admin_url(client):
token = create_jwt_token(current_app.config['ADMIN_CLIENT_SECRETS'][0], current_app.config['ADMIN_CLIENT_USER_NAME'])
token = create_jwt_token(
current_app.config['ADMIN_CLIENT_SECRETS'][0], current_app.config['ADMIN_CLIENT_USER_NAME']
)
response = client.get('/service', headers={'Authorization': 'Bearer {}'.format(token)})
assert response.status_code == 200
def test_should_allow_valid_token_for_request_with_path_params_for_admin_url_with_second_secret(client):
with set_config(client.application, 'ADMIN_CLIENT_SECRETS', ["secret1", "secret2"]):
token = create_jwt_token(
current_app.config['ADMIN_CLIENT_SECRETS'][0], current_app.config['ADMIN_CLIENT_USER_NAME']
)
response = client.get('/service', headers={'Authorization': 'Bearer {}'.format(token)})
assert response.status_code == 200
token = create_jwt_token(
current_app.config['ADMIN_CLIENT_SECRETS'][1], current_app.config['ADMIN_CLIENT_USER_NAME']
)
response = client.get('/service', headers={'Authorization': 'Bearer {}'.format(token)})
assert response.status_code == 200
def test_should_allow_valid_token_when_service_has_multiple_keys(client, sample_api_key):
data = {'service': sample_api_key.service,
'name': 'some key name',
@@ -274,9 +314,9 @@ def test_authentication_returns_error_when_admin_client_has_no_secrets(client):
response = client.get(
'/service',
headers={'Authorization': 'Bearer {}'.format(token)})
assert response.status_code == 403
assert response.status_code == 401
error_message = json.loads(response.get_data())
assert error_message['message'] == {"token": ["Invalid token: could not decode your API token"]}
assert error_message['message'] == {"token": ["Unauthorized: admin authentication token not found"]}
def test_authentication_returns_error_when_admin_client_secret_is_invalid(client):
@@ -289,9 +329,9 @@ def test_authentication_returns_error_when_admin_client_secret_is_invalid(client
response = client.get(
'/service',
headers={'Authorization': 'Bearer {}'.format(token)})
assert response.status_code == 403
assert response.status_code == 401
error_message = json.loads(response.get_data())
assert error_message['message'] == {"token": ["Invalid token: could not decode your API token"]}
assert error_message['message'] == {"token": ["Unauthorized: admin authentication token not found"]}
current_app.config['ADMIN_CLIENT_SECRETS'][0] = api_secret
@@ -397,7 +437,9 @@ def test_proxy_key_non_auth_endpoint(notify_api, check_proxy_header, header_valu
(False, 'wrong_key', 200),
])
def test_proxy_key_on_admin_auth_endpoint(notify_api, check_proxy_header, header_value, expected_status):
token = create_jwt_token(current_app.config['ADMIN_CLIENT_SECRETS'][0], current_app.config['ADMIN_CLIENT_USER_NAME'])
token = create_jwt_token(
current_app.config['ADMIN_CLIENT_SECRETS'][0], current_app.config['ADMIN_CLIENT_USER_NAME']
)
with set_config_values(notify_api, {
'ROUTE_SECRET_KEY_1': 'key_1',