Skip to content

Commit

Permalink
[uss_qualifier] new resource for cleanly exposing the client identity
Browse files Browse the repository at this point in the history
  • Loading branch information
Shastick committed Nov 22, 2023
1 parent b151a97 commit 00e0db9
Show file tree
Hide file tree
Showing 21 changed files with 193 additions and 57 deletions.
4 changes: 4 additions & 0 deletions monitoring/monitorlib/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ def issue_token(self, intended_audience: str, scopes: List[str]) -> str:
)
return response.json()["access_token"]

def get_sub(self) -> Optional[str]:
"""directly return the configured `sub` value"""
return self._sub


class _SessionIssuer:
"""Helper for issuing tokens using a pre-configured Google session."""
Expand Down
20 changes: 11 additions & 9 deletions monitoring/uss_qualifier/configurations/dev/dss_probing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ v1:
test_run:
resources:
resource_declarations:
id_generator: {$ref: 'library/resources.yaml#/id_generator'}
kentland_service_area: {$ref: 'library/resources.yaml#/kentland_service_area'}
kentland_problematically_big_area: {$ref: 'library/resources.yaml#/kentland_problematically_big_area'}
kentland_service_area: { $ref: 'library/resources.yaml#/kentland_service_area' }
kentland_problematically_big_area: { $ref: 'library/resources.yaml#/kentland_problematically_big_area' }

utm_auth: {$ref: 'library/environment.yaml#/utm_auth'}
scd_dss_instances: {$ref: 'library/environment.yaml#/scd_dss_instances'}
netrid_dss_instances_v22a: {$ref: 'library/environment.yaml#/netrid_dss_instances_v22a'}
netrid_dss_instances_v19: {$ref: 'library/environment.yaml#/netrid_dss_instances_v19'}
utm_auth: { $ref: 'library/environment.yaml#/utm_auth' }
utm_client_identity: { $ref: 'library/resources.yaml#/utm_client_identity' }
id_generator: { $ref: 'library/resources.yaml#/id_generator' }
scd_dss_instances: { $ref: 'library/environment.yaml#/scd_dss_instances' }
netrid_dss_instances_v22a: { $ref: 'library/environment.yaml#/netrid_dss_instances_v22a' }
netrid_dss_instances_v19: { $ref: 'library/environment.yaml#/netrid_dss_instances_v19' }
non_baseline_inputs:
- v1.test_run.resources.resource_declarations.utm_auth
- v1.test_run.resources.resource_declarations.scd_dss_instances
Expand All @@ -22,15 +23,16 @@ v1:
resources:
f3411v19_dss_instances: netrid_dss_instances_v19
f3411v22a_dss_instances: netrid_dss_instances_v22a
utm_client_identity: utm_client_identity
id_generator: id_generator
service_area: kentland_service_area
problematically_big_area: kentland_problematically_big_area
execution:
stop_fast: true
artifacts:
output_path: output/dss_probing
raw_report: {}
sequence_view: {}
raw_report: { }
sequence_view: { }
tested_requirements:
- report_name: requirements
requirement_collections:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ utm_auth:
specification:
environment_variable_containing_auth_spec: AUTH_SPEC

utm_client_identity:
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json
resource_type: resources.communications.ClientIdentityResource
dependencies:
auth_adapter: utm_auth
specification:
whoami_audience: localhost
whoami_scope: rid.display_provider

# ===== NetRID =====

netrid_service_providers_v19:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ utm_auth:
specification:
environment_variable_containing_auth_spec: AUTH_SPEC

utm_client_identity:
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json
resource_type: resources.communications.ClientIdentityResource
dependencies:
auth_adapter: utm_auth
specification:
whoami_audience: localhost
whoami_scope: rid.display_provider

# ===== NetRID =====

netrid_service_providers_v19:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@ netrid_observation_evaluation_configuration:
resource_type: resources.netrid.EvaluationConfigurationResource
specification: { }

id_generator:
utm_client_identity:
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json
resource_type: resources.interuss.IDGeneratorResource
resource_type: resources.communications.ClientIdentityResource
dependencies:
auth_adapter: utm_auth
specification:
whoami_audience: localhost
whoami_scope: rid.display_provider

id_generator:
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json
resource_type: resources.interuss.IDGeneratorResource
dependencies:
client_identity: utm_client_identity
specification: { }

kentland_service_area:
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json
resource_type: resources.netrid.ServiceAreaResource
Expand Down
2 changes: 2 additions & 0 deletions monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ v1:
resource_declarations:
kentland_flights_data: {$ref: 'library/resources.yaml#/kentland_flights_data'}
netrid_observation_evaluation_configuration: {$ref: 'library/resources.yaml#/netrid_observation_evaluation_configuration'}
utm_client_identity: {$ref: 'library/resources.yaml#/utm_client_identity'}
id_generator: {$ref: 'library/resources.yaml#/id_generator'}
kentland_service_area: {$ref: 'library/resources.yaml#/kentland_service_area'}
au_problematically_big_area: {$ref: 'library/resources.yaml#/au_problematically_big_area'}
Expand All @@ -27,6 +28,7 @@ v1:
observers: netrid_observers_v19
evaluation_configuration: netrid_observation_evaluation_configuration
dss_instances: netrid_dss_instances_v19
utm_client_identity: utm_client_identity
id_generator: id_generator
service_area: kentland_service_area
problematically_big_area: au_problematically_big_area
Expand Down
2 changes: 2 additions & 0 deletions monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ v1:
resource_declarations:
kentland_flights_data: {$ref: 'library/resources.yaml#/kentland_flights_data'}
netrid_observation_evaluation_configuration: {$ref: 'library/resources.yaml#/netrid_observation_evaluation_configuration'}
utm_client_identity: {$ref: 'library/resources.yaml#/utm_client_identity'}
id_generator: {$ref: 'library/resources.yaml#/id_generator'}
kentland_service_area: {$ref: 'library/resources.yaml#/kentland_service_area'}
au_problematically_big_area: {$ref: 'library/resources.yaml#/au_problematically_big_area'}
Expand All @@ -27,6 +28,7 @@ v1:
observers: netrid_observers_v22a
evaluation_configuration: netrid_observation_evaluation_configuration
dss_instances: netrid_dss_instances_v22a
utm_client_identity: utm_client_identity
id_generator: id_generator
service_area: kentland_service_area
problematically_big_area: au_problematically_big_area
Expand Down
3 changes: 3 additions & 0 deletions monitoring/uss_qualifier/configurations/dev/uspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ v1:
che_non_conflicting_flights: {$ref: 'library/resources.yaml#/che_non_conflicting_flights'}
foca_flights_data: {$ref: 'library/resources.yaml#/foca_flights_data'}
netrid_observation_evaluation_configuration: {$ref: 'library/resources.yaml#/netrid_observation_evaluation_configuration'}
utm_client_identity: {$ref: 'library/resources.yaml#/utm_client_identity'}
id_generator: {$ref: 'library/resources.yaml#/id_generator'}
kentland_service_area: {$ref: 'library/resources.yaml#/kentland_service_area'}
au_problematically_big_area: {$ref: 'library/resources.yaml#/au_problematically_big_area'}
Expand Down Expand Up @@ -59,6 +60,7 @@ v1:
observers: netrid_observers_v22a
evaluation_configuration: netrid_observation_evaluation_configuration
netrid_dss_instances: netrid_dss_instances_v22a
utm_client_identity: utm_client_identity
id_generator: id_generator
service_area: kentland_service_area
problematically_big_area: au_problematically_big_area
Expand Down Expand Up @@ -86,6 +88,7 @@ v1:
observers: observers
evaluation_configuration: evaluation_configuration
netrid_dss_instances: netrid_dss_instances
utm_client_identity: utm_client_identity
id_generator: id_generator
service_area: service_area
problematically_big_area: problematically_big_area
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .auth_adapter import AuthAdapterResource
from .client_identity import ClientIdentityResource
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from implicitdict import ImplicitDict

from monitoring.monitorlib.infrastructure import AuthAdapter
from monitoring.uss_qualifier.resources.communications import AuthAdapterResource
from monitoring.uss_qualifier.resources.resource import Resource


class ClientIdentitySpecification(ImplicitDict):
"""
Specification for a Client Identity resource:
defines the audience and scope to use when a token is requested with the sole goal of
discovering the identity under which the client is known to the DSS, and no other audience or scope
are otherwise available in the context.
This is mostly useful for determining the client identity upon setup of the qualifier, when no
requests have yet been made to the DSS.
"""

whoami_audience: str
"""Audience to request for the access token used to determine subscriber identity."""

whoami_scope: str
"""Scope to request for the access token used to determine subscribe identity. Must be a scope that the client is
authorized to obtain."""


class ClientIdentityResource(Resource[ClientIdentitySpecification]):

specification: ClientIdentitySpecification

_adapter: AuthAdapter

def __init__(
self,
specification: ClientIdentitySpecification,
auth_adapter: AuthAdapterResource,
):
self.specification = specification
# Keep the adapter: we will only use it later at the moment it is required
self._adapter = auth_adapter.adapter

def subscriber(self) -> str:
"""
Return the subscriber identity as determined by the adapter:
this will usually only trigger a token request if no token had been requested yet by the auth adapter.
This is a function and not a field, to possibly profit from a token that would have been requested earlier
"""

sub = self._adapter.get_sub()
if sub is None:
# sub might be none because no authentication has happened yet:
# we force one using the client identify audience and scopes

# Do an initial token request so that adapter.get_sub() will return something
token = self._adapter.issue_token(
intended_audience=self.specification.whoami_audience,
scopes=[self.specification.whoami_scope],
)

sub = self._adapter.get_sub()
# Confirm we have a `sub` field available: if we don't, we bail
if sub is None:
raise ValueError(
f"subscriber is None, meaning `sub` claim was not found in payload of token, "
f"using {type(self._adapter).__name__} requesting {self.specification.whoami_scope} scope "
f"for {self.specification.whoami_audience} audience: {token}"
)

return sub
44 changes: 16 additions & 28 deletions monitoring/uss_qualifier/resources/interuss/id_generator.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,32 @@
from implicitdict import ImplicitDict
import jwt

from monitoring.prober.infrastructure import IDFactory
from monitoring.uss_qualifier.resources.communications import AuthAdapterResource
from monitoring.uss_qualifier.resources.communications import ClientIdentityResource
from monitoring.uss_qualifier.resources.resource import Resource


class IDGeneratorSpecification(ImplicitDict):
"""Generated IDs contain the client's identity so the appropriate client can clean up any dangling resources.
"""No fields required for the ID generator"""

Note that this may not be necessary/important with the resolution of https://github.com/interuss/dss/issues/939

To determine the client's identity, an access token is retrieved and the subscriber is read from the obtained token.
Therefore, the client running uss_qualifier must have the ability to obtain an access token via an auth adapter.
"""

whoami_audience: str
"""Audience to request for the access token used to determine subscriber identity."""

whoami_scope: str
"""Scope to request for the access token used to determine subscribe identity. Must be a scope that the client is
authorized to obtain."""
class IDGeneratorResource(Resource[IDGeneratorSpecification]):

_client_identity: ClientIdentityResource

class IDGeneratorResource(Resource[IDGeneratorSpecification]):
id_factory: IDFactory
subscriber: str
# Not initialised before it's actually used
_id_factory: IDFactory = None

def __init__(
self,
specification: IDGeneratorSpecification,
auth_adapter: AuthAdapterResource,
client_identity: ClientIdentityResource,
):
token = auth_adapter.adapter.issue_token(
specification.whoami_audience, [specification.whoami_scope]
)
payload = jwt.decode(token, options={"verify_signature": False})
if "sub" not in payload:
raise ValueError(
f"`sub` claim not found in payload of token using {type(auth_adapter).__name__} requesting {specification.whoami_scope} scope for {specification.whoami_audience} audience: {token}"
)
self.subscriber = payload["sub"]
self.id_factory = IDFactory(self.subscriber)
self._client_identity = client_identity

@property
def id_factory(self) -> IDFactory:
# Not thread safe, but the consequences here are acceptable
if self._id_factory is None:
self._id_factory = IDFactory(self._client_identity.subscriber())

return self._id_factory
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from monitoring.uss_qualifier.common_data_definitions import Severity
from monitoring.uss_qualifier.resources import VerticesResource
from monitoring.uss_qualifier.resources.astm.f3411.dss import DSSInstanceResource
from monitoring.uss_qualifier.resources.communications import ClientIdentityResource
from monitoring.uss_qualifier.resources.interuss.id_generator import IDGeneratorResource
from monitoring.uss_qualifier.resources.netrid.service_area import ServiceAreaResource
from monitoring.uss_qualifier.scenarios.astm.netrid.dss_wrapper import DSSWrapper
Expand All @@ -31,7 +32,7 @@ class SubscriptionSimple(GenericTestScenario):
_base_sub_id: str

# The value for 'owner' we'll expect the DSS to set on subscriptions
_owner: str
_client_identity: ClientIdentityResource

_test_subscription_ids: List[str]

Expand All @@ -53,6 +54,7 @@ def __init__(
id_generator: IDGeneratorResource,
isa: ServiceAreaResource,
problematically_big_area: VerticesResource,
client_identity: ClientIdentityResource,
):
"""
Expand All @@ -74,8 +76,6 @@ def __init__(
self._isa_area_loop = self._isa_area.copy()
self._isa_area_loop.append(self._isa_area_loop[0])

self._owner = id_generator.subscriber

# Prepare 4 different subscription ids:
self._test_subscription_ids = [
self._base_sub_id[:-1] + f"{i}" for i in range(4)
Expand All @@ -94,6 +94,8 @@ def __init__(
for vertex in problematically_big_area.specification.vertices
]

self._client_identity = client_identity

def run(self, context: ExecutionContext):
self.begin_test_scenario(context)

Expand Down Expand Up @@ -627,11 +629,12 @@ def _validate_subscription(
with self.check(
"Returned subscription owner is correct", [self._dss_wrapper.participant_id]
) as check:
if sub_under_test.owner != self._owner:
client_sub = self._client_identity.subscriber()
if sub_under_test.owner != client_sub:
check.record_failed(
"Returned subscription owner does not match provided one",
Severity.High,
f"Provided: {self._owner}, Returned: {sub_under_test.owner}",
f"Provided: {client_sub}, Returned: {sub_under_test.owner}",
query_timestamps=query_timestamps,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Perform basic operations on a single DSS instance to create, update and delete s

[`VerticesResource`](../../../../../resources/vertices.py) describing an area designed to be too big to be accepted by the DSS.

### client_identity

[`ClientIdentityResource`](../../../../../resources/communications/client_identity.py) to be used for this scenario.

## Setup test case

### Ensure clean workspace test step
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Perform basic operations on a single DSS instance to create, update and delete s

[`VerticesResource`](../../../../../resources/vertices.py) describing an area designed to be too big to be accepted by the DSS.

### client_identity

[`ClientIdentityResource`](../../../../../resources/communications/client_identity.py) to be used for this scenario.

## Setup test case

### Ensure clean workspace test step
Expand Down
Loading

0 comments on commit 00e0db9

Please sign in to comment.