update mobile app proxy's usage of the Cloud Auth API (#4194)
# What this PR does
## Checklist
- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
show up in the autogenerated release notes.
This commit is contained in:
parent
64bca2a2c0
commit
7c6ccd772c
4 changed files with 24 additions and 42 deletions
|
|
@ -300,7 +300,7 @@ def test_mobile_app_gateway_proxies_headers(
|
|||
):
|
||||
mock_requests.post.return_value = MockResponse()
|
||||
|
||||
_, user, auth_token = make_organization_and_user_with_mobile_app_auth_token()
|
||||
_, _, auth_token = make_organization_and_user_with_mobile_app_auth_token()
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("mobile_app:gateway", kwargs={"downstream_backend": DOWNSTREAM_BACKEND, "downstream_path": "test"})
|
||||
|
|
@ -315,7 +315,6 @@ def test_mobile_app_gateway_proxies_headers(
|
|||
params={},
|
||||
headers={
|
||||
"Authorization": f"Bearer {MOCK_AUTH_TOKEN}",
|
||||
"X-Grafana-User": f"email:{user.email}",
|
||||
"Content-Type": content_type_header,
|
||||
},
|
||||
)
|
||||
|
|
@ -328,30 +327,11 @@ def test_mobile_app_gateway_properly_generates_an_auth_token(
|
|||
make_organization,
|
||||
make_user_for_organization,
|
||||
):
|
||||
user_id = 90095905
|
||||
stack_id = 895
|
||||
organization_id = 8905
|
||||
stack_slug = "mvcmnvcmnvc"
|
||||
org_slug = "raintank"
|
||||
|
||||
organization = make_organization(
|
||||
stack_id=stack_id, org_id=organization_id, stack_slug=stack_slug, org_slug=org_slug
|
||||
)
|
||||
user = make_user_for_organization(organization, user_id=user_id)
|
||||
organization = make_organization(stack_id=stack_id)
|
||||
user = make_user_for_organization(organization)
|
||||
|
||||
auth_token = MobileAppGatewayView._get_auth_token(DOWNSTREAM_BACKEND, user)
|
||||
|
||||
assert auth_token == f"{stack_id}:{MOCK_AUTH_TOKEN}"
|
||||
|
||||
mock_request_signed_token.assert_called_once_with(
|
||||
organization,
|
||||
[CloudAuthApiClient.Scopes.INCIDENT_WRITE],
|
||||
{
|
||||
"user_id": user.user_id, # grafana user ID
|
||||
"user_email": user.email,
|
||||
"stack_id": organization.stack_id,
|
||||
"organization_id": organization.org_id, # grafana org ID
|
||||
"stack_slug": organization.stack_slug,
|
||||
"org_slug": organization.org_slug,
|
||||
},
|
||||
)
|
||||
mock_request_signed_token.assert_called_once_with(user, [CloudAuthApiClient.Scopes.INCIDENT_WRITE])
|
||||
|
|
|
|||
|
|
@ -153,20 +153,11 @@ class MobileAppGatewayView(APIView):
|
|||
HS256 = symmetric = shared secret (don't use this)
|
||||
"""
|
||||
org = user.organization
|
||||
token_claims = {
|
||||
"user_id": user.user_id, # grafana user ID
|
||||
"user_email": user.email,
|
||||
"stack_id": org.stack_id,
|
||||
"organization_id": org.org_id, # grafana org ID
|
||||
"stack_slug": org.stack_slug,
|
||||
"org_slug": org.org_slug,
|
||||
}
|
||||
|
||||
token_scopes = {
|
||||
cls.SupportedDownstreamBackends.INCIDENT: [CloudAuthApiClient.Scopes.INCIDENT_WRITE],
|
||||
}[downstream_backend]
|
||||
|
||||
return f"{org.stack_id}:{CloudAuthApiClient().request_signed_token(org, token_scopes, token_claims)}"
|
||||
return f"{org.stack_id}:{CloudAuthApiClient().request_signed_token(user, token_scopes)}"
|
||||
|
||||
@classmethod
|
||||
def _get_downstream_headers(
|
||||
|
|
@ -174,7 +165,6 @@ class MobileAppGatewayView(APIView):
|
|||
) -> typing.Dict[str, str]:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {cls._get_auth_token(downstream_backend, user)}",
|
||||
"X-Grafana-User": f"email:{user.email}",
|
||||
}
|
||||
|
||||
if (v := request.META.get("CONTENT_TYPE", None)) is not None:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from django.conf import settings
|
|||
from rest_framework import status
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.user_management.models import Organization
|
||||
from apps.user_management.models import User
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -43,8 +43,13 @@ class CloudAuthApiClient:
|
|||
self.api_token = settings.GRAFANA_CLOUD_AUTH_API_SYSTEM_TOKEN
|
||||
|
||||
def request_signed_token(
|
||||
self, org: "Organization", scopes: typing.List[Scopes], claims: typing.Dict[str, typing.Any]
|
||||
self,
|
||||
user: "User",
|
||||
scopes: typing.List[Scopes],
|
||||
extra_claims: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
||||
) -> str:
|
||||
org = user.organization
|
||||
|
||||
# The Cloud Auth API expects the org_id and stack_id to be strings
|
||||
org_id = str(org.org_id)
|
||||
stack_id = str(org.stack_id)
|
||||
|
|
@ -73,7 +78,10 @@ class CloudAuthApiClient:
|
|||
url,
|
||||
headers=headers,
|
||||
json={
|
||||
"claims": claims,
|
||||
"claims": {
|
||||
"sub": f"email:{user.email}",
|
||||
},
|
||||
"extra": extra_claims or {},
|
||||
"accessPolicy": {
|
||||
"scopes": scopes,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ def configure_cloud_auth_api_client(settings):
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("response_status_code", [status.HTTP_200_OK, status.HTTP_401_UNAUTHORIZED])
|
||||
@httpretty.activate(verbose=True, allow_net_connect=False)
|
||||
def test_request_signed_token(make_organization, response_status_code):
|
||||
def test_request_signed_token(make_organization, make_user_for_organization, response_status_code):
|
||||
mock_auth_token = ",mnasdlkjlakjoqwejroiqwejr"
|
||||
mock_response_text = "error message"
|
||||
|
||||
|
|
@ -27,12 +27,13 @@ def test_request_signed_token(make_organization, response_status_code):
|
|||
stack_id = 5
|
||||
|
||||
organization = make_organization(stack_id=stack_id, org_id=org_id)
|
||||
user = make_user_for_organization(organization=organization)
|
||||
|
||||
scopes = ["incident:write", "foo:bar"]
|
||||
claims = {"vegetable": "carrot", "fruit": "apple"}
|
||||
extra_claims = {"vegetable": "carrot", "fruit": "apple"}
|
||||
|
||||
def _make_request():
|
||||
return CloudAuthApiClient().request_signed_token(organization, scopes, claims)
|
||||
return CloudAuthApiClient().request_signed_token(user, scopes, extra_claims)
|
||||
|
||||
url = f"{GRAFANA_CLOUD_AUTH_API_URL}/v1/sign"
|
||||
mock_response = httpretty.Response(json.dumps({"data": {"token": mock_auth_token}}), status=response_status_code)
|
||||
|
|
@ -55,7 +56,10 @@ def test_request_signed_token(make_organization, response_status_code):
|
|||
|
||||
# assert we're sending the right body
|
||||
assert json.loads(last_request.body) == {
|
||||
"claims": claims,
|
||||
"claims": {
|
||||
"sub": f"email:{user.email}",
|
||||
},
|
||||
"extra": extra_claims,
|
||||
"accessPolicy": {
|
||||
"scopes": scopes,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue