diff --git a/monitoring/monitorlib/mutate/scd.py b/monitoring/monitorlib/mutate/scd.py index 40c1e67eea..3543fe2c58 100644 --- a/monitoring/monitorlib/mutate/scd.py +++ b/monitoring/monitorlib/mutate/scd.py @@ -4,7 +4,12 @@ import s2sphere import yaml from implicitdict import ImplicitDict -from uas_standards.astm.f3548.v21.api import OPERATIONS, OperationID, Subscription +from uas_standards.astm.f3548.v21.api import ( + OPERATIONS, + OperationID, + Subscription, + PutSubscriptionParameters, +) from yaml.representer import Representer from monitoring.monitorlib import fetch @@ -74,18 +79,16 @@ def upsert_subscription( path = op.path.format(subscriptionid=subscription_id, version=version) query_type = QueryType.F3548v21DSSUpdateSubscription - body = { - "extents": Volume4D.from_values( - start_time, - end_time, - min_alt_m, - max_alt_m, - polygon=Polygon.from_latlng_rect(latlngrect=area), - ).to_f3548v21(), - "uss_base_url": base_url, - "notify_for_operational_intents": notify_for_op_intents, - "notify_for_constraints": notify_for_constraints, - } + body = build_upsert_subscription_params( + area_vertices=area, + start_time=start_time, + end_time=end_time, + base_url=base_url, + notify_for_op_intents=notify_for_op_intents, + notify_for_constraints=notify_for_constraints, + min_alt_m=min_alt_m, + max_alt_m=max_alt_m, + ) result = MutatedSubscription( fetch.query_and_describe( @@ -102,6 +105,30 @@ def upsert_subscription( return result +def build_upsert_subscription_params( + area_vertices: s2sphere.LatLngRect, + start_time: datetime.datetime, + end_time: datetime.datetime, + base_url: str, + notify_for_op_intents: bool, + notify_for_constraints: bool, + min_alt_m: float, + max_alt_m: float, +) -> PutSubscriptionParameters: + return PutSubscriptionParameters( + extents=Volume4D.from_values( + start_time, + end_time, + min_alt_m, + max_alt_m, + polygon=Polygon.from_latlng_rect(latlngrect=area_vertices), + ).to_f3548v21(), + uss_base_url=base_url, + notify_for_operational_intents=notify_for_op_intents, + notify_for_constraints=notify_for_constraints, + ) + + def delete_subscription( utm_client: infrastructure.UTMClientSession, subscription_id: str, diff --git a/monitoring/prober/infrastructure.py b/monitoring/prober/infrastructure.py index 3feb7eac51..95e3aac299 100644 --- a/monitoring/prober/infrastructure.py +++ b/monitoring/prober/infrastructure.py @@ -100,7 +100,7 @@ def wrapper_default_scope(*args, **kwargs): resource_type_code_descriptions: Dict[ResourceType, str] = {} -# Next code: 379 +# Next code: 381 def register_resource_type(code: int, description: str) -> ResourceType: """Register that the specified code refers to the described resource. diff --git a/monitoring/uss_qualifier/resources/astm/f3548/v21/planning_area.py b/monitoring/uss_qualifier/resources/astm/f3548/v21/planning_area.py index 73bf102364..eade2fae25 100644 --- a/monitoring/uss_qualifier/resources/astm/f3548/v21/planning_area.py +++ b/monitoring/uss_qualifier/resources/astm/f3548/v21/planning_area.py @@ -1,10 +1,19 @@ import datetime -from typing import List, Dict, Any, Optional, Self +from typing import List, Optional -from implicitdict import ImplicitDict, StringBasedDateTime -from uas_standards.astm.f3548.v21.api import Volume4D +from implicitdict import ImplicitDict +from uas_standards.astm.f3548.v21.api import ( + EntityOVN, + OperationalIntentState, + UssBaseURL, + EntityID, + PutOperationalIntentReferenceParameters, + ImplicitSubscriptionParameters, +) -from monitoring.monitorlib.geo import LatLngPoint, make_latlng_rect, Volume3D, Polygon +from monitoring.monitorlib.geo import make_latlng_rect, Volume3D +from monitoring.monitorlib.geotemporal import Volume4D +from monitoring.monitorlib.temporal import Time from monitoring.uss_qualifier.resources.astm.f3548.v21.subscription_params import ( SubscriptionParams, ) @@ -52,6 +61,46 @@ def get_new_subscription_params( notify_for_constraints=notify_for_constraints, ) + def get_new_operational_intent_ref_params( + self, + key: List[EntityOVN], + state: OperationalIntentState, + uss_base_url: UssBaseURL, + time_start: datetime.datetime, + time_end: datetime.datetime, + subscription_id: Optional[EntityID], + implicit_sub_base_url: Optional[UssBaseURL] = None, + implicit_sub_for_constraints: Optional[bool] = None, + ) -> PutOperationalIntentReferenceParameters: + """ + Build a PutOperationalIntentReferenceParameters object that can be used against the DSS OIR API. + + The extents contained in these parameters contain a single 4DVolume, which may not be entirely realistic, + but is sufficient in situations where the content of the OIR is irrelevant as long as it is valid, such + as for testing authentication or parameter validation. + + Note that this method allows building inconsistent parameters: + """ + return PutOperationalIntentReferenceParameters( + extents=[ + Volume4D( + volume=self.volume, + time_start=Time(time_start), + time_end=Time(time_end), + ).to_f3548v21() + ], + key=key, + state=state, + uss_base_url=uss_base_url, + subscription_id=subscription_id, + new_subscription=ImplicitSubscriptionParameters( + uss_base_url=implicit_sub_base_url, + notify_for_constraints=implicit_sub_for_constraints, + ) + if implicit_sub_base_url + else None, + ) + class PlanningAreaResource(Resource[PlanningAreaSpecification]): specification: PlanningAreaSpecification diff --git a/monitoring/uss_qualifier/resources/astm/f3548/v21/subscription_params.py b/monitoring/uss_qualifier/resources/astm/f3548/v21/subscription_params.py index b230563688..f02485d706 100644 --- a/monitoring/uss_qualifier/resources/astm/f3548/v21/subscription_params.py +++ b/monitoring/uss_qualifier/resources/astm/f3548/v21/subscription_params.py @@ -1,9 +1,12 @@ import datetime from typing import List, Optional, Self +import s2sphere from implicitdict import ImplicitDict +from uas_standards.astm.f3548.v21.api import PutSubscriptionParameters from monitoring.monitorlib.geo import LatLngPoint +from monitoring.monitorlib.mutate import scd as mutate class SubscriptionParams(ImplicitDict): @@ -44,3 +47,28 @@ class SubscriptionParams(ImplicitDict): def copy(self) -> Self: return SubscriptionParams(super().copy()) + + def to_upsert_subscription_params( + self, area: s2sphere.LatLngRect + ) -> PutSubscriptionParameters: + """ + Prepares the subscription parameters to be used in the body of an HTTP request + to create or update a subscription on the DSS in the SCD context. + + Args: + area: area to include in the subscription parameters + + Returns: + A dict to be passed as the request body when calling the subscription creation or update API. + + """ + return mutate.build_upsert_subscription_params( + area_vertices=area, + start_time=self.start_time, + end_time=self.end_time, + base_url=self.base_url, + notify_for_op_intents=self.notify_for_op_intents, + notify_for_constraints=self.notify_for_constraints, + min_alt_m=self.min_alt_m, + max_alt_m=self.max_alt_m, + ) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/__init__.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/__init__.py new file mode 100644 index 0000000000..8871d77868 --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/__init__.py @@ -0,0 +1 @@ +from .authentication_validation import AuthenticationValidation diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.md b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.md new file mode 100644 index 0000000000..4d5c528f34 --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.md @@ -0,0 +1,164 @@ +# ASTM SCD DSS: Interfaces authentication test scenario + +## Overview + +Ensures that a DSS properly authenticates requests to all its endpoints. + +Note that this does not cover authorization. + +## Resources + +### dss + +[`DSSInstanceResource`](../../../../../resources/astm/f3548/v21/dss.py) to be tested in this scenario. + +### id_generator + +[`IDGeneratorResource`](../../../../../resources/interuss/id_generator.py) providing the Subscription ID for this scenario. + +### planning_area + +[`PlanningAreaResource`](../../../../../resources/astm/f3548/v21/planning_area.py) describes the 3D volume in which entities will be created. + +## Setup test case + +### [Ensure clean workspace test step](../clean_workspace.md) + +This step ensures that no entity with the known test IDs exists in the DSS. + +## Endpoint authorization test case + +This test case ensures that the DSS properly authenticates requests to all its endpoints. + +### Subscription endpoints authentication test step + +#### 🛑 Unauthorized requests return the proper error message body check + +If the DSS under test does not return a proper error message body when an unauthorized request is received, it fails to properly implement the OpenAPI specification that is part of **[astm.f3548.v21.DSS0005,5](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Create subscription with missing credentials check + +If the DSS under test allows the creation of a subscription without any credentials being presented, it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Create subscription with invalid credentials check + +If the DSS under test allows the creation of a subscription with credentials that are well-formed but invalid, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Create subscription with missing scope check + +If the DSS under test allows the creation of a subscription with valid credentials but a missing scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Create subscription with incorrect scope check + +If the DSS under test allows the creation of a subscription with valid credentials but an incorrect scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Create subscription with valid credentials check + +If the DSS does not allow the creation of a subscription when valid credentials are presented, +it is in violation of **[astm.f3548.v21.DSS0005,5](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Get subscription with missing credentials check + +If the DSS under test allows the fetching of a subscription without any credentials being presented, it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Get subscription with invalid credentials check + +If the DSS under test allows the fetching of a subscription with credentials that are well-formed but invalid, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Get subscription with missing scope check + +If the DSS under test allows the fetching of a subscription with valid credentials but a missing scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Get subscription with incorrect scope check + +If the DSS under test allows the fetching of a subscription with valid credentials but an incorrect scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Get subscription with valid credentials check + +If the DSS does not allow fetching a subscription when valid credentials are presented, +it is in violation of **[astm.f3548.v21.DSS0005,5](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Mutate subscription with missing credentials check + +If the DSS under test allows the mutation of a subscription without any credentials being presented, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Mutate subscription with invalid credentials check + +If the DSS under test allows the mutation of a subscription with credentials that are well-formed but invalid, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Mutate subscription with missing scope check + +If the DSS under test allows the mutation of a subscription with valid credentials but a missing scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Mutate subscription with incorrect scope check + +If the DSS under test allows the mutation of a subscription with valid credentials but an incorrect scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Mutate subscription with valid credentials check + +If the DSS does not allow the mutation of a subscription when valid credentials are presented, +it is in violation of **[astm.f3548.v21.DSS0005,5](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Delete subscription with missing credentials check + +If the DSS under test allows the deletion of a subscription without any credentials being presented, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Delete subscription with invalid credentials check + +If the DSS under test allows the deletion of a subscription with credentials that are well-formed but invalid, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Delete subscription with missing scope check + +If the DSS under test allows the deletion of a subscription with valid credentials but a missing scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Delete subscription with incorrect scope check + +If the DSS under test allows the deletion of a subscription with valid credentials but an incorrect scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Delete subscription with valid credentials check + +If the DSS does not allow the deletion of a subscription when valid credentials are presented, +it is in violation of **[astm.f3548.v21.DSS0005,5](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Search subscriptions with missing credentials check + +If the DSS under test allows searching for subscriptions without any credentials being presented, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Search subscriptions with invalid credentials check + +If the DSS under test allows searching for subscriptions with credentials that are well-formed but invalid, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Search subscriptions with missing scope check + +If the DSS under test allows searching for subscriptions with valid credentials but a missing scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Search subscriptions with incorrect scope check + +If the DSS under test allows searching for subscriptions with valid credentials but an incorrect scope, +it is in violation of **[astm.f3548.v21.DSS0210,A2-7-2,7](../../../../../requirements/astm/f3548/v21.md)**. + +#### 🛑 Search subscriptions with valid credentials check + +If the DSS does not allow searching for subscriptions when valid credentials are presented, +it is in violation of **[astm.f3548.v21.DSS0005,5](../../../../../requirements/astm/f3548/v21.md)**. + +## [Cleanup](../clean_workspace.md) + +The cleanup phase of this test scenario removes the subscription with the known test ID if it has not been removed before. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.py new file mode 100644 index 0000000000..c6affd219c --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.py @@ -0,0 +1,151 @@ +from datetime import datetime, timedelta + +from uas_standards.astm.f3548.v21.constants import ( + Scope, +) + +from monitoring.monitorlib import fetch +from monitoring.monitorlib.auth import InvalidTokenSignatureAuth +from monitoring.monitorlib.fetch import QueryError +from monitoring.monitorlib.geotemporal import Volume4D +from monitoring.monitorlib.infrastructure import UTMClientSession +from monitoring.prober.infrastructure import register_resource_type +from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstanceResource +from monitoring.uss_qualifier.resources.astm.f3548.v21.planning_area import ( + PlanningAreaResource, + PlanningAreaSpecification, +) +from monitoring.uss_qualifier.resources.astm.f3548.v21.subscription_params import ( + SubscriptionParams, +) +from monitoring.uss_qualifier.resources.interuss.id_generator import IDGeneratorResource +from monitoring.uss_qualifier.scenarios.astm.utm.dss import test_step_fragments +from monitoring.uss_qualifier.scenarios.astm.utm.dss.authentication.generic import ( + GenericAuthValidator, +) +from monitoring.uss_qualifier.scenarios.astm.utm.dss.authentication.sub_api_validator import ( + SubscriptionAuthValidator, +) +from monitoring.uss_qualifier.scenarios.scenario import ( + TestScenario, +) +from monitoring.uss_qualifier.suites.suite import ExecutionContext + + +class AuthenticationValidation(TestScenario): + """ + A scenario that verifies that the DSS properly authenticates requests to all its endpoints. + + This scenario does not (yet) cover anything related to authorization: this first version + is intended to cover DSS0210,A2-7-2,7 + """ + + SUB_TYPE = register_resource_type( + 380, "Subscription, Operational Entity Id, Constraint" + ) + + # Reuse the same ID for every type of entity. + # As we are testing serially and cleaning up after each test, this should be fine. + _test_id: str + """Base identifier for the entities that will be created""" + + def __init__( + self, + dss: DSSInstanceResource, + id_generator: IDGeneratorResource, + planning_area: PlanningAreaResource, + ): + """ + Args: + dss: dss to test + id_generator: will let us generate specific identifiers + planning_area: An Area to use for the tests. It should be an area for which the DSS is responsible, + but has no other requirements. + """ + super().__init__() + scopes = {Scope.StrategicCoordination: "create and delete subscriptions"} + # This is an UTMClientSession + self._dss = dss.get_instance(scopes) + self._pid = [self._dss.participant_id] + self._test_id = id_generator.id_factory.make_id(self.SUB_TYPE) + self._planning_area = planning_area.specification + + # Build a ready-to-use 4D volume with no specified time for searching + # the currently active subscriptions + self._planning_area_volume4d = Volume4D( + volume=self._planning_area.volume, + ) + + self._sub_params = self._planning_area.get_new_subscription_params( + subscription_id=self._test_id, + # Set this slightly in the past: we will update the subscriptions + # to a later value that still needs to be roughly 'now' without getting into the future + start_time=datetime.now().astimezone() - timedelta(seconds=10), + duration=timedelta(minutes=20), + # This is a planning area without constraint processing + notify_for_op_intents=True, + notify_for_constraints=False, + ) + + # Session that won't provide a token at all + self._no_auth_session = UTMClientSession(self._dss.base_url, auth_adapter=None) + + # Session that should provide a well-formed token with a wrong signature + self._invalid_token_session = UTMClientSession( + self._dss.base_url, auth_adapter=InvalidTokenSignatureAuth() + ) + + generic_validator = GenericAuthValidator(self, self._dss) + + self._sub_validator = SubscriptionAuthValidator( + self, + generic_validator, + self._dss, + self._test_id, + self._planning_area, + self._planning_area_volume4d, + self._no_auth_session, + self._invalid_token_session, + ) + + def run(self, context: ExecutionContext): + self.begin_test_scenario(context) + self._setup_case() + self.begin_test_case("Endpoint authorization") + + self.begin_test_step("Subscription endpoints authentication") + self._sub_validator.verify_sub_endpoints_authentication() + self.end_test_step() + + self.end_test_case() + self.end_test_scenario() + + def _setup_case(self): + self.begin_test_case("Setup") + + self._ensure_clean_workspace_step() + + self.end_test_case() + + def _ensure_clean_workspace_step(self): + self.begin_test_step("Ensure clean workspace") + # Check for subscriptions that will collide with our IDs and drop them + self._ensure_test_entities_dont_exist() + # Drop any active remaining sub + self._ensure_no_active_subs_exist() + self.end_test_step() + + def _ensure_test_entities_dont_exist(self): + test_step_fragments.cleanup_sub(self, self._dss, self._test_id) + + def _ensure_no_active_subs_exist(self): + test_step_fragments.cleanup_active_subs( + self, + self._dss, + self._planning_area_volume4d, + ) + + def cleanup(self): + self.begin_cleanup() + self._ensure_test_entities_dont_exist() + self.end_cleanup() diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/generic.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/generic.py new file mode 100644 index 0000000000..b687eb65d5 --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/generic.py @@ -0,0 +1,99 @@ +from uas_standards.astm.f3548.v21.constants import Scope + +from monitoring.monitorlib import fetch +from monitoring.monitorlib.auth import InvalidTokenSignatureAuth +from monitoring.monitorlib.infrastructure import UTMClientSession +from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance +from monitoring.uss_qualifier.scenarios.scenario import TestScenario + + +class GenericAuthValidator: + """ + Utility class for common DSS authentication validation requirements. + """ + + def __init__( + self, + scenario: TestScenario, + dss: DSSInstance, + ): + self._scenario = scenario + self._authenticated_session = dss.client + self._invalid_token_session = UTMClientSession( + dss.base_url, auth_adapter=InvalidTokenSignatureAuth() + ) + self._no_auth_session = UTMClientSession(dss.base_url, auth_adapter=None) + + def query_no_auth(self, **query_kwargs) -> fetch.Query: + """Issue a query to the DSS without any credentials being passed""" + q = fetch.query_and_describe(client=self._no_auth_session, **query_kwargs) + self._scenario.record_query(q) + return q + + def query_invalid_token(self, **query_kwargs) -> fetch.Query: + """ + Issue a query to the DSS with an invalid token signature, but a valid token. + An appropriate scope is provided. + """ + q = fetch.query_and_describe( + client=self._invalid_token_session, + scope=Scope.StrategicCoordination, + **query_kwargs, + ) + self._scenario.record_query(q) + return q + + def query_missing_scope(self, **query_kwargs) -> fetch.Query: + """ + Issue a query to the DSS with a valid token, but omits specifying a scope. + """ + q = fetch.query_and_describe(client=self._authenticated_session, **query_kwargs) + self._scenario.record_query(q) + return q + + def query_wrong_scope(self, **query_kwargs) -> fetch.Query: + """ + Issue a query to the DSS with a valid token, but with a scope that is not allowed + to perform the operation. + """ + q = fetch.query_and_describe( + client=self._authenticated_session, + scope=Scope.AvailabilityArbitration, # TODO make these configurable via the constructor? + **query_kwargs, + ) + self._scenario.record_query(q) + return q + + def query_valid_auth(self, **query_kwargs) -> fetch.Query: + """ + Issue a query to the DSS with valid credentials. + """ + q = fetch.query_and_describe( + client=self._authenticated_session, + scope=Scope.StrategicCoordination, + # TODO make these configurable via the constructor? 'valid' may be different for certain endpoints + **query_kwargs, + ) + self._scenario.record_query(q) + return q + + def verify_4xx_response(self, q: fetch.Query): + """Verifies that the passed query response's body is a valid ErrorResponse: + it is either empty or contains a single 'message' field, as per the OpenAPI spec. + + Note that 409 responses to Operational Intent Reference mutations will contain more fields, + these are not handled here. + """ + with self._scenario.check( + "Unauthorized requests return the proper error message body" + ) as check: + if len(q.response.json) == 0: + return + elif len(q.response.json) == 1 and "message" in q.response.json: + return + else: + check.record_failed( + summary="Unexpected error response body", + details=f"Response body for {q.request.method} query to {q.request.url} should be empty or contain a single 'message' field. Was: {q.response.json}", + query_timestamps=[q.request.timestamp], + ) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/sub_api_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/sub_api_validator.py new file mode 100644 index 0000000000..7712f991fc --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/sub_api_validator.py @@ -0,0 +1,515 @@ +from datetime import datetime, timedelta + +from uas_standards.astm.f3548.v21.api import ( + OPERATIONS, + OperationID, + QuerySubscriptionParameters, +) + +from monitoring.monitorlib import fetch +from monitoring.monitorlib.fetch import QueryType +from monitoring.monitorlib.geotemporal import Volume4D +from monitoring.monitorlib.infrastructure import UTMClientSession +from monitoring.monitorlib.mutate import scd as mutate +from monitoring.monitorlib.mutate.scd import MutatedSubscription +from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance +from monitoring.uss_qualifier.resources.astm.f3548.v21.planning_area import ( + PlanningAreaSpecification, +) +from monitoring.uss_qualifier.scenarios.astm.utm.dss.authentication.generic import ( + GenericAuthValidator, +) +from monitoring.uss_qualifier.scenarios.scenario import TestScenario, PendingCheck + +TIME_TOLERANCE_SEC = 1 + + +class SubscriptionAuthValidator: + def __init__( + self, + scenario: TestScenario, + generic_validator: GenericAuthValidator, + dss: DSSInstance, + test_id: str, + planning_area: PlanningAreaSpecification, + planning_area_volume4d: Volume4D, + no_auth_session: UTMClientSession, + invalid_token_session: UTMClientSession, + ): + self._scenario = scenario + self._gen_val = generic_validator + self._dss = dss + self._pid = dss.participant_id + self._test_id = test_id + self._planning_area = planning_area + self._planning_area_volume4d = planning_area_volume4d + + self._sub_params = self._planning_area.get_new_subscription_params( + subscription_id=self._test_id, + # Set this slightly in the past: we will update the subscriptions + # to a later value that still needs to be roughly 'now' without getting into the future + start_time=datetime.now().astimezone() - timedelta(seconds=10), + duration=timedelta(minutes=20), + # This is a planning area without constraint processing + notify_for_op_intents=True, + notify_for_constraints=False, + ) + + self._no_auth_session = no_auth_session + self._invalid_token_session = invalid_token_session + + def verify_sub_endpoints_authentication(self): + self._verify_sub_creation() + self._verify_sub_get() + self._verify_sub_mutation() + self._verify_sub_deletion() + self._verify_sub_search() + + def _verify_sub_creation(self): + # Subscription creation request: + sub_params = dict(**self._sub_params) + op = OPERATIONS[OperationID.CreateSubscription] + del sub_params["sub_id"] + query_kwargs = dict( + verb=op.verb, + url=op.path.format(subscriptionid=self._sub_params.sub_id), + json=mutate.build_upsert_subscription_params(**sub_params), + query_type=QueryType.F3548v21DSSCreateSubscription, + participant_id=self._dss.participant_id, + ) + + # No auth: + no_auth_q = self._gen_val.query_no_auth(**query_kwargs) + with self._scenario.check( + "Create subscription with missing credentials" + ) as check: + if no_auth_q.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {no_auth_q.status_code}", + query_timestamps=[no_auth_q.request.timestamp], + ) + + self._sanity_check_sub_not_created(check, no_auth_q) + + self._gen_val.verify_4xx_response(no_auth_q) + + # Bad token signature: + invalid_token_q = self._gen_val.query_invalid_token(**query_kwargs) + with self._scenario.check( + "Create subscription with invalid credentials" + ) as check: + if invalid_token_q.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {invalid_token_q.status_code}", + query_timestamps=[invalid_token_q.request.timestamp], + ) + + self._sanity_check_sub_not_created(check, invalid_token_q) + + self._gen_val.verify_4xx_response(invalid_token_q) + + # Valid credentials but missing scope: + no_scope_q = self._gen_val.query_missing_scope(**query_kwargs) + with self._scenario.check("Create subscription with missing scope") as check: + if no_scope_q.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {no_scope_q.status_code}", + query_timestamps=[no_scope_q.request.timestamp], + ) + + self._sanity_check_sub_not_created(check, no_scope_q) + + # Valid credentials but wrong scope: + wrong_scope_q = self._gen_val.query_wrong_scope(**query_kwargs) + with self._scenario.check("Create subscription with incorrect scope") as check: + if wrong_scope_q.status_code != 403: + check.record_failed( + summary=f"Expected 403, got {wrong_scope_q.status_code}", + details="With an incorrect scope, the DSS should return a 403 without any data.", + query_timestamps=[wrong_scope_q.request.timestamp], + ) + + self._sanity_check_sub_not_created(check, wrong_scope_q) + + # Correct token: + # - confirms that the request would otherwise work + # - makes a subscription available for read/mutation tests + create_ok_q = self._gen_val.query_valid_auth(**query_kwargs) + with self._scenario.check( + "Create subscription with valid credentials" + ) as check: + if create_ok_q.status_code != 200: + check.record_failed( + summary=f"Expected 200, got {create_ok_q.status_code}", + query_timestamps=[create_ok_q.request.timestamp], + ) + + # Store the subscription + self._current_sub = MutatedSubscription(create_ok_q).subscription + + def _verify_sub_get(self): + op = OPERATIONS[OperationID.GetSubscription] + + query_kwargs = dict( + verb=op.verb, + url=op.path.format(subscriptionid=self._sub_params.sub_id), + query_type=QueryType.F3548v21DSSGetSubscription, + participant_id=self._dss.participant_id, + ) + + query_no_auth = self._gen_val.query_no_auth(**query_kwargs) + with self._scenario.check("Get subscription with missing credentials") as check: + if query_no_auth.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_no_auth.status_code}", + query_timestamps=[query_no_auth.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_no_auth) + + query_invalid_token = self._gen_val.query_invalid_token(**query_kwargs) + with self._scenario.check("Get subscription with invalid credentials") as check: + if query_invalid_token.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_invalid_token.status_code}", + query_timestamps=[query_invalid_token.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_invalid_token) + + query_missing_scope = self._gen_val.query_missing_scope(**query_kwargs) + with self._scenario.check("Get subscription with missing scope") as check: + if query_missing_scope.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_missing_scope.status_code}", + details="Without the proper scope, the DSS should return a 401 without any data.", + query_timestamps=[query_missing_scope.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_missing_scope) + + query_wrong_scope = self._gen_val.query_wrong_scope(**query_kwargs) + with self._scenario.check("Get subscription with incorrect scope") as check: + if query_wrong_scope.status_code != 403: + check.record_failed( + summary=f"Expected 403, got {query_wrong_scope.status_code}", + details="With an incorrect scope, the DSS should return a 403 without any data.", + query_timestamps=[query_wrong_scope.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_wrong_scope) + + query_ok = self._gen_val.query_valid_auth(**query_kwargs) + with self._scenario.check("Get subscription with valid credentials") as check: + if query_ok.status_code != 200: + check.record_failed( + summary=f"Expected 200, got {query_ok.status_code}", + query_timestamps=[query_ok.request.timestamp], + ) + + def _verify_sub_mutation(self): + # Subscription creation request: + new_params = self._sub_params.copy() + new_params.end_time = new_params.end_time - timedelta(seconds=10) + pld_params = dict(**new_params) + op = OPERATIONS[OperationID.UpdateSubscription] + del pld_params["sub_id"] + + query_kwargs = dict( + verb=op.verb, + url=op.path.format( + subscriptionid=self._sub_params.sub_id, + version=self._current_sub.version, + ), + json=mutate.build_upsert_subscription_params(**pld_params), + query_type=QueryType.F3548v21DSSCreateSubscription, + participant_id=self._dss.participant_id, + ) + + no_auth_q = self._gen_val.query_no_auth(**query_kwargs) + with self._scenario.check( + "Mutate subscription with missing credentials" + ) as check: + if no_auth_q.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {no_auth_q.status_code}", + query_timestamps=[no_auth_q.request.timestamp], + ) + # Sanity check: confirm the subscription was not mutated by the faulty call: + self._sanity_check_sub_not_mutated(check, no_auth_q) + + self._gen_val.verify_4xx_response(no_auth_q) + + invalid_token_q = self._gen_val.query_invalid_token(**query_kwargs) + with self._scenario.check( + "Mutate subscription with invalid credentials" + ) as check: + if invalid_token_q.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {invalid_token_q.status_code}", + query_timestamps=[invalid_token_q.request.timestamp], + ) + # Sanity check: confirm the subscription was not mutated by the faulty call: + self._sanity_check_sub_not_mutated(check, invalid_token_q) + + self._gen_val.verify_4xx_response(invalid_token_q) + + query_missing_scope = self._gen_val.query_missing_scope(**query_kwargs) + with self._scenario.check("Mutate subscription with missing scope") as check: + if query_missing_scope.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_missing_scope.status_code}", + details="Without the proper scope, the DSS should return a 401 without any data.", + query_timestamps=[query_missing_scope.request.timestamp], + ) + # Sanity check: confirm the subscription was not mutated by the faulty call: + self._sanity_check_sub_not_mutated(check, query_missing_scope) + + self._gen_val.verify_4xx_response(query_missing_scope) + + query_wrong_scope = self._gen_val.query_wrong_scope(**query_kwargs) + with self._scenario.check("Mutate subscription with incorrect scope") as check: + if query_wrong_scope.status_code != 403: + check.record_failed( + summary=f"Expected 403, got {query_wrong_scope.status_code}", + details="With an incorrect scope, the DSS should return a 403 without any data.", + query_timestamps=[query_wrong_scope.request.timestamp], + ) + # Sanity check: confirm the subscription was not mutated by the faulty call: + self._sanity_check_sub_not_mutated(check, query_wrong_scope) + + mutate_ok_q = self._gen_val.query_valid_auth(**query_kwargs) + with self._scenario.check( + "Mutate subscription with valid credentials" + ) as check: + if mutate_ok_q.status_code != 200: + check.record_failed( + summary=f"Expected 200, got {mutate_ok_q.status_code}", + query_timestamps=[mutate_ok_q.request.timestamp], + ) + + self._current_sub = MutatedSubscription(mutate_ok_q).subscription + + def _verify_sub_deletion(self): + op = OPERATIONS[OperationID.DeleteSubscription] + + query_kwargs = dict( + verb=op.verb, + url=op.path.format( + subscriptionid=self._sub_params.sub_id, + version=self._current_sub.version, + ), + query_type=QueryType.F3548v21DSSDeleteSubscription, + participant_id=self._dss.participant_id, + ) + + query_no_auth = self._gen_val.query_no_auth(**query_kwargs) + with self._scenario.check( + "Delete subscription with missing credentials" + ) as check: + if query_no_auth.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_no_auth.status_code}", + query_timestamps=[query_no_auth.request.timestamp], + ) + # Sanity check + self._sanity_check_sub_not_deleted(check, query_no_auth) + + self._gen_val.verify_4xx_response(query_no_auth) + + query_invalid_token = self._gen_val.query_invalid_token(**query_kwargs) + with self._scenario.check( + "Delete subscription with invalid credentials" + ) as check: + if query_invalid_token.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_invalid_token.status_code}", + query_timestamps=[query_invalid_token.request.timestamp], + ) + # Sanity check + self._sanity_check_sub_not_deleted(check, query_invalid_token) + + self._gen_val.verify_4xx_response(query_invalid_token) + + query_missing_scope = self._gen_val.query_missing_scope(**query_kwargs) + with self._scenario.check("Delete subscription with missing scope") as check: + if query_missing_scope.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_missing_scope.status_code}", + details="Without the proper scope, the DSS should return a 401 without any data.", + query_timestamps=[query_missing_scope.request.timestamp], + ) + # Sanity check + self._sanity_check_sub_not_deleted(check, query_missing_scope) + + self._gen_val.verify_4xx_response(query_missing_scope) + + query_wrong_scope = self._gen_val.query_wrong_scope(**query_kwargs) + with self._scenario.check("Delete subscription with incorrect scope") as check: + if query_wrong_scope.status_code != 403: + check.record_failed( + summary=f"Expected 403, got {query_wrong_scope.status_code}", + details="With an incorrect scope, the DSS should return a 403 without any data.", + query_timestamps=[query_wrong_scope.request.timestamp], + ) + # Sanity check + self._sanity_check_sub_not_deleted(check, query_wrong_scope) + + self._gen_val.verify_4xx_response(query_wrong_scope) + + query_ok = self._gen_val.query_valid_auth(**query_kwargs) + with self._scenario.check( + "Delete subscription with valid credentials" + ) as check: + if query_ok.status_code != 200: + check.record_failed( + summary=f"Expected 200, got {query_ok.status_code}", + query_timestamps=[query_ok.request.timestamp], + ) + + # Confirm the subscription was deleted + not_found = self._dss.get_subscription(self._sub_params.sub_id) + with self._scenario.check( + "Delete subscription with valid credentials" + ) as check: + if not_found.status_code != 404: + check.record_failed( + summary=f"Expected 404, got {not_found.status_code}", + details="The subscription should have been deleted, as the deletion attempt was appropriately authenticated.", + query_timestamps=[ + query_ok.request.timestamp, + not_found.request.timestamp, + ], + ) + + def _verify_sub_search(self): + op = OPERATIONS[OperationID.QuerySubscriptions] + + query_kwargs = dict( + verb=op.verb, + url=op.path, + query_type=QueryType.F3548v21DSSQuerySubscriptions, + json=QuerySubscriptionParameters( + area_of_interest=self._planning_area_volume4d + ), + participant_id=self._dss.participant_id, + ) + + query_no_auth = self._gen_val.query_no_auth(**query_kwargs) + with self._scenario.check( + "Search subscriptions with missing credentials" + ) as check: + if query_no_auth.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_no_auth.status_code}", + details="Without valid credentials, the DSS should return a 401 without any data.", + query_timestamps=[query_no_auth.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_no_auth) + + query_invalid_token = self._gen_val.query_invalid_token(**query_kwargs) + with self._scenario.check( + "Search subscriptions with invalid credentials" + ) as check: + if query_invalid_token.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_invalid_token.status_code}", + details="Without valid credentials, the DSS should return a 401 without any data.", + query_timestamps=[query_invalid_token.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_invalid_token) + + query_missing_scope = self._gen_val.query_missing_scope(**query_kwargs) + with self._scenario.check("Search subscriptions with missing scope") as check: + if query_missing_scope.status_code != 401: + check.record_failed( + summary=f"Expected 401, got {query_missing_scope.status_code}", + details="Without the proper scope, the DSS should return a 401 without any data.", + query_timestamps=[query_missing_scope.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_missing_scope) + + query_wrong_scope = self._gen_val.query_wrong_scope(**query_kwargs) + with self._scenario.check("Search subscriptions with incorrect scope") as check: + if query_wrong_scope.status_code != 403: + check.record_failed( + summary=f"Expected 403, got {query_wrong_scope.status_code}", + details="With an incorrect scope, the DSS should return a 403 without any data.", + query_timestamps=[query_wrong_scope.request.timestamp], + ) + + self._gen_val.verify_4xx_response(query_wrong_scope) + + query_ok = self._gen_val.query_valid_auth(**query_kwargs) + with self._scenario.check( + "Search subscriptions with valid credentials" + ) as check: + if query_ok.status_code != 200: + check.record_failed( + summary=f"Expected 200, got {query_ok.status_code}", + query_timestamps=[query_ok.request.timestamp], + ) + + def _sanity_check_sub_not_created( + self, check: PendingCheck, creation_q: fetch.Query + ): + sanity_check = self._dss.get_subscription(self._sub_params.sub_id) + self._scenario.record_query(sanity_check) + if sanity_check.status_code != 404: + check.record_failed( + summary=f"Subscription was created by an unauthorized request.", + details="The subscription should not have been created, as the creation attempt was not authenticated.", + query_timestamps=[ + creation_q.request.timestamp, + sanity_check.request.timestamp, + ], + ) + self._gen_val.verify_4xx_response(sanity_check) + + def _sanity_check_sub_not_mutated( + self, check: PendingCheck, mutation_q: fetch.Query + ): + sanity_check = self._dss.get_subscription(self._sub_params.sub_id) + self._scenario.record_query(sanity_check) + if ( + abs( + sanity_check.subscription.time_end.value.datetime + - self._current_sub.time_end.value.datetime + ).total_seconds() + > TIME_TOLERANCE_SEC + ): + check.record_failed( + summary=f"Subscription was mutated by an unauthorized query.", + details="The subscription should not have been mutated, as the mutation attempt was not appropriately authenticated.", + query_timestamps=[ + mutation_q.request.timestamp, + sanity_check.request.timestamp, + ], + ) + + def _sanity_check_sub_not_deleted( + self, check: PendingCheck, deletion_q: fetch.Query + ): + sanity_check = self._dss.get_subscription(self._sub_params.sub_id) + self._scenario.record_query(sanity_check) + if sanity_check.status_code == 404: + check.record_failed( + summary=f"Unauthorized request could delete the subscription.", + details="The subscription should not have been deleted, as the deletion attempt was not authenticated.", + query_timestamps=[ + deletion_q.request.timestamp, + sanity_check.request.timestamp, + ], + ) + elif sanity_check.status_code != 200: + check.record_failed( + summary=f"Expected 200, got {sanity_check.status_code}", + details="The subscription should not have been deleted, as the deletion attempt was not appropriately authenticated.", + query_timestamps=[ + deletion_q.request.timestamp, + sanity_check.request.timestamp, + ], + ) diff --git a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md index 8be2073a07..afd2306191 100644 --- a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md +++ b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md @@ -4,10 +4,11 @@ ## [Actions](../../README.md#actions) -1. Scenario: [ASTM SCD DSS: Subscription Simple](../../../scenarios/astm/utm/dss/subscription_simple.md) ([`scenarios.astm.utm.dss.SubscriptionSimple`](../../../scenarios/astm/utm/dss/subscription_simple.py)) -2. Scenario: [ASTM SCD DSS: Subscription Validation](../../../scenarios/astm/utm/dss/subscription_validation.md) ([`scenarios.astm.utm.dss.SubscriptionValidation`](../../../scenarios/astm/utm/dss/subscription_validation.py)) -3. Scenario: [ASTM F3548-21 UTM DSS Operational Intent Reference Access Control](../../../scenarios/astm/utm/op_intent_ref_access_control.md) ([`scenarios.astm.utm.OpIntentReferenceAccessControl`](../../../scenarios/astm/utm/op_intent_ref_access_control.py)) -4. Scenario: [ASTM F3548-21 UTM DSS interoperability](../../../scenarios/astm/utm/dss_interoperability.md) ([`scenarios.astm.utm.DSSInteroperability`](../../../scenarios/astm/utm/dss_interoperability.py)) +1. Scenario: [ASTM SCD DSS: Interfaces authentication](../../../scenarios/astm/utm/dss/authentication/authentication_validation.md) ([`scenarios.astm.utm.dss.authentication.AuthenticationValidation`](../../../scenarios/astm/utm/dss/authentication/authentication_validation.py)) +2. Scenario: [ASTM SCD DSS: Subscription Simple](../../../scenarios/astm/utm/dss/subscription_simple.md) ([`scenarios.astm.utm.dss.SubscriptionSimple`](../../../scenarios/astm/utm/dss/subscription_simple.py)) +3. Scenario: [ASTM SCD DSS: Subscription Validation](../../../scenarios/astm/utm/dss/subscription_validation.md) ([`scenarios.astm.utm.dss.SubscriptionValidation`](../../../scenarios/astm/utm/dss/subscription_validation.py)) +4. Scenario: [ASTM F3548-21 UTM DSS Operational Intent Reference Access Control](../../../scenarios/astm/utm/op_intent_ref_access_control.md) ([`scenarios.astm.utm.OpIntentReferenceAccessControl`](../../../scenarios/astm/utm/op_intent_ref_access_control.py)) +5. Scenario: [ASTM F3548-21 UTM DSS interoperability](../../../scenarios/astm/utm/dss_interoperability.md) ([`scenarios.astm.utm.DSSInteroperability`](../../../scenarios/astm/utm/dss_interoperability.py)) ## [Checked requirements](../../README.md#checked-requirements) @@ -19,26 +20,31 @@ Checked in - astm
.f3548
.v21
+ astm
.f3548
.v21
DSS0005,1 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0005,2 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0005,5 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0015 Implemented ASTM SCD DSS: Subscription Validation + + DSS0210,A2-7-2,7 + Implemented + ASTM SCD DSS: Interfaces authentication + DSS0300 Implemented diff --git a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml index dcf5c76b95..49cc0d8660 100644 --- a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml +++ b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml @@ -8,6 +8,12 @@ resources: planning_area: resources.astm.f3548.v21.PlanningAreaResource problematically_big_area: resources.VerticesResource actions: + - test_scenario: + scenario_type: scenarios.astm.utm.dss.authentication.AuthenticationValidation + resources: + dss: dss + id_generator: id_generator + planning_area: planning_area - test_scenario: scenario_type: scenarios.astm.utm.dss.SubscriptionSimple resources: diff --git a/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md b/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md index c22d904d04..c1565f1781 100644 --- a/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md +++ b/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md @@ -32,20 +32,20 @@ Checked in - astm
.f3548
.v21
+ astm
.f3548
.v21
DSS0005,1 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted DSS0005,2 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents DSS0005,5 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0015 @@ -57,6 +57,11 @@ Implemented Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + + DSS0210,A2-7-2,7 + Implemented + ASTM SCD DSS: Interfaces authentication + DSS0300 Implemented diff --git a/monitoring/uss_qualifier/suites/faa/uft/message_signing.md b/monitoring/uss_qualifier/suites/faa/uft/message_signing.md index 3899352ff7..d2096e92a5 100644 --- a/monitoring/uss_qualifier/suites/faa/uft/message_signing.md +++ b/monitoring/uss_qualifier/suites/faa/uft/message_signing.md @@ -18,20 +18,20 @@ Checked in - astm
.f3548
.v21
+ astm
.f3548
.v21
DSS0005,1 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted DSS0005,2 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents DSS0005,5 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0015 @@ -43,6 +43,11 @@ Implemented Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + + DSS0210,A2-7-2,7 + Implemented + ASTM SCD DSS: Interfaces authentication + DSS0300 Implemented diff --git a/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md b/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md index c5d3da4855..09fbed9992 100644 --- a/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md +++ b/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md @@ -408,26 +408,31 @@ ASTM NetRID DSS: Concurrent Requests
ASTM NetRID DSS: ISA Expiry
ASTM NetRID DSS: ISA Subscription Interactions
ASTM NetRID DSS: Simple ISA
ASTM NetRID DSS: Submitted ISA Validations
ASTM NetRID DSS: Subscription Simple
ASTM NetRID DSS: Subscription Validation
ASTM NetRID DSS: Token Validation - astm
.f3548
.v21
+ astm
.f3548
.v21
DSS0005,1 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0005,2 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0005,5 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0015 Implemented ASTM SCD DSS: Subscription Validation + + DSS0210,A2-7-2,7 + Implemented + ASTM SCD DSS: Interfaces authentication + DSS0300 Implemented diff --git a/monitoring/uss_qualifier/suites/uspace/flight_auth.md b/monitoring/uss_qualifier/suites/uspace/flight_auth.md index ae57d185f0..836283b0ca 100644 --- a/monitoring/uss_qualifier/suites/uspace/flight_auth.md +++ b/monitoring/uss_qualifier/suites/uspace/flight_auth.md @@ -19,20 +19,20 @@ Checked in - astm
.f3548
.v21
+ astm
.f3548
.v21
DSS0005,1 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted DSS0005,2 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents DSS0005,5 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0015 @@ -44,6 +44,11 @@ Implemented Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + + DSS0210,A2-7-2,7 + Implemented + ASTM SCD DSS: Interfaces authentication + DSS0300 Implemented diff --git a/monitoring/uss_qualifier/suites/uspace/required_services.md b/monitoring/uss_qualifier/suites/uspace/required_services.md index 8fe4bd2d12..6e44f0ea9f 100644 --- a/monitoring/uss_qualifier/suites/uspace/required_services.md +++ b/monitoring/uss_qualifier/suites/uspace/required_services.md @@ -454,20 +454,20 @@ ASTM NetRID DSS: Concurrent Requests
ASTM NetRID DSS: ISA Expiry
ASTM NetRID DSS: ISA Subscription Interactions
ASTM NetRID DSS: Simple ISA
ASTM NetRID DSS: Submitted ISA Validations
ASTM NetRID DSS: Subscription Simple
ASTM NetRID DSS: Subscription Validation
ASTM NetRID DSS: Token Validation - astm
.f3548
.v21
+ astm
.f3548
.v21
DSS0005,1 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted DSS0005,2 Implemented - ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents + ASTM F3548 flight planners preparation
ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation
Data Validation of GET operational intents by USS
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted
Validation of operational intents DSS0005,5 Implemented - ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation + ASTM F3548-21 UTM DSS Operational Intent Reference Access Control
ASTM SCD DSS: Interfaces authentication
ASTM SCD DSS: Subscription Simple
ASTM SCD DSS: Subscription Validation DSS0015 @@ -479,6 +479,11 @@ Implemented Off-Nominal planning: down USS
Off-Nominal planning: down USS with equal priority conflicts not permitted + + DSS0210,A2-7-2,7 + Implemented + ASTM SCD DSS: Interfaces authentication + DSS0300 Implemented