diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.md b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.md index 33fe346f07..de87a4b816 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.md @@ -22,6 +22,36 @@ Verifies that all subscription CRUD operations performed on a single DSS instanc [`PlanningAreaResource`](../../../../../resources/astm/f3548/v21/planning_area.py) describes the 3D volume in which subscriptions will be created. +### second_utm_auth + +A `resources.communications.AuthAdapterResource` containing a second set of valid credentials for interacting with the DSS. + +This second set of credentials is required to validate that the DSS properly synchronizes the manager of a subscription to other DSS instances. + +The participant under test is responsible for providing this second set of credentials along the primary ones used in most other scenarios. + +#### Credential requirements + +In general, these test credentials may be in all points equal to the ones used by the `AuthAdapterResource` that is +provided to the `dss` resources above, except for the value contained in the `sub` claim of the token. + +For the purpose of this scenario, these credentials must be allowed to create, modify and delete subscriptions on the DSS, +as well as querying them. + +Note that most checks in this scenario will work if the second set of credentials is not provided. + +##### Required scope + +For the purpose of this scenario, the `second_utm_auth` resource must provide access to a token with at least the following scope: + +* `utm.strategic_coordination` + +##### Separate subscription + +Note that the subscription (or 'sub' claim, not to be confused with an SCD DSS subscription) of the token that will be obtained for this resource +MUST be different from the one of the `dss` resources mentioned above: +this will be verified at runtime, and the depending checks will not be run if this is not the case. + ## Setup test case ### [Ensure clean workspace test step](../clean_workspace.md) @@ -117,6 +147,23 @@ Verify that the subscription returned by every DSS is correctly formatted and co Verify that the version of the subscription returned by every DSS is as expected. +### Verify manager synchronization test step + +Checks that the manager of a subscription is properly synchronized across all DSS instances. + +This is done by means of using a separate set of credentials to create a subscription on the primary DSS, +and then verifying that the main credentials are not able to mutate this subscription via one of the secondary DSS instances + +#### [Create subscription](../fragments/sub/crud/create.md) + +Verify that a subscription can be created on the primary DSS. + +#### 🛑 Subscription deletion with different non-managing credentials on secondary DSS fails check + +If the subscription can be deleted by a client which did not create it, via a DSS instance to which the subscription was synced +following its creation on the primary DSS, either one of the primary DSS or the DSS that accepted the deletion failed to properly broadcast, respectively take into account, the manage of the subscription, +and therefore violates **[astm.f3548.v21.DSS0210,1b](../../../../../requirements/astm/f3548/v21.md)**. + ### Delete subscription test step Attempt to delete the subscription in various ways and ensure that the DSS reacts properly. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py index 6cb84e84be..f32e07b333 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py @@ -19,6 +19,7 @@ from monitoring.uss_qualifier.resources.astm.f3548.v21.planning_area import ( SubscriptionParams, ) +from monitoring.uss_qualifier.resources.communications import AuthAdapterResource 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.validators.subscription_validator import ( @@ -43,14 +44,23 @@ class SubscriptionSynchronization(TestScenario): """ SUB_TYPE = register_resource_type(379, "Subscription") + ACL_SUB_TYPE = register_resource_type(380, "Subscription with different credentials") _dss: DSSInstance + # Separate DSS client for testing manager synchronization + _dss_separate_creds: Optional[DSSInstance] + _dss_read_instances: List[DSSInstance] # Base identifier for the subscriptions that will be created _sub_id: SubscriptionID + # Extra sub id for testing manager sync + _acl_sub_id: SubscriptionID + # The extra subscription for testing manager sync + _current_acl_sub: Subscription + # Base parameters used for subscription creation _sub_params: SubscriptionParams @@ -63,6 +73,7 @@ def __init__( other_instances: DSSInstancesResource, id_generator: IDGeneratorResource, planning_area: PlanningAreaResource, + second_utm_auth: Optional[AuthAdapterResource] = None, ): """ Args: @@ -87,6 +98,7 @@ def __init__( ] self._sub_id = id_generator.id_factory.make_id(self.SUB_TYPE) + self._acl_sub_id = id_generator.id_factory.make_id(self.ACL_SUB_TYPE) self._planning_area = planning_area.specification # Build a ready-to-use 4D volume with no specified time for searching @@ -126,6 +138,14 @@ def __init__( notify_for_constraints=False, ) + if second_utm_auth is not None: + # Build a second DSSWrapper identical to the first but with the other auth adapter + self._dss_separate_creds = self._dss.with_different_auth( + second_utm_auth, scopes_primary + ) + else: + self._dss_separate_creds = None + def run(self, context: ExecutionContext): # Check that we actually have at least one other DSS to test against: @@ -155,6 +175,19 @@ def run(self, context: ExecutionContext): self._query_secondaries_and_compare(self._sub_params) self.end_test_step() + if self._dss_separate_creds: + self.begin_test_step("Create subscription with different credentials") + + self.end_test_step() + self.begin_test_step("Verify manager synchronization") + self._step_test_delete_sub_with_separate_creds() + self.end_test_step() + else: + self.record_note( + "manager_check", + "Skipping manager synchronization check: no extra credentials provided", + ) + self.begin_test_step("Delete subscription") self._test_delete_sub() self.end_test_step() @@ -184,6 +217,10 @@ def _ensure_clean_workspace_step(self): def _ensure_test_sub_ids_do_not_exist(self): test_step_fragments.cleanup_sub(self, self._dss, self._sub_id) + if self._dss_separate_creds: + test_step_fragments.cleanup_sub( + self, self._dss_separate_creds, self._acl_sub_id + ) def _ensure_no_active_subs_exist(self): test_step_fragments.cleanup_active_subs( @@ -546,6 +583,69 @@ def _test_mutate_subscriptions_shift_time(self): # Update the parameters we used for that subscription self._sub_params = new_params + def _step_create_sub_separate_creds(self): + """Create a subscription on the main DSS with the separate credentials""" + params = self._sub_params.copy() + params.sub_id = self._acl_sub_id + with self.check( + "Create subscription query succeeds", [self._primary_pid] + ) as check: + acl_sub = self._dss_separate_creds.upsert_subscription( + **params, + ) + self.record_query(acl_sub) + if not acl_sub.success: + check.record_failed( + "Subscription creation with separate credentials failed", + details=f"Subscription creation failed with status code {acl_sub.status_code} when attempted " + f"with separate credentials: {acl_sub.error_message}", + query_timestamps=[acl_sub.request.timestamp], + ) + self._current_acl_sub = acl_sub.subscription + + def _step_test_delete_sub_with_separate_creds(self): + """Check we can't delete the subscription created with separate credentials with the main credentials. + This is to confirm that the manager of the subscription is properly synced. + Note that if the separate credentials are for the same subject as the main one, the checks are skipped. + """ + + if not self._credentials_are_different(): + self.record_note( + "manager_check", + "Skipping manager synchronization check: " + "separate credentials have the same subscriber as the main ones.", + ) + return + + # For each secondary dss, try to delete the subscription using the main credentials: + for secondary_dss in self._dss_read_instances: + deleted_sub = secondary_dss.delete_subscription( + sub_id=self._acl_sub_id, sub_version=self._current_acl_sub.version + ) + self.record_query(deleted_sub) + with self.check( + "Subscription deletion with different non-managing credentials on secondary DSS fails", + [secondary_dss.participant_id], + ) as check: + if deleted_sub.status_code != 403: + check.record_failed( + "Subscription deletion with main credentials did not fail", + details=f"Subscription deletion with main credentials did not fail with the expected " + f"status code of 403; instead returned {deleted_sub.status_code}", + query_timestamps=[deleted_sub.request.timestamp], + ) + + def _credentials_are_different(self): + """ + Checks the auth adapters for the subscription (jwt 'sub' field) they used and returns False if they are the same. + Note that both adapters need to have been used at least once before this check can be performed, + otherwise they have no token available. + """ + return ( + self._dss_separate_creds.client.auth_adapter.get_sub() + != self._dss.client.auth_adapter.get_sub() + ) + def _test_delete_sub(self): deleted_sub = self._dss.delete_subscription( sub_id=self._sub_id, sub_version=self._current_subscription.version diff --git a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md index 36af08e5d3..acf4db5ef5 100644 --- a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md +++ b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.md @@ -62,6 +62,11 @@ Implemented ASTM SCD DSS: Subscription Synchronization + + DSS0210,1b + Implemented + ASTM SCD DSS: Subscription Synchronization + DSS0210,1c Implemented diff --git a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml index 87b3b1b6db..96f7ba5ab1 100644 --- a/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml +++ b/monitoring/uss_qualifier/suites/astm/utm/dss_probing.yaml @@ -48,6 +48,7 @@ actions: other_instances: all_dss_instances id_generator: id_generator planning_area: planning_area + second_utm_auth: second_utm_auth - test_scenario: scenario_type: scenarios.astm.utm.dss.CRDBAccess resources: diff --git a/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md b/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md index 2a046e4d3e..5a51be5bcb 100644 --- a/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md +++ b/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md @@ -80,6 +80,11 @@ Implemented ASTM SCD DSS: Subscription Synchronization + + DSS0210,1b + Implemented + ASTM SCD DSS: Subscription Synchronization + DSS0210,1c Implemented diff --git a/monitoring/uss_qualifier/suites/faa/uft/message_signing.md b/monitoring/uss_qualifier/suites/faa/uft/message_signing.md index e0ae0dd8ed..1f67ce7f6f 100644 --- a/monitoring/uss_qualifier/suites/faa/uft/message_signing.md +++ b/monitoring/uss_qualifier/suites/faa/uft/message_signing.md @@ -63,6 +63,11 @@ Implemented ASTM SCD DSS: Subscription Synchronization + + DSS0210,1b + Implemented + ASTM SCD DSS: Subscription Synchronization + DSS0210,1c Implemented diff --git a/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md b/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md index 1bec59228b..4456de2c86 100644 --- a/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md +++ b/monitoring/uss_qualifier/suites/interuss/dss/all_tests.md @@ -448,6 +448,11 @@ Implemented ASTM SCD DSS: Subscription Synchronization + + DSS0210,1b + Implemented + ASTM SCD DSS: Subscription Synchronization + DSS0210,1c Implemented diff --git a/monitoring/uss_qualifier/suites/uspace/flight_auth.md b/monitoring/uss_qualifier/suites/uspace/flight_auth.md index b6fedb9162..84f2d1a2e4 100644 --- a/monitoring/uss_qualifier/suites/uspace/flight_auth.md +++ b/monitoring/uss_qualifier/suites/uspace/flight_auth.md @@ -64,6 +64,11 @@ Implemented ASTM SCD DSS: Subscription Synchronization + + DSS0210,1b + Implemented + ASTM SCD DSS: Subscription Synchronization + DSS0210,1c Implemented diff --git a/monitoring/uss_qualifier/suites/uspace/required_services.md b/monitoring/uss_qualifier/suites/uspace/required_services.md index 385a0ad6ad..ea8258d712 100644 --- a/monitoring/uss_qualifier/suites/uspace/required_services.md +++ b/monitoring/uss_qualifier/suites/uspace/required_services.md @@ -499,6 +499,11 @@ Implemented ASTM SCD DSS: Subscription Synchronization + + DSS0210,1b + Implemented + ASTM SCD DSS: Subscription Synchronization + DSS0210,1c Implemented