Only in .: .DS_Store
diff -r ./app_utils.py /tmp/foursight-core/foursight_core/app_utils.py
11,14d10
< from http.cookies import SimpleCookie
< import pkg_resources
< import platform
< import pytz
16d11
< import socket
18,19d12
< import time
< import types
24,32c17
< from dcicutils.env_utils import (
<     EnvUtils,
<     full_env_name,
<     get_foursight_bucket,
<     get_foursight_bucket_prefix,
<     infer_foursight_from_env,
<     public_env_name,
<     short_env_name,
< )
---
> from dcicutils.env_utils import full_env_name, public_env_name, short_env_name
34,36c19
< from dcicutils.misc_utils import get_error_message
< from dcicutils.obfuscation_utils import obfuscate_dict
< from dcicutils.secrets_utils import (get_identity_name, get_identity_secrets)
---
> # from dcicutils.obfuscation_utils import obfuscate_dict
38d20
< from .identity import apply_identity_globally
57,67d38
<     # Define in subclass.
<     APP_PACKAGE_NAME = None
< 
<     def get_app_version(self):
<         return pkg_resources.get_distribution(self.APP_PACKAGE_NAME).version
< 
<     # dmichaels/2022-07-20/C4-826: Apply identity globally.
<     # NOTE (2022-08-24): No longer call from the top-level here (not polite);
<     # rather call from (AppUtils) sub-classes in foursight-cgap and foursight.
<     # apply_identity_globally()
< 
94c65
<         self.init_load_time = self.get_load_time()
---
> 
105,116d75
<         self.portal_url = None
<         self.auth0_client_id = None
<         self.user_record = None
<         self.lambda_last_modified = None
< 
<     @staticmethod
<     def note_non_fatal_error_for_ui_info(error_object, calling_function):
<         if isinstance(calling_function, types.FunctionType):
<             calling_function = calling_function.__name__
<         logger.warn(f"Non-fatal error in function ({calling_function})."
<                     f" Missing information via this function used only for Foursight UI display."
<                     f" Underlying error: {get_error_message(error_object)}")
173,254d131
<     def is_running_locally(self, request_dict) -> bool:
<         return request_dict.get('context', {}).get('identity', {}).get('sourceIp', '') == "127.0.0.1"
< 
<     def get_logged_in_user_info(self, environ: str, request_dict: dict) -> str:
<         email_address = ""
<         email_verified = ""
<         first_name = ""
<         last_name = ""
<         issuer = ""
<         subject = ""
<         audience = ""
<         issued_time = ""
<         expiration_time = ""
<         jwt_decoded = ""
<         try:
<             jwt_decoded = self.get_decoded_jwt_token(environ, request_dict)
<             if jwt_decoded:
<                 email_address = jwt_decoded.get("email")
<                 email_verified = jwt_decoded.get("email_verified")
<                 issuer = jwt_decoded.get("iss")
<                 if issuer:
<                     name = jwt_decoded.get(issuer + "name")
<                     if name:
<                         first_name = name.get("name_first")
<                         last_name = name.get("name_last")
<                 subject = jwt_decoded.get("sub")
<                 audience = jwt_decoded.get("aud")
<                 issued_time = self.convert_time_t_to_useastern_datetime(jwt_decoded.get("iat"))
<                 expiration_time = self.convert_time_t_to_useastern_datetime(jwt_decoded.get("exp"))
<         except Exception as e:
<             self.note_non_fatal_error_for_ui_info(e, 'get_logged_in_user_info')
<         return {"email_address": email_address,
<                 "email_verified": email_verified,
<                 "first_name": first_name,
<                 "last_name": last_name,
<                 "issuer": issuer,
<                 "subject": subject,
<                 "audience": audience,
<                 "issued_time": issued_time,
<                 "expiration_time": expiration_time,
<                 "jwt": jwt_decoded}
< 
<     def get_portal_url(self, env_name: str) -> str:
<         if not self.portal_url:
<             try:
<                 environment_and_bucket_info = \
<                     self.environment.get_environment_and_bucket_info(env_name, self.stage.get_stage())
<                 self.portal_url = environment_and_bucket_info.get("fourfront")
<                 return self.portal_url
<             except Exception as e:
<                 logger.error(f"Error determining portal URL: {e}")
<                 raise e
<         return self.portal_url
< 
<     def get_auth0_client_id(self, env_name: str) -> str:
<         auth0_client_id = os.environ.get("CLIENT_ID")
<         if not auth0_client_id:
<             # TODO: Confirm that we do not actually need to do this.
<             # Just in case. We should already have this value from the GAC.
<             # But Will said get it from the portal (was hardcoded in the template),
<             # so I had written code to do that; just call as fallback for now.
<             auth0_client_id = self.get_auth0_client_id_from_portal(env_name)
<         return auth0_client_id
< 
<     def get_auth0_client_id_from_portal(self, env_name: str) -> str:
<         logger.warn(f"Fetching Auth0 client ID from portal.")
<         portal_url = self.get_portal_url(env_name)
<         auth0_config_url = portal_url + "/auth0_config?format=json"
<         if not self.auth0_client_id:
<             try:
<                 response = requests.get(auth0_config_url).json()
<                 self.auth0_client_id = response.get("auth0Client")
<             except Exception as e:
<                 # TODO: Fallback behavior to old hardcoded value (previously in templates/header.html).
<                 self.auth0_client_id = "DPxEwsZRnKDpk0VfVAxrStRKukN14ILB"
<                 logger.error(f"Error fetching Auth0 client ID from portal ({auth0_config_url}); using default value: {e}")
<         logger.warn(f"Done fetching Auth0 client ID from portal ({auth0_config_url}): {self.auth0_client_id}")
<         return self.auth0_client_id
< 
<     def get_auth0_secret(self, env_name: str) -> str:
<         return os.environ.get("CLIENT_SECRET")
< 
272c149,150
<         if self.is_running_locally(request_dict):
---
>         src_ip = request_dict.get('context', {}).get('identity', {}).get('sourceIp', '')
>         if src_ip == '127.0.0.1':
274,275c152,155
<         jwt_decoded = self.get_decoded_jwt_token(env, request_dict)
<         if jwt_decoded:
---
>         token = self.get_jwt(request_dict)
>         auth0_client = os.environ.get('CLIENT_ID', None)
>         auth0_secret = os.environ.get('CLIENT_SECRET', None)
>         if auth0_client and auth0_secret and token:
278a159,160
>                 # leeway accounts for clock drift between us and auth0
>                 payload = jwt.decode(token, auth0_secret, audience=auth0_client, leeway=30)
280c162
<                     user_res = ff_utils.get_metadata('users/' + jwt_decoded.get('email').lower(),
---
>                     user_res = ff_utils.get_metadata('users/' + payload.get('email').lower(),
282,397c164,166
<                     logger.warn("foursight_core.check_authorization: env_info ...")
<                     logger.warn(env_info)
<                     logger.warn("foursight_core.check_authorization: user_res ...")
<                     logger.warn(user_res)
<                     #
<                     # The following tries to referent 'groups' in the JSON returned by the get_metadata call above,
<                     # but there is no such element. The JSON we get looks like this:
<                     # {
<                     #     "email": "david_michaels@hms.harvard.edu",
<                     #     "status": "current",
<                     #     "timezone": "US/Eastern",
<                     #     "last_name": "Michaels",
<                     #     "first_name": "David",
<                     #     "date_created": "2022-05-09T19:29:04.053460+00:00",
<                     #     "subscriptions": [],
<                     #     "schema_version": "1",
<                     #     "was_unauthorized": true,
<                     #     "@id": "/users/04bf103b-5a61-4ff9-96ac-e8d104f8b7ee/",
<                     #     "@type": [
<                     #         "User",
<                     #         "Item"
<                     #     ],
<                     #     "uuid": "04bf103b-5a61-4ff9-96ac-e8d104f8b7ee",
<                     #     "principals_allowed": {
<                     #         "view": [
<                     #             "group.admin",
<                     #             "group.read-only-admin",
<                     #             "remoteuser.EMBED",
<                     #             "remoteuser.INDEXER",
<                     #             "userid.04bf103b-5a61-4ff9-96ac-e8d104f8b7ee"
<                     #         ],
<                     #         "edit": [
<                     #             "group.admin",
<                     #             "userid.04bf103b-5a61-4ff9-96ac-e8d104f8b7ee"
<                     #         ]
<                     #     },
<                     #     "display_title": "David Michaels",
<                     #     "external_references": [],
<                     #     "title": "David Michaels",
<                     #     "contact_email": "david_michaels@hms.harvard.edu"
<                     # }
<                     #
<                     # Looks like we want to check principals_allowed/{view,edit} for 'group.admin' ...
<                     # For now only check the 'groups' element if present otherwise ignore that part of the check below.
<                     #
<                     # And BTW here is another sample record from the same source:
<                     # {
<                     #       "lab": "/labs/4dn-dcic-lab/",
<                     #       "tags": [
<                     #            "skip_oh_synchronization"
<                     #        ],
<                     #        "email": "kent_pitman@hms.harvard.edu",
<                     #        "groups": [
<                     #            "admin"
<                     #        ],
<                     #        "status": "current",
<                     #        "timezone": "US/Eastern",
<                     #        "job_title": "Software Developer",
<                     #        "last_name": "Pitman",
<                     #        "first_name": "Kent",
<                     #        "submits_for": [
<                     #            "/labs/4dn-dcic-lab/"
<                     #        ],
<                     #        "date_created": "2020-01-06T21:24:22.260908+00:00",
<                     #        "submitted_by": "/users/1a12362f-4eb6-4a9c-8173-776667226988/",
<                     #        "last_modified": {
<                     #            "modified_by": "/users/986b362f-4eb6-4a9c-8173-3ab267307e3a/",
<                     #            "date_modified": "2021-03-04T21:20:45.428320+00:00"
<                     #        },
<                     #        "subscriptions": [
<                     #            {
<                     #                "url": "?submitted_by.uuid=1a12362f-4eb6-4a9c-8173-776667226&sort=-date_created",
<                     #                "title": "My submissions"
<                     #            },
<                     #            {
<                     #                "url": "?lab.uuid=828cd4fe-ebb0-4b36-a94a-d2e3a36cc989&sort=-date_created",
<                     #                "title": "Submissions for my lab"
<                     #            }
<                     #        ],
<                     #        "schema_version": "1",
<                     #        "viewing_groups": [
<                     #            "4DN"
<                     #        ],
<                     #        "@id": "/users/1a12362f-4eb6-4a9c-8173-776667226962/",
<                     #        "@type": [
<                     #            "User",
<                     #            "Item"
<                     #        ],
<                     #        "uuid": "1a12362f-4eb6-4a9c-8173-776667226962",
<                     #        "principals_allowed": {
<                     #            "view": [
<                     #                "group.admin",
<                     #                "group.read-only-admin",
<                     #                "remoteuser.EMBED",
<                     #                "remoteuser.INDEXER",
<                     #                "userid.1a12362f-4eb6-4a9c-8173-776667226962"
<                     #            ],
<                     #            "edit": [
<                     #                "group.admin",
<                     #                "userid.1a12362f-4eb6-4a9c-8173-776667226962"
<                     #             ]
<                     #        },
<                     #        "display_title": "Kent Pitman",
<                     #        "external_references": [],
<                     #        "title": "Kent Pitman",
<                     #        "contact_email": "kent_pitman@hms.harvard.edu"
<                     # }
<                     #
<                     # if not ('admin' in user_res.get('groups') and payload.get('email_verified')):
<                     #
<                     groups = user_res.get('groups')
<                     if not groups:
<                         logger.warn("foursight_core.check_authorization: No 'groups' element for user record! Returning False.")
<                         return False
<                     if not ((not groups or 'admin' in groups) and jwt_decoded.get('email_verified')):
<                         logger.error("foursight_core.check_authorization: Returning False")
---
>                     logger.error(env_info)
>                     logger.error(user_res)
>                     if not ('admin' in user_res['groups'] and payload.get('email_verified')):
400,401d168
<                     self.user_record = user_res
<                 logger.warn("foursight_core.check_authorization: Returning True")
403,406c170,171
<             except Exception as e:
<                 logger.error("foursight_core.check_authorization: Exception on check_authorization")
<                 logger.error(e)
<         logger.error("foursight_core.check_authorization: Returning False ")
---
>             except Exception:
>                 pass
409c174,175
<     def auth0_callback(self, request, env):
---
>     @classmethod
>     def auth0_callback(cls, request, env):
411c177
<         domain, context = self.get_domain_and_context(req_dict)
---
>         domain, context = cls.get_domain_and_context(req_dict)
415,430c181,184
< 
< #       for cookie in cookies.split(';'):
< #           name, val = cookie.strip().split('=')
< #           if name == 'redir':
< #               redir_url = val
<         try:
<             simple_cookies = SimpleCookie()
<             simple_cookies.load(cookies)
<             simple_cookies = {k: v.value for k, v in simple_cookies.items()}
<             redir_url_cookie = simple_cookies.get("redir")
<             if redir_url_cookie:
<                 redir_url = redir_url_cookie
<         except Exception as e:
<             print("Exception loading cookies: {cookies}")
<             print(e)
< 
---
>         for cookie in cookies.split(';'):
>             name, val = cookie.strip().split('=')
>             if name == 'redir':
>                 redir_url = val
434c188
<             return self.forbidden_response()
---
>             return cls.forbidden_response()
436,437c190,191
<         auth0_client = self.get_auth0_client_id(env)
<         auth0_secret = self.get_auth0_secret(env)
---
>         auth0_client = os.environ.get('CLIENT_ID', None)
>         auth0_secret = os.environ.get('CLIENT_SECRET', None)
439c193,194
<             return Response(status_code=301, body=json.dumps(resp_headers), headers=resp_headers)
---
>             return Response(status_code=301, body=json.dumps(resp_headers),
>                             headers=resp_headers)
449c204
<         res = requests.post(self.OAUTH_TOKEN_URL, data=json_payload, headers=headers)
---
>         res = requests.post(cls.OAUTH_TOKEN_URL, data=json_payload, headers=headers)
460c215,216
<     def get_jwt_token(self, request_dict) -> str:
---
>     @classmethod
>     def get_jwt(cls, request_dict):
475,487d230
<     def get_decoded_jwt_token(self, env_name: str, request_dict) -> dict:
<         try:
<             jwt_token = self.get_jwt_token(request_dict)
<             if not jwt_token:
<                 return None
<             auth0_client_id = self.get_auth0_client_id(env_name)
<             auth0_secret = self.get_auth0_secret(env_name)
<             # leeway accounts for clock drift between us and auth0
<             return jwt.decode(jwt_token, auth0_secret, audience=auth0_client_id, leeway=30)
<         except:
<             logger.warn(f"foursight_core: Exception decoding JWT token: {jwt_token}")
<             return None
< 
495c238,239
<     def get_domain_and_context(self, request_dict):
---
>     @classmethod
>     def get_domain_and_context(cls, request_dict):
591,775d334
<     def sorted_dict(self, dictionary: dict) -> dict:
<         """
<         Returns the given dictionary sorted by key values (yes, dictionaries are ordered as of Python 3.7).
<         If the given value is not a dictionary it will be coerced to one.
<         :param dictionary: Dictionary to sort.
<         :return: Given dictionary sorted by key value.
<         """
<         dictionary = dict(dictionary)
<         return {key: dictionary[key] for key in sorted(dictionary.keys(), key=lambda key: key.lower())}
< 
<     def get_aws_account_number(self) -> dict:
<         try:
<             caller_identity = boto3.client("sts").get_caller_identity()
<             return caller_identity["Account"]
<         except Exception as e:
<             self.note_non_fatal_error_for_ui_info(e, 'get_aws_account_number')
<             return None
< 
<     def get_obfuscated_credentials_info(self, env_name: str) -> dict:
<         try:
<             session = boto3.session.Session()
<             credentials = session.get_credentials()
<             access_key_id = credentials.access_key
<             region_name = session.region_name
<             caller_identity = boto3.client("sts").get_caller_identity()
<             user_arn = caller_identity["Arn"]
<             account_number = caller_identity["Account"]
<             auth0_client_id = self.get_auth0_client_id(env_name)
<             credentials_info = {
<                 "AWS Account Number:": account_number,
<                 "AWS User ARN:": user_arn,
<                 "AWS Access Key ID:": access_key_id,
<                 "AWS Region Name:": region_name,
<                 "Auth0 Client ID:": auth0_client_id
<             }
<             return credentials_info
<         except Exception as e:
<             self.note_non_fatal_error_for_ui_info(e, 'get_obfuscated_credentials_info')
<             return {}
< 
<     def convert_utc_datetime_to_useastern_datetime(self, t) -> str:
<         """
<         Converts the given UTC datetime object or string into a US/Eastern datetime string
<         and returns its value in a form that looks like 2022-08-22 13:25:34 EDT.
<         If the argument is a string it is ASSUMED to have a value which looks
<         like 2022-08-22T14:24:49.000+0000; this is the datetime string format
<         we get from AWS via boto3 (e.g. for a lambda last-modified value).
< 
<         :param t: UTC datetime object or string value.
<         :return: US/Eastern datetime string (e.g.: 2022-08-22 13:25:34 EDT).
<         """
<         try:
<             if isinstance(t, str):
<                 t = datetime.datetime.strptime(t, "%Y-%m-%dT%H:%M:%S.%f%z")
<             t = t.replace(tzinfo=pytz.UTC).astimezone(pytz.timezone("US/Eastern"))
<             return t.strftime("%Y-%m-%d %H:%M:%S %Z")
<         except Exception as e:
<             self.note_non_fatal_error_for_ui_info(e, 'convert_utc_datetime_to_useastern_datetime')
<             return ""
< 
<     def convert_time_t_to_useastern_datetime(self, time_t: int) -> str:
<         """
<         Converts the given "epoch" time (seconds since 1970-01-01T00:00:00Z)
<         integer value to a US/Eastern datetime string and returns its value
<         in a form that looks like 2022-08-22 13:25:34 EDT.
< 
<         :param time_t: Epoch time value (i.e. seconds since 1970-01-01T00:00:00Z)
<         :return: US/Eastern datetime string (e.g.: 2022-08-22 13:25:34 EDT).
<         """
<         try:
<             if not isinstance(time_t, int):
<                 return ""
<             t = datetime.datetime.fromtimestamp(time_t, tz=pytz.UTC)
<             return self.convert_utc_datetime_to_useastern_datetime(t)
<         except Exception as e:
<             self.note_non_fatal_error_for_ui_info(e, 'convert_time_t_to_useastern_datetime')
<             return ""
< 
<     def ping_elasticsearch(self, env_name: str) -> bool:
<         logger.warn(f"foursight_core: Pinging ElasticSearch: {self.host}")
<         try:
<             response = self.init_connection(env_name).connections["es"].test_connection()
<             logger.warn(f"foursight_core: Done pinging ElasticSearch: {self.host}")
<             return response
<         except Exception as e:
<             logger.warn(f"Exception pinging ElasticSearch ({self.host}): {e}")
<             return False
< 
<     def ping_portal(self, env_name: str) -> bool:
<         portal_url = ""
<         try:
<             portal_url = self.get_portal_url(env_name)
<             logger.warn(f"foursight_core: Pinging portal: {portal_url}")
<             response = requests.get(portal_url, timeout=4)
<             logger.warn(f"foursight_core: Done pinging portal: {portal_url}")
<             return (response.status_code == 200)
<         except Exception as e:
<             logger.warn(f"foursight_core: Exception pinging portal ({portal_url}): {e}")
<             return False
< 
<     def ping_sqs(self) -> bool:
<         sqs_url = ""
<         try:
<             sqs_url = self.sqs.get_sqs_queue().url
<             logger.warn(f"foursight_core: Pinging SQS: {sqs_url}")
<             sqs_attributes = self.sqs.get_sqs_attributes(sqs_url)
<             logger.warn(f"foursight_core: Done pinging SQS: {sqs_url}")
<             return (sqs_attributes is not None)
<         except Exception as e:
<             logger.warn(f"Exception pinging SQS ({sqs_url}): {e}")
<             return False
< 
<     def reload_lambda(self, lambda_name: str = None) -> bool:
<         """
<         Experimental.
<         Reloads the lambda code for the given lambda name. We do this by making an innocuous change
<         to it, namely, by adding/removing a trailing dot to its description. This causes the lambda
<         to be reloaded, however this also changes its last modified date, which we would also like to
<         to accurately get (get_lambda_last_modified), so we store its original value in a lambda tag,
<         the updating of which does not update the modified time; so the code (get_lambda_last_modified)
<         to get the last modified time of the lambda needs to look first at this tag and take that
<         if it exists before looking at the real last modified time.
<         """
<         if not lambda_name or lambda_name.lower() == "current":
<             lambda_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME")
<             if not lambda_name:
<                 return False
<         try:
<             boto_lambda = boto3.client("lambda")
<             lambda_info = boto_lambda.get_function(FunctionName=lambda_name)
<             if lambda_info:
<                 lambda_arn = lambda_info["Configuration"]["FunctionArn"]
<                 lambda_tags = boto_lambda.list_tags(Resource=lambda_arn)["Tags"]
<                 lambda_last_modified_tag = lambda_tags.get("last_modified")
<                 if not lambda_last_modified_tag:
<                     lambda_last_modified = lambda_info["Configuration"]["LastModified"]
<                     boto_lambda.tag_resource(Resource=lambda_arn, Tags={"last_modified": lambda_last_modified})
<                 lambda_description = lambda_info["Configuration"]["Description"]
<                 if not lambda_description:
<                     lambda_description = "Reload"
<                 else:
<                     if lambda_description.endswith("."):
<                         lambda_description = lambda_description[:-1]
<                     else:
<                         lambda_description = lambda_description + "."
<                 logger.warn(f"Reloading lambda: {lambda_name}")
<                 boto_lambda.update_function_configuration(FunctionName=lambda_name, Description=lambda_description)
<                 logger.warn(f"Reloaded lambda: {lambda_name}")
<         except Exception as e:
<             logger.warn(f"Error reloading lambda ({lambda_name}): {e}")
<         return False
< 
<     def get_lambda_last_modified(self, lambda_name: str = None) -> str:
<         """
<         Returns the last modified time for the given lambda name.
<         See comments in reload_lambda on this.
<         """
<         lambda_current = False
<         if not lambda_name or lambda_name.lower() == "current":
<             lambda_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME")
<             if not lambda_name:
<                 return None
<             lambda_current = True
<         if lambda_current:
<             if self.lambda_last_modified:
<                 return self.lambda_last_modified
<         try:
<             boto_lambda = boto3.client("lambda")
<             lambda_info = boto_lambda.get_function(FunctionName=lambda_name)
<             if lambda_info:
<                 lambda_arn = lambda_info["Configuration"]["FunctionArn"]
<                 lambda_tags = boto_lambda.list_tags(Resource=lambda_arn)["Tags"]
<                 lambda_last_modified_tag = lambda_tags.get("last_modified")
<                 if lambda_last_modified_tag:
<                     lambda_last_modified = self.convert_utc_datetime_to_useastern_datetime(lambda_last_modified_tag)
<                 else:
<                     lambda_last_modified = lambda_info["Configuration"]["LastModified"]
<                     lambda_last_modified = self.convert_utc_datetime_to_useastern_datetime(lambda_last_modified)
<                 if lambda_current:
<                     self.lambda_last_modified = lambda_last_modified
<                 return lambda_last_modified
<         except Exception as e:
<             logger.warn(f"Error getting lambda ({lambda_name}) last modified time: {e}")
<         return None
< 
840,849c399
<     def get_unique_annotated_environment_names(self):
<         unique_environment_names = self.environment.list_unique_environment_names()
<         unique_annotated_environment_names = [
<             {"name": env,
<              "short": short_env_name(env),
<              "full": full_env_name(env),
<              "foursight": infer_foursight_from_env(envname=env)} for env in unique_environment_names]
<         return sorted(unique_annotated_environment_names, key=lambda key: key["full"])
< 
<     def view_foursight(self, request, environ, is_admin=False, domain="", context="/"):
---
>     def view_foursight(self, environ, is_admin=False, domain="", context="/"):
902d451
<         request_dict = request.to_dict()
904,906d452
<             request=request,
<             version=self.get_app_version(),
<             package=self.APP_PACKAGE_NAME,
908,909d453
<             env_short=short_env_name(environ),
<             env_full=full_env_name(environ),
913,914d456
<             init_load_time=self.init_load_time,
<             lambda_deployed_time=self.get_lambda_last_modified(),
916,919d457
<             is_running_locally=self.is_running_locally(request_dict),
<             logged_in_as=self.get_logged_in_user_info(environ, request_dict),
<             auth0_client_id=self.get_auth0_client_id(environ),
<             aws_account_number=self.get_aws_account_number(),
922d459
<             environments=self.get_unique_annotated_environment_names(),
931,1136c468
<     def view_reload_lambda(self, request, environ, is_admin=False, domain="", context="/", lambda_name: str = None):
<         self.reload_lambda(lambda_name)
<         time.sleep(3)
<         resp_headers = {'Location': f"{context}info/{environ}"}
<         return Response(status_code=302, body=json.dumps(resp_headers), headers=resp_headers)
< 
<     # dmichaels/2020-08-01:
<     # Added /info/{environ} for debugging/troubleshooting purposes.
<     def view_info(self, request, environ, is_admin=False, domain="", context="/"):
<         """
<         Displays a /{environ}/info page containing sundry info about the running Foursight instance.
<         Any sensitive data is obfuscated. This is a protected route.
<         :param domain: Current FS domain, needed for Auth0 redirect.
<         :param context: Current context, usually "/api/" or "/".
<         :return: Response with html content.
<         """
< 
<         html_resp = Response('Foursight viewing suite')
<         html_resp.headers = {'Content-Type': 'text/html'}
<         template = self.jin_env.get_template('info.html')
<         # env_name = os.environ.get("ENV_NAME")
<         stage_name = self.stage.get_stage()
<         environment_names = {
<             "Environment Name:": environ,
<             "Environment Name (Full):": full_env_name(environ),
<             "Environment Name (Short):": short_env_name(environ),
<             "Environment Name (Public):": public_env_name(envname=environ),
<             "Environment Name (Foursight):": infer_foursight_from_env(envname=environ),
<             "Environment Name List:": sorted(self.environment.list_environment_names()),
<             "Environment Name List (Unique):": sorted(self.environment.list_unique_environment_names())
<         }
<         bucket_names = {
<             "Environment Bucket Name:": self.environment.get_env_bucket_name(),
<             "Foursight Bucket Name:": get_foursight_bucket(envname=environ, stage=stage_name),
<             "Foursight Bucket Prefix:": get_foursight_bucket_prefix()
<         }
<         gac_name = get_identity_name()
<         gac_values = self.sorted_dict(obfuscate_dict(get_identity_secrets()))
<         environment_and_bucket_info = self.sorted_dict(obfuscate_dict(
<                                         self.environment.get_environment_and_bucket_info(environ, stage_name)))
<         declared_data = self.sorted_dict(EnvUtils.declared_data())
<         dcicutils_version = pkg_resources.get_distribution('dcicutils').version
<         foursight_core_version = pkg_resources.get_distribution('foursight-core').version
<         versions = {
<             # TODO/dmichaels/2022-08-04: Get the "Foursight-CGAP" value some other way,
<             # as the package might not be "Foursight-CGAP" (might be for example just "Foursight").
<             f"{self.html_main_title}:": self.get_app_version(),
<             "Foursight-Core:": foursight_core_version,
<             "DCIC-Utils:": dcicutils_version,
<             "Python:": platform.python_version()
<         }
<         resources = {
<             "Foursight Server:": socket.gethostname(),
<             "Portal Server:": self.get_portal_url(environ),
<             "ElasticSearch Server:": self.host,
<             "RDS Server:": os.environ["RDS_HOSTNAME"],
<             "SQS Server:": self.sqs.get_sqs_queue().url,
<         }
<         aws_credentials = self.get_obfuscated_credentials_info(environ)
<         aws_account_number = aws_credentials.get("AWS Account Number:")
<         os_environ = self.sorted_dict(obfuscate_dict(dict(os.environ)))
<         request_dict = request.to_dict()
< 
<         html_resp.body = template.render(
<             request=request,
<             version=self.get_app_version(),
<             package=self.APP_PACKAGE_NAME,
<             env=environ,
<             env_short=short_env_name(environ),
<             env_full=full_env_name(environ),
<             domain=domain,
<             context=context,
<             environments=self.get_unique_annotated_environment_names(),
<             stage=stage_name,
<             is_admin=is_admin,
<             is_running_locally=self.is_running_locally(request_dict),
<             logged_in_as=self.get_logged_in_user_info(environ, request_dict),
<             user_record=self.user_record,
<             auth0_client_id=self.get_auth0_client_id(environ),
<             aws_credentials=aws_credentials,
<             aws_account_number=aws_account_number,
<             main_title=self.html_main_title,
<             favicon=self.get_favicon(),
<             load_time=self.get_load_time(),
<             init_load_time=self.init_load_time,
<             lambda_deployed_time=self.get_lambda_last_modified(),
<             running_checks='0',
<             queued_checks='0',
<             environment_names=environment_names,
<             bucket_names=bucket_names,
<             environment_and_bucket_info=environment_and_bucket_info,
<             declared_data=declared_data,
<             identity_name=gac_name,
<             identity_secrets=gac_values,
<             resources=resources,
<             ping_portal=self.ping_portal(environ),
<             ping_elasticsearch=self.ping_elasticsearch(environ),
<             ping_sqs=self.ping_sqs(),
<             versions=versions,
<             os_environ=os_environ
<         )
<         html_resp.status_code = 200
<         return self.process_response(html_resp)
< 
<     def view_user(self, request, environ, is_admin=False, domain="", context="/", email=None):
<         html_resp = Response('Foursight viewing suite')
<         html_resp.headers = {'Content-Type': 'text/html'}
<         request_dict = request.to_dict()
<         stage_name = self.stage.get_stage()
<         users = []
<         for this_email in email.split(","):
<             try:
<                 this_user = ff_utils.get_metadata('users/' + this_email.lower(),
<                                                   ff_env=full_env_name(environ), add_on='frame=object')
<                 users.append({"email": this_email, "record": this_user})
<             except Exception as e:
<                 users.append({"email": this_email, "record": {"error": str(e)}})
<         template = self.jin_env.get_template('user.html')
<         html_resp.body = template.render(
<             request=request,
<             version=self.get_app_version(),
<             package=self.APP_PACKAGE_NAME,
<             env=environ,
<             env_short=short_env_name(environ),
<             env_full=full_env_name(environ),
<             domain=domain,
<             context=context,
<             environments=self.get_unique_annotated_environment_names(),
<             stage=stage_name,
<             is_admin=is_admin,
<             is_running_locally=self.is_running_locally(request_dict),
<             logged_in_as=self.get_logged_in_user_info(environ, request_dict),
<             users=users,
<             auth0_client_id=self.get_auth0_client_id(environ),
<             aws_account_number=self.get_aws_account_number(),
<             main_title=self.html_main_title,
<             favicon=self.get_favicon(),
<             load_time=self.get_load_time(),
<             init_load_time=self.init_load_time,
<             lambda_deployed_time=self.get_lambda_last_modified(),
<             running_checks='0',
<             queued_checks='0'
<         )
<         html_resp.status_code = 200
<         return self.process_response(html_resp)
< 
<     def view_users(self, request, environ, is_admin=False, domain="", context="/"):
<         html_resp = Response('Foursight viewing suite')
<         html_resp.headers = {'Content-Type': 'text/html'}
<         request_dict = request.to_dict()
<         stage_name = self.stage.get_stage()
<         users = []
<         # TODO: Support paging.
<         user_records = ff_utils.get_metadata('users/', ff_env=full_env_name(environ), add_on='frame=object&limit=10000')
<         for user_record in user_records["@graph"]:
<             last_modified = user_record.get("last_modified")
<             if last_modified:
<                 last_modified = last_modified.get("date_modified")
<             # TODO
<             # roles = []
<             # project_roles = user_record.get("project_roles")
<             # if project_roles:
<             #     role = role.get("date_modified")
<             #     roles.append({
<             #         "groups": groups,
<             #         "project_roles": project_roles,
<             #         "principals_view": principals_view,
<             #         "principals_edit": principals_edit
<             #     })
<             users.append({
<                 "email_address": user_record.get("email"),
<                 "first_name": user_record.get("first_name"),
<                 "last_name": user_record.get("last_name"),
<                 "uuid": user_record.get("uuid"),
<                 "modified": self.convert_utc_datetime_to_useastern_datetime(last_modified)})
<         users = sorted(users, key=lambda key: key["email_address"])
<         template = self.jin_env.get_template('users.html')
<         html_resp.body = template.render(
<             request=request,
<             version=self.get_app_version(),
<             package=self.APP_PACKAGE_NAME,
<             env=environ,
<             env_short=short_env_name(environ),
<             env_full=full_env_name(environ),
<             domain=domain,
<             context=context,
<             environments=self.get_unique_annotated_environment_names(),
<             stage=stage_name,
<             is_admin=is_admin,
<             is_running_locally=self.is_running_locally(request_dict),
<             logged_in_as=self.get_logged_in_user_info(environ, request_dict),
<             users=users,
<             auth0_client_id=self.get_auth0_client_id(environ),
<             aws_account_number=self.get_aws_account_number(),
<             main_title=self.html_main_title,
<             favicon=self.get_favicon(),
<             load_time=self.get_load_time(),
<             init_load_time=self.init_load_time,
<             lambda_deployed_time=self.get_lambda_last_modified(),
<             running_checks='0',
<             queued_checks='0'
<         )
<         html_resp.status_code = 200
<         return self.process_response(html_resp)
< 
<     def view_foursight_check(self, request, environ, check, uuid, is_admin=False, domain="", context="/"):
---
>     def view_foursight_check(self, environ, check, uuid, is_admin=False, domain="", context="/"):
1174d505
<         request_dict = request.to_dict()
1176,1178d506
<             request=request,
<             version=self.get_app_version(),
<             package=self.APP_PACKAGE_NAME,
1180,1181d507
<             env_short=short_env_name(environ),
<             env_full=full_env_name(environ),
1185,1186c511
<             init_load_time=self.init_load_time,
<             lambda_deployed_time=self.get_lambda_last_modified(),
---
>             is_admin=is_admin,
1189,1194d513
<             environments=self.get_unique_annotated_environment_names(),
<             is_admin=is_admin,
<             is_running_locally=self.is_running_locally(request_dict),
<             logged_in_as=self.get_logged_in_user_info(environ, request_dict),
<             auth0_client_id=self.get_auth0_client_id(environ),
<             aws_account_number=self.get_aws_account_number(),
1212c531
<         return ''.join([str(ts_local.date()), ' ', str(ts_local.time()), ' ', str(ts_local.tzname())])
---
>         return ''.join([str(ts_local.date()), ' at ', str(ts_local.time()), ' (', str(ts_local.tzname()), ')'])
1298c617
<     def view_foursight_history(self, request, environ, check, start=0, limit=25, is_admin=False,
---
>     def view_foursight_history(self, environ, check, start=0, limit=25, is_admin=False,
1328d646
<         request_dict = request.to_dict()
1330,1332d647
<             request=request,
<             version=self.get_app_version(),
<             package=self.APP_PACKAGE_NAME,
1334,1335d648
<             env_short=short_env_name(environ),
<             env_full=full_env_name(environ),
1338,1339d650
<             init_load_time=self.init_load_time,
<             lambda_deployed_time=self.get_lambda_last_modified(),
1348,1351d658
<             is_running_locally=self.is_running_locally(request_dict),
<             logged_in_as=self.get_logged_in_user_info(environ, request_dict),
<             auth0_client_id=self.get_auth0_client_id(environ),
<             aws_account_number=self.get_aws_account_number(),
1354d660
<             environments=self.get_unique_annotated_environment_names(),
1609d914
<         logger.warn(f"queue_scheduled_checks: sched_environ={sched_environ} schedule_name={schedule_name} conditions={conditions}")
1611d915
<         logger.warn(f"queue_scheduled_checks: queue={queue}")
1613,1614d916
<             logger.warn(f"queue_scheduled_checks: have schedule_name")
<             logger.warn(f"queue_scheduled_checks: environment.is_valid_environment_name(sched_environ, or_all=True)={self.environment.is_valid_environment_name(sched_environ, or_all=True)}")
1619d920
<             logger.warn(f"queue_scheduled_checks: sched_environs={sched_environs}")
1621d921
<             logger.warn(f"queue_scheduled_checks: sched_environs={check_schedule}")
1629,1630d928
<                 logger.warn(f"queue_scheduled_checks: calling send_sqs_messages({environ}) ... check_values:")
<                 logger.warn(check_vals)
1632d929
<                 logger.warn(f"queue_scheduled_checks: after calling send_sqs_messages({environ})")
1635d931
<             logger.warn(f"queue_scheduled_checks: calling invoke_check_runner({runner_input})")
1637,1638d932
<             logger.warn(f"queue_scheduled_checks: after calling invoke_check_runner({runner_input})")
<         logger.warn(f"queue_scheduled_checks: returning({runner_input})")
1642a937,942
>         """
>         Gets schedules from the check_schedule table for the given environ, which may be a short, full, or public name.
> 
>         In the new environment configuration, there are multiple aliases that refer to the same environment.
>         This function ensures that when writing a check schedule you can refer to any of the aliases.
>         """
diff -r ./check_utils.py /tmp/foursight-core/foursight_core/check_utils.py
35d34
<         print(f"foursight_core/CheckHandler: Loading check_setup.json file: {setup_path}")
38,39d36
<         print(f"foursight_core/CheckHandler: Loaded check_setup.json file: {setup_path} ...")
<         print(self.CHECK_SETUP)
41d37
<         print(f"foursight_core/CheckHandler: Validating check_setup.json file: {setup_path}")
43d38
<         print(f"foursight_core/CheckHandler: Done validating check_setup.json file: {setup_path}")
diff -r ./deploy.py /tmp/foursight-core/foursight_core/deploy.py
5d4
< from dcicutils.misc_utils import PRINT
15,45d13
< # TODO: Move to dcicutils.command_utils.
< def subprocess_call(command: list, verbose: bool = False, **kwargs) -> int:
<     """
<     Executes the given command (list of arguments, the main command as the zeroth)
<     in a sub-process and returns the return code from the command execution.
<     Prints (to stdout) before/after execution messages if ``verbose`` is True.
<     If general (not command execution related) exception occurs raises that exception.
< 
<     :param command: Command as list with zeroth item being the main command.
<     :param verbose: If True prints (to stdout) before/after execution messages.
<     :return: Return code from the command execution.
<     """
<     if verbose:
<         command_string = " ".join(command)
<     try:
<         if verbose:
<             PRINT(f"Executing command in sub-process: {command_string}")
<         return_code = subprocess.check_call(command, **kwargs)
<         if verbose:
<             PRINT(f"Done executing (return-code: {return_code}) command in sub-process: {command_string}")
<     except subprocess.CalledProcessError as command_exception:
<         return_code = command_exception.returncode
<         if verbose:
<             PRINT(f"Error executing (return-code: {return_code}) command in sub-process: {command_string}")
<     except Exception as general_exception:
<         if verbose:
<             PRINT(f"Exception executing command ({command_string}) in sub-process: {general_exception}")
<         raise general_exception
<     return return_code
< 
< 
111c79
<             #     PRINT(''.join(['ERROR. You are missing one more more environment',
---
>             #     print(''.join(['ERROR. You are missing one more more environment',
125c93
<             #     PRINT(''.join(['ERROR. You are missing one more more environment ',
---
>             #     print(''.join(['ERROR. You are missing one more more environment ',
164c132
<                         PRINT('ERROR. GLOBAL_BUCKET_ENV and GLOBAL_ENV_BUCKET are both set, but inconsistently.')
---
>                         print('ERROR. GLOBAL_BUCKET_ENV and GLOBAL_ENV_BUCKET are both set, but inconsistently.')
171c139
<                     PRINT('ERROR. GLOBAL_ENV_BUCKET must be set or global_env_bucket= must be passed'
---
>                     print('ERROR. GLOBAL_ENV_BUCKET must be set or global_env_bucket= must be passed'
182c150
<         PRINT(''.join(['Writing: ', filename]))
---
>         print(''.join(['Writing: ', filename]))
185d152
<         PRINT(''.join(['Done writing: ', filename]))
187,188c154,155
<         subprocess_call(
<             ['poetry', 'export', '-f', 'requirements.txt', '--without-hashes', '-o', 'requirements.txt'], verbose=True)
---
>         subprocess.check_call(
>             ['poetry', 'export', '-f', 'requirements.txt', '--without-hashes', '-o', 'requirements.txt'])
194c161
<         subprocess_call(['chalice', 'deploy', '--stage', stage], verbose=True)
---
>         subprocess.call(['chalice', 'deploy', '--stage', stage])
223,224c190
<                 cls.build_config(stage, identity=identity, stack_name=stack_name, trial_creds=trial_creds,
<                                  trial_global_env_bucket=True,
---
>                 cls.build_config(stage, identity=identity, stack_name=stack_name, trial_creds=trial_creds, trial_global_env_bucket=True,
237c203
<         subprocess_call(['chalice', 'package', *flags, output_file], verbose=True)
---
>         subprocess.call(['chalice', 'package', *flags, output_file])
diff -r ./identity.py /tmp/foursight-core/foursight_core/identity.py
25c25,26
<     GLOBAL_APPLICATION_CONFIGURATION
---
>     GLOBAL_APPLICATION_CONFIGURATION,
>     SecretsTable,
33,81c34
< def find_lambda_names(stack_name: str, lambda_name_pattern: str) -> list:
<     """
<     Returns list of lambda function names for the given stack name and containing the given pattern.
<     This takes into account the fact that chalice MAY have TRUNCATED the stack name portion of the
<     lambda function name we are looking for. So the lambda function name we look for is the one
<     containing the given pattern AND which starts with (up until the given pattern) a string
<     which either matches the stack name or is a substring of the stack name.
< 
<     For example, if the the stack name is "c4-foursight-fourfront-production-stack" and there exists
<     a lambda function named "c4-foursight-fourfront-production-stac-CheckRunner-MW4VHuCIsDXc" then
<     we will match this even though the "c4-foursight-fourfront-production-stack" stack name prefix
<     was truncated (by one character) to "c4-foursight-fourfront-production-stac" in the lambda name.
< 
<     :param stack_name: Stack name which is a prefix of the lambda name to find.
<     :param lambda_name_pattern: Substring which the lambda function name must contain.
<     :return: List of zero or more matching names.
<     """
<     response = []
<     lambda_name_regex = f".*{lambda_name_pattern}.*"
<     lambda_names = AbstractOrchestrationManager.find_lambda_function_names(lambda_name_regex)
<     for lambda_name in lambda_names:
<         lambda_name_prefix_end_index = lambda_name.find(lambda_name_pattern)
<         if lambda_name_prefix_end_index < 0:
<             continue
<         lambda_name_prefix = lambda_name[:lambda_name_prefix_end_index]
<         if stack_name.startswith(lambda_name_prefix):
<             response.append(lambda_name)
<     return response
< 
< 
< def find_check_runner_lambda_name(stack_name: str) -> str:
<     """
<     Returns name of the CheckRunner lambda function for the given stack name.
<     Raises exception if not found or if a unique name is not found.
< 
<     :param stack_name: Stack name which is a prefix of the lambda name to find.
<     :return: Lambda name of the CheckRunner lamba for the given stack name. Exception if not found or not unique.
<     """
<     check_runner_lambda_name_pattern = "-CheckRunner-"
<     check_runner_lambda_names = find_lambda_names(stack_name, check_runner_lambda_name_pattern)
<     if not check_runner_lambda_names:
<         logger.warn(f"Foursight CheckRunner lambda (containing: {check_runner_lambda_name_pattern})"
<                     f" not found for stack: {stack_name}")
<         return None
<     elif len(check_runner_lambda_names) != 1:
<         logger.warn(f"Unique Foursight CheckRunner lambda (containing: {check_runner_lambda_name_pattern})"
<                     f" not found (matches: {len(check_runner_lambda_names)}) for stack: {stack_name}")
<         return None
<     return check_runner_lambda_names[0]
---
> def apply_identity_globally():
82a36,42
>     # This maps key names in the global application configuration (GAC) to names used here.
>     IDENTITY_KEY_MAP = {
>         "ENCODED_AUTH0_CLIENT": "CLIENT_ID",
>         "ENCODED_AUTH0_SECRET": "CLIENT_SECRET",
>         "ENCODED_S3_ENCRYPT_KEY_ID": "S3_ENCRYPT_KEY_ID",
>         "ENCODED_ES_SERVER": "ES_HOST",
>     }
84c44,45
< def verify_identity_name_environment_variable_exists() -> None:
---
>     # Make sure the IDENTITY (environment variable) is set (via Foursight CloudFormation template);
>     # this is the name of the global application configuration (GAC) secret.
87c48
<         raise Exception(f"Foursight {GLOBAL_APPLICATION_CONFIGURATION} environment variable not set!")
---
>         raise Exception("Foursight {GLOBAL_APPLICATION_CONFIGURATION} environment variable not set!")
90,91c51,52
< 
< def get_stack_name_from_environment_variable() -> str:
---
>     # Make sure the STACK_NAME (environment variable) is set (via Foursight CloudFormation template);
>     # from this we are able to find the CHECK_RUNNER lambda function name.
94c55
<         raise Exception(f"Foursight STACK_NAME environment variable not set!")
---
>         raise Exception("Foursight STACK_NAME environment variable not set!")
96d56
<     return stack_name
97a58,59
>     # Apply the GAC secrets values globally to os.environ.
>     apply_identity(identity_kind=GLOBAL_APPLICATION_CONFIGURATION, rename_keys=IDENTITY_KEY_MAP)
99c61,63
< def verify_rds_name_environment_variable_exists() -> None:
---
>     # Get RDS_NAME from the GAC. But just added this recently (late July 2022), so to avoid
>     # issues with release timing, at least for testing period, if it is not set then get it
>     # from the RDS secrets. Temporary hack. TODO: Remove when fully tested and ready to go.
101,117c65,68
<     if rds_name:
<         logger.info(f"Foursight RDS_NAME environment variable value is: {rds_name}")
<     else:
<         logger.info(f"Foursight RDS_NAME environment variable is not set.")
< 
< 
< def apply_identity_environment_variables() -> None:
< 
<     # This maps key names in the global application configuration (GAC) to names used here.
<     IDENTITY_KEY_MAP_REQUIRED = {
<         "ENCODED_AUTH0_CLIENT": "CLIENT_ID",
<         "ENCODED_AUTH0_SECRET": "CLIENT_SECRET",
<         "ENCODED_ES_SERVER": "ES_HOST",
<     }
<     IDENTITY_KEY_MAP_OPTIONAL = {
<         "ENCODED_S3_ENCRYPT_KEY_ID": "S3_ENCRYPT_KEY_ID"
<     }
---
>     if not rds_name:
>         rds_secrets_name = identity_name.replace("ApplicationConfiguration", "RDSSecret")
>         rds_secrets = SecretsTable(rds_secrets_name)
>         os.environ["RDS_NAME"] = rds_secrets.get("dbInstanceIdentifier")
119,127c70,80
<     apply_identity(identity_kind=GLOBAL_APPLICATION_CONFIGURATION, rename_keys=IDENTITY_KEY_MAP_REQUIRED)
<     try:
<         apply_identity(identity_kind=GLOBAL_APPLICATION_CONFIGURATION, rename_keys=IDENTITY_KEY_MAP_OPTIONAL)
<     except (KeyError, ValueError):
<         pass
< 
< 
< def set_check_runner_lambda_environment_variable(stack_name) -> None:
<     os.environ["CHECK_RUNNER"] = find_check_runner_lambda_name(stack_name)
---
>     # Get the CHECK_RUNNER lambda function name; using the stack_name as a prefix; for example,
>     # this lambda name looks like: c4-foursight-cgap-supertest-stack-CheckRunner-pKsOvziDT7QI
>     # where c4-foursight-cgap-supertest-stack is the stack_name.
>     check_runner_lambda_function_name_pattern = stack_name + "-CheckRunner-.*"
>     check_runner_lambda_function_names = (
>         AbstractOrchestrationManager.find_lambda_function_names(check_runner_lambda_function_name_pattern))
>     if not check_runner_lambda_function_names:
>         raise Exception("Foursight CheckRunner lambda not found: {check_runner_lambda_function_name_pattern}")
>     elif len(check_runner_lambda_function_names) != 1:
>         raise Exception("Unique Foursight CheckRunner lambda not found: {check_runner_lambda_function_name_pattern}")
>     os.environ["CHECK_RUNNER"] = check_runner_lambda_function_names[0]
130,131c83
< 
< def set_elasticsearch_host_environment_variable() -> None:
---
>     # Set ES_HOST to proxy for local testing (e.g. http://localhost:9200) via ES_HOST_LOCAL environment variable.
138,169d89
< 
< 
< def apply_identity_globally():
< 
<     # Make sure the IDENTITY (environment variable) is set (via Foursight CloudFormation template);
<     # this is the name of the global application configuration (GAC) secret.
<     verify_identity_name_environment_variable_exists()
< 
<     # Make sure the STACK_NAME (environment variable) is set (via Foursight CloudFormation template);
<     # from this we are able to find the CHECK_RUNNER lambda function name.
<     stack_name = get_stack_name_from_environment_variable()
< 
<     # Apply the GAC secrets values globally to os.environ.
<     # First do the required ones and then the non-required ones;
<     # like this because apply_identity will throw an exception of the given key name does not exist.
<     # Could enhance apply_identity to handle this optional-key scenario, but for now just do it like this.
<     apply_identity_environment_variables()
< 
<     # Note that we will assume RDS_NAME is in the GAC.
<     # Previously it was (and still is) in the RDSSecret (as dbInstanceIdentifier)
<     # but we added it (as RDS_NAME) to the GAC (circa August 2022).
<     verify_rds_name_environment_variable_exists()
< 
<     # Get the CHECK_RUNNER lambda function name; using the stack_name as a prefix; for example,
<     # this lambda name looks like: c4-foursight-cgap-supertest-stack-CheckRunner-pKsOvziDT7QI
<     # where c4-foursight-cgap-supertest-stack is the stack_name. See find_check_runner_lambda_name
<     # above which hides handling of details related to the fact that chalice may have truncated
<     # the stack name (prefix) portion of the lambda function name.
<     set_check_runner_lambda_environment_variable(stack_name)
< 
<     # Set ES_HOST to proxy for local testing (e.g. http://localhost:9200) via ES_HOST_LOCAL environment variable.
<     set_elasticsearch_host_environment_variable()
Only in .: react
diff -r ./templates/base.html /tmp/foursight-core/foursight_core/templates/base.html
1,2d0
< {% extends "header.html" %}
< 
124a123,375
> 
> <!-- Main body template. Define the base template, <style> and <script> here -->
> <!DOCTYPE html>
> <html class="html-scroll">
> <head>
>     <meta charset="utf-8">
>     <title>{{main_title}}</title>
>     <meta name="viewport" content="width=device-width, initial-scale=1">
>     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
>     <!-- Optional Bootstrap theme -->
>     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
>     <!-- Update with data soon -->
>     <link rel="shortcut icon" type="image/x-icon" href={{favicon}}>
>     <!-- Get variables needed within js from jinja -->
> 
>     <style type="text/css">
>     .html-scroll {
>         overflow-y: scroll;
>     }
>     .container {
>         max-width: 1900px;
>         padding-top: 10px;
>     }
>     .control-form {
>         display: inline;
>         margin-right: 20px;
>         margin-top: -8px;
>     }
>     .boxstyle {
>         border: 1px solid #ccc;
>         margin-bottom: 5px;
>         border-radius: 8px;
>         padding: 0px 0px 0px 0px;
>     }
>     .group-boxstyle {
>         border: 1px solid #ccc;
>         margin-bottom: 5px;
>         border-radius: 8px;
>         padding: 0px 0px 0px 0px;
>         background-color: #f8f8f8;
>     }
>     .group-boxstyle:hover {
>         background-color: #f2f2f2;
>     }
>     .login-w-hover {
>         border: 1px solid #ccc;
>         border-radius: 8px;
>         background-color: #f8f8f8;
>         font-weight: 350;
>     }
>     .login-w-hover:hover {
>         background-color: #d9d9d9;
>     }
>     .commonstyle {
>         margin: 0px 10px 5px 10px;
>     }
>     .collapse-div{
>         overflow: hidden;
>         clear: both;
>     }
>     .own-line {
>         display: block;
>     }
>     .in-line{
>         display: inline-block;
>     }
>     .action-icon {
>         height: 11px;
>         width: 11px;
>         border-radius: 50%;
>         display: inline-block;
>     }
>     .assc-action-done {
>         background-color: #55aa57;
>     }
>     .assc-action-fail {
>         background-color: #b84947;
>     }
>     .assc-action-pend {
>         background-color: #e7c418;
>     }
>     .assc-action-ready {
>         background-color: #b6b5b5;
>     }
>     .check-pass, .check-done {
>         background-color: #dff0d8;
>         color: #3c763d;
>     }
>     .check-warn, .check-pend {
>         background-color: #fcf8e3;
>         color: #8a6d3b;
>     }
>     .check-fail {
>         background-color: #f2dede;
>         color: #a94442;
>     }
>     .check-error {
>         background-color: #fbdcbd;
>         color: #b75c00;
>     }
>     .check-ignore {
>         display: none;
>     }
>     .center-element {
>         text-align: center;
>     }
>     .opacity-transition {
>         -webkit-transition: opacity 0.5s;
>         transition: opacity 0.5s;
>     }
>     .title-image-hover, .title-image-hover:visited, .title-image-hover:active {
>         text-decoration: none;
>         opacity: 0.7;
>     }
>     .title-image-hover:hover, .title-image-hover:focus {
>         text-decoration: none;
>         opacity: 1.0;
>     }
>     .title-hover, .title-hover:visited, .title-hover:active {
>         color: inherit;
>     }
>     .title-hover:hover, .title-hover:focus{
>         text-decoration: none;
>         color: #000;
>     }
>     .top-level-list {
>         padding: 0px;
>     }
>     .no-border {
>         border: none;
>     }
>     li {
>         list-style-type: none;
>         padding: 0;
>         margin: 0;
>     }
>     dl {
>         margin-bottom: 0px;
>     }
>     dd {
>         margin-left: 20px;
>         margin-bottom: 5px;
>     }
>     </style>
> </head>
> <body>
>     <div class="container">
>         <h3 class="center-element">
>             <a class="title-image-hover opacity-transition" href={{context + 'view/' + env}}>
>                 <img class="in-line" alt="FS logo" style="margin:-5px 2px 0px 0px;" height="30" src={{favicon}}></img>
>             </a>
>             <span>{{main_title}}</span>
>         </h3>
>         {% if load_time %}
>             <h5 class="center-element">
>                 <span>{{'Loaded on ' + load_time}}</span>
>             </h5>
>         {% endif %}
>         {% if is_admin %}
>             <h5 class="center-element">
>                 <span>{{'Currently logged in as admin'}}</span> <br />
>                 <span>{{'Deployment Stage is ' + stage + ''}}</span>
>             </h5>
>         {% else %}
>             <h5 class="center-element">
>                 <span>{{'Not logged in as admin'}}</span>
>                 <button class="login-w-hover in-line" onclick="create_redir_cookie(); lock.show();">Log in</button>
>             </h5>
>         {% endif %}
>         {% if running_checks != '0' or queued_checks != '0' %}
>             <div class='center-element'>
>                 <span style='margin-right:10px; color:#777'>
>                     {{'Running: ' + running_checks}}
>                 </span>
>                 <span style='color:#777'>
>                     {{'Queued: ' + queued_checks}}
>                 </span>
>             </div>
>         {% endif %}
>         <div id="content">{% block content %}{% endblock %}</div>
>     </div>
>     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
>     <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
>     <script src="https://cdn.auth0.com/js/lock/10.24.1/lock.min.js"></script>
>     <script type="text/javascript">
>       // set cookie containing the location we were at when we tried to Login
>       // this cookie is re-loaded at /callback and parsed for the url, which
>       // it redirects to upon finishing authentication
>       function create_redir_cookie() {
>         var expr = new Date();
>         expr.setFullYear(expr.getFullYear() + 1);
>         document.cookie = "redir=" + window.location.href + "; path=/; expires=" + expr.toUTCString();
>       }
>     </script>
>     <script type="text/javascript">
>         var domain = "{{domain}}";
>         var context = "{{context}}";
>         var lock = new Auth0Lock('DPxEwsZRnKDpk0VfVAxrStRKukN14ILB', 'hms-dbmi.auth0.com', {
>             auth: {
>                 redirectUrl: 'https://' + domain + context + 'callback/',
>                 responseType: 'code',
>                 sso: false,
>                 params: {scope: 'openid email', prompt: 'select_account'}
>             },
>             socialButtonStyle: 'big',
>             languageDictionary: { title: "Log in" },
>             theme: { logo: "{{favicon}}" },
>             allowedConnections: ['github', 'google-oauth2']
>         });
>     </script>
>     <script type="text/javascript">
>         function collapse_group(e, id){
>             // Do not trigger collapse if click is on or a child of a check-result
>             var isCheck = $(e.target).hasClass("check-result")
>             var parents = $(e.target).parents(".check-result");
>             if(!isCheck && parents.length === 0){
>                 $('#' + id).collapse('toggle');
>             }
>         }
>     </script>
>     <script type="text/javascript">
>         function confirm_action(uuid, message){
>             var actionMessage = document.getElementById("action-message-" + uuid);
>             actionMessage.innerHTML = '<pre class="commonstyle"><code>' + message + '</code></pre>';
>             var actionButton = document.getElementById("action-" + uuid);
>             actionButton.classList.remove('btn-warning');
>             actionButton.classList.add('btn-danger');
>             actionButton.onclick = null;
>             actionButton.disabled = true;
>             setTimeout(function(){
>                 actionButton.disabled = false;
>                 actionButton.type = "submit";
>             },1000);
>         }
>     </script>
>     <script>
>         // Toggle opacity of a check result summary when the anchor to open/close result is used
>         $(document).ready(function() {
>             $('.title-hover').click(function(){
>                 var anchorId = $(this).attr("id");
>                 var variablePart = anchorId.replace("toggle-","");
>                 var summaryEle = $('#summary-'+variablePart);
>                 if (summaryEle.css('opacity') == 0){
>                     summaryEle.css({ opacity: 1 });
>                 }else{
>                     summaryEle.css({ opacity: 0 });
>                 }
>             });
>         });
>     </script>
>     {% block script %}{% endblock %}
> </body>
> </html>
Only in ./templates: header.html
Only in ./templates: info.html
Only in ./templates: user.html
Only in ./templates: users.html
diff -r ./templates/view_checks.html /tmp/foursight-core/foursight_core/templates/view_checks.html
17a18
>         <hr>
diff -r ./templates/view_groups.html /tmp/foursight-core/foursight_core/templates/view_groups.html
13c13
<                     <div onclick="{{"return collapse_group(event, '" + env['environment'] + "-" + group_title + "');" | safe}}" class="group-boxstyle" style="cursor:pointer">
---
>                     <div onclick="{{"return collapse_group(event, '" + env['environment'] + "-" + group_title + "');" | safe}}" class="group-boxstyle">
41a42
>         <hr>
Only in .: x
