diff --git a/.gitignore b/.gitignore index fe61f0a7..d96f9064 100644 --- a/.gitignore +++ b/.gitignore @@ -147,3 +147,4 @@ typescript !.gitattributes !.gitignore !.gitkeep +.envrc diff --git a/src/pds/ingress/authorizer/index.js b/src/pds/ingress/authorizer/index.js index 8afaf206..856f8463 100644 --- a/src/pds/ingress/authorizer/index.js +++ b/src/pds/ingress/authorizer/index.js @@ -60,11 +60,16 @@ exports.handler = async(event, _context, callback) => { return; } - if (groups.includes(request_group)) { + // The UserGroup header may contain multiple comma-separated group names (e.g. for SBN which + // has two orgs). Authorize if the user belongs to ANY of the permitted groups for the node. + let permitted_groups = request_group.split(',').map(g => g.trim()); + let authorized = permitted_groups.some(g => groups.includes(g)); + + if (authorized) { console.log("VALID TOKEN, ALLOW!!"); callback(null, generatePolicy('user', 'Allow', event.methodArn)); } else { - console.log(`Invalid request group. User belongs to: [${groups}], but requested: ${request_group}`); + console.log(`Invalid request group. User belongs to: [${groups}], but requested: [${permitted_groups}]`); callback("Unauthorized"); } }; diff --git a/src/pds/ingress/client/pds_ingress_client.py b/src/pds/ingress/client/pds_ingress_client.py index 57c54d7a..e3b067c3 100644 --- a/src/pds/ingress/client/pds_ingress_client.py +++ b/src/pds/ingress/client/pds_ingress_client.py @@ -457,7 +457,7 @@ def request_batch_for_ingress(request_batch, batch_index, node_id, force_overwri params = {"node": node_id, "node_name": NodeUtil.node_id_to_long_name[node_id]} headers = { "Authorization": BEARER_TOKEN, - "UserGroup": NodeUtil.node_id_to_group_name(node_id), + "UserGroup": ",".join(NodeUtil.node_id_to_group_names(node_id)), "ForceOverwrite": str(int(force_overwrite)), "ClientVersion": __version__, "content-type": "application/json", diff --git a/src/pds/ingress/client/pds_status_client.py b/src/pds/ingress/client/pds_status_client.py index 847530ca..d509ae20 100644 --- a/src/pds/ingress/client/pds_status_client.py +++ b/src/pds/ingress/client/pds_status_client.py @@ -143,7 +143,7 @@ def main(args): params = {"node": args.node, "node_name": NodeUtil.node_id_to_long_name[args.node]} headers = { "Authorization": bearer_token, - "UserGroup": NodeUtil.node_id_to_group_name(args.node), + "UserGroup": ",".join(NodeUtil.node_id_to_group_names(args.node)), "ForceOverwrite": "1", # Likely we'll repeat requests using same manifest file names, so always overwrite "ClientVersion": __version__, "content-type": "application/json", @@ -184,7 +184,7 @@ def main(args): headers = { "Authorization": bearer_token, - "UserGroup": NodeUtil.node_id_to_group_name(args.node), + "UserGroup": ",".join(NodeUtil.node_id_to_group_names(args.node)), "ClientVersion": __version__, "content-type": "application/json", "x-amz-docs-region": api_gateway_region, diff --git a/src/pds/ingress/util/log_util.py b/src/pds/ingress/util/log_util.py index c062b94a..f02a46b9 100644 --- a/src/pds/ingress/util/log_util.py +++ b/src/pds/ingress/util/log_util.py @@ -501,7 +501,7 @@ def send_log_events_to_cloud_watch(self, log_events): headers = { "Authorization": self.bearer_token, - "UserGroup": NodeUtil.node_id_to_group_name(self.node_id), + "UserGroup": ",".join(NodeUtil.node_id_to_group_names(self.node_id)), "content-type": "application/json", "x-amz-docs-region": api_gateway_region, } @@ -522,7 +522,7 @@ def send_log_events_to_cloud_watch(self, log_events): payload["logEvents"] = log_events headers = { "Authorization": self.bearer_token, - "UserGroup": NodeUtil.node_id_to_group_name(self.node_id), + "UserGroup": ",".join(NodeUtil.node_id_to_group_names(self.node_id)), "content-type": "application/json", "x-amz-docs-region": api_gateway_region, } diff --git a/src/pds/ingress/util/node_util.py b/src/pds/ingress/util/node_util.py index 307e44d9..82be73e3 100644 --- a/src/pds/ingress/util/node_util.py +++ b/src/pds/ingress/util/node_util.py @@ -23,15 +23,28 @@ class NodeUtil: "sbn": "Small Bodies", } + node_id_to_cognito_groups = { + "atm": ["PDS_ATM_USERS"], + "eng": ["PDS_ENG_USERS"], + "geo": ["PDS_GEO_USERS"], + "img": ["PDS_IMG_USERS"], + "naif": ["PDS_NAIF_USERS"], + "ppi": ["PDS_PPI_USERS"], + "rs": ["PDS_RS_USERS"], + "rms": ["PDS_RMS_USERS"], + "sbn": ["PDS_SBN_USERS", "PDS_SBNUMD_USERS"], + } + @classmethod def permissible_node_ids(cls): """Returns a list of the Node IDs accepted by the Ingress client""" return cls.node_id_to_long_name.keys() @classmethod - def node_id_to_group_name(cls, node_id): - """Returns the Cognito group name for the given node ID""" - if node_id.lower() not in cls.node_id_to_long_name.keys(): + def node_id_to_group_names(cls, node_id): + """Returns all Cognito group names permitted for the given node ID""" + if node_id.lower() not in cls.node_id_to_cognito_groups: raise ValueError(f'Unknown node ID "{node_id}"') - return f"PDS_{node_id.upper()}_USERS" + return cls.node_id_to_cognito_groups[node_id.lower()] + diff --git a/tests/pds/ingress/util/test_log_util.py b/tests/pds/ingress/util/test_log_util.py index 61f7ba59..7662e40c 100644 --- a/tests/pds/ingress/util/test_log_util.py +++ b/tests/pds/ingress/util/test_log_util.py @@ -131,7 +131,7 @@ def requests_post_patch(url, data=None, json=None, **kwargs): self.assertIn("Authorization", headers) self.assertIn("UserGroup", headers) self.assertEqual(headers["Authorization"], "Bearer faketoken") - self.assertEqual(headers["UserGroup"], NodeUtil.node_id_to_group_name("eng")) + self.assertEqual(headers["UserGroup"], ",".join(NodeUtil.node_id_to_group_names("eng"))) response = requests.Response() response.status_code = HTTPStatus.OK