From b8f0a466ff2b917d114ef4d4b3c92f1c5b32e3fb Mon Sep 17 00:00:00 2001 From: David Michaels Date: Fri, 1 Sep 2023 13:18:53 -0400 Subject: [PATCH] Minor SMaHT related updates. --- foursight_core/react/api/react_api.py | 41 ++++++++---- foursight_core/react/api/react_api_base.py | 4 +- pyproject.toml | 2 +- react/src/hooks/FetchBase.js | 2 +- react/src/hooks/UserMetadata.js | 36 ++++++++++- react/src/pages/AccountsComponent.js | 14 +++- react/src/pages/UserPage.js | 24 ++++++- react/src/pages/UsersPage.js | 75 +++++++++++++++++++--- react/src/utils/Env.js | 49 ++++++++------ 9 files changed, 197 insertions(+), 50 deletions(-) diff --git a/foursight_core/react/api/react_api.py b/foursight_core/react/api/react_api.py index f7cd1a41b..20260ff26 100644 --- a/foursight_core/react/api/react_api.py +++ b/foursight_core/react/api/react_api.py @@ -407,7 +407,7 @@ def reactapi_header(self, request: dict, env: str) -> Response: test_mode_access_key_simulate_error=test_mode_access_key_simulate_error, test_mode_access_key_expiration_warning_days=test_mode_access_key_expiration_warning_days) if data["portal_access_key"].get("invalid"): - data["portal_access_key_error"] = True + data["portal_access_key_erro"] = True return self.create_success_response(data) @function_cache(key=lambda self, request, env: env) # new as of 2023-04-27 @@ -648,7 +648,7 @@ def _create_user_record_for_output(self, user: dict) -> dict: updated = last_modified.get("date_modified") or user.get("date_created") else: updated = user.get("date_created") - user = { + result = { # Lower case email to avoid any possible issues on lookup later. "email": (user.get("email") or "").lower(), "first_name": user.get("first_name"), @@ -662,24 +662,28 @@ def _create_user_record_for_output(self, user: dict) -> dict: "created": convert_utc_datetime_to_utc_datetime_string(user.get("date_created")) } institution = user.get("user_institution") - project = user.get("project"), + project = user.get("project") award = user.get("award") lab = user.get("lab") consortia = user.get("consortia") submission_centers = user.get("submission_centers") if institution: - user["institution"] = institution + result["institution"] = institution if project: - user["project"] = project + result["project"] = project if award: - user["award"] = award + result["award"] = award if lab: - user["lab"] = lab + result["lab"] = lab + # Note that for the affilitiaions, like institution/project for CGAP + # and award/institution for Fourfrount, where these are single + # values, for SMaHT consortia/submission-centers are arrays; + # will let the UI deal with any display issues there. if consortia: - user["consortia"] = consortia + result["consortia"] = consortia if submission_centers: - user["submission_centers"] = submission_centers - return user + result["submission_centers"] = submission_centers + return result def _create_user_record_from_input(self, user: dict, include_deletes: bool = False) -> dict: """ @@ -1452,10 +1456,22 @@ def is_this_server(url: str) -> bool: try: url_origin = urllib.parse.urlparse(url).netloc this_origin = request.get('headers', {}).get('host') - return url_origin == this_origin + return url_origin == this_origin or this_origin == "localhost:8000" except Exception: return False + def check_s3_aws_access_key() -> bool: + s3_aws_access_key_id = os.environ.get("S3_AWS_ACCESS_KEY_ID") + s3_secret_access_key = os.environ.get("S3_SECRET_ACCESS_KEY") + global_env_bucket = os.environ.get("GLOBAL_ENV_BUCKET") + if s3_aws_access_key_id and s3_secret_access_key and global_env_bucket: + s3 = s3.client("s3") + try: + s3.list_objects_v2(Bucket=global_env_bucket) + except Exception: + return False + return True + def get_foursight_info(foursight_url: str, response: dict) -> Optional[str]: response["foursight"] = {} if not foursight_url: @@ -1503,6 +1519,9 @@ def get_foursight_info(foursight_url: str, response: dict) -> Optional[str]: response["foursight"]["s3"]["encrypt_key_id"] = foursight_header_json_s3.get("encrypt_key_id") response["foursight"]["s3"]["has_encryption"] = foursight_header_json_s3.get("has_encryption") response["foursight"]["s3"]["buckets"] = foursight_header_json_s3.get("buckets") + response["foursight"]["s3"]["access_key"] = os.environ.get("S3_AWS_ACCESS_KEY_ID") + if not check_s3_aws_access_key(): + response["foursight"]["s3"]["access_key_error"] = True response["foursight"]["aws_account_number"] = foursight_app["credentials"].get("aws_account_number") response["foursight"]["aws_account_name"] = foursight_app["credentials"].get("aws_account_name") response["foursight"]["re_captcha_key"] = foursight_app["credentials"].get("re_captcha_key") diff --git a/foursight_core/react/api/react_api_base.py b/foursight_core/react/api/react_api_base.py index 7fda2b5f2..dae86561e 100644 --- a/foursight_core/react/api/react_api_base.py +++ b/foursight_core/react/api/react_api_base.py @@ -223,10 +223,10 @@ def get_redirect_url(self, request: dict, env: str, domain: str, context: str) - return f"{self.foursight_instance_url(request)}/react/{env}/login" def is_foursight_cgap(self) -> bool: - return app.core.APP_PACKAGE_NAME == "foursight" or app.core.APP_PACKAGE_NAME == "foursight-fourfront" + return app.core.APP_PACKAGE_NAME == "foursight-cgap" def is_foursight_fourfront(self) -> bool: - return app.core.APP_PACKAGE_NAME == "foursight-cgap" + return app.core.APP_PACKAGE_NAME == "foursight" or app.core.APP_PACKAGE_NAME == "foursight-fourfront" def is_foursight_smaht(self) -> bool: return app.core.APP_PACKAGE_NAME == "foursight-smaht" diff --git a/pyproject.toml b/pyproject.toml index 8ffd8d5bc..24841cd3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "foursight-core" -version = "4.4.0.1b11" # TODO: To become 4.5.0 +version = "4.4.0.1b12" # TODO: To become 4.5.0 description = "Serverless Chalice Application for Monitoring" authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/react/src/hooks/FetchBase.js b/react/src/hooks/FetchBase.js index 572c2363c..c63603f80 100644 --- a/react/src/hooks/FetchBase.js +++ b/react/src/hooks/FetchBase.js @@ -424,7 +424,7 @@ function _doFetch(args, current = undefined, fetcher) { if (args.cache) { Debug.Info(`FETCH CACHING RESPONSE: ${args.method} ${args.url} -> HTTP ${status}`); _fetchCache[args.url] = { - data: data, + data: fetcher.data, status: status } } diff --git a/react/src/hooks/UserMetadata.js b/react/src/hooks/UserMetadata.js index e62990164..1a8698912 100644 --- a/react/src/hooks/UserMetadata.js +++ b/react/src/hooks/UserMetadata.js @@ -1,6 +1,6 @@ import useFetch from './Fetch'; -const useUserMetadata = () => { +const useUserMetadataCgap = () => { const projects = useFetch("/users/projects", { cache: true }); const roles = useFetch("/users/roles", { cache: true }); const institutions = useFetch("/users/institutions", { cache: true }); @@ -19,4 +19,38 @@ const useUserMetadata = () => { return response; } +const useUserMetadataSmaht = () => { + const consortia = useFetch("/users/consortia", { cache: true }); + const roles = useFetch("/users/roles", { cache: true }); + const submissionCenters = useFetch("/users/submission_centers", { cache: true }); + const statuses = useFetch("/users/statuses", { cache: true }); + const response = { + consortiumTitle: (id) => { + let value = consortia.data?.find(item => item.id === id)?.title || ""; + if (value?.endsWith(" Consortium")) { + value = value.replace(" Consortium", ""); + } + return value; + }, + roleTitle: (id) => roles.data?.find(item => item.id === id)?.title || "", + submissionCenterTitle: (id) => { + let value = submissionCenters.data?.find(item => item.id === id)?.title || ""; + if (value?.endsWith(" Submission Center")) { + value = value.replace(" Submission Center", ""); + } + return value; + }, + statusTitle: (id) => statuses.data?.find(item => item.id === id)?.title || "", + userRole: (user, consortiumId) => user.roles?.find(item => item.consortium === consortiumId)?.role || "", + title: (s) => s.replace(/\w\S*/g, (s) => s.charAt(0).toUpperCase() + s.substr(1).toLowerCase()) || "", + principleInvestigator: (submissionCenterId) => submissionCenters?.data?.find(item => item.id === submissionCenterId)?.pi + } + response.userRoleTitle = (user, consortiumId) => response.roleTitle(response.userRole(user, consortiumId)) || ""; + response.titles = (sa) => sa?.map(s => response.title(s)).join(", ") || ""; + return response; +} + +//const useUserMetadata = useUserMetadataCgap; +const useUserMetadata = useUserMetadataSmaht; + export default useUserMetadata; diff --git a/react/src/pages/AccountsComponent.js b/react/src/pages/AccountsComponent.js index 8549ffbac..441475b29 100644 --- a/react/src/pages/AccountsComponent.js +++ b/react/src/pages/AccountsComponent.js @@ -122,7 +122,7 @@ const Row = ({ title, value, additionalValue, externalLink, children, tooltip = const tooltipText = Type.IsArray(tooltip) ? tooltip[0] : null; const tooltipId = Type.IsArray(tooltip) ? tooltip[1] : null; return - + {title}: @@ -168,7 +168,7 @@ const SecretsDropdown = ({ header, info, name }) => { :<> Secrets {Char.UpArrow} } - { showIdentity && } + { showIdentity && } } } @@ -300,6 +300,16 @@ const AccountInfoLeft = ({ header, info, foursightUrl }) => { externalLink={`https://us-east-1.console.aws.amazon.com/kms/home?region=us-east-1#/kms/keys/${info.get("foursight.s3.encrypt_key_id")}`} tooltip={[`S3 Bucket Encryption Key ID`, `tooltip-encryption-key-${info.get("foursight.aws_account_number")}`]} showEmpty={true}> + +   + { info.get("foursight.s3.access_key_error") ? <> + {Char.X} + :<> + {Char.Check} + } + diff --git a/react/src/pages/UserPage.js b/react/src/pages/UserPage.js index 274f4e767..69a2e1e81 100644 --- a/react/src/pages/UserPage.js +++ b/react/src/pages/UserPage.js @@ -88,14 +88,36 @@ const UserBox = (props) => { const userMetadata = useUserMetadata(); + const affiliations = { + [Env.FoursightTitleCgap]: [ + { label: "Institution", name: "institution", + map: value => userMetadata.institutionTitle(value), + subComponent: (institution) => }, + { label: "Project", name: "project", + map: value => userMetadata.projectTitle(value) } + ], + [Env.FoursightTitleFourfront]: [ + { label: "Award", name: "award", + map: value => userMetadata.awardTitle(value) }, + { label: "Lab", name: "lab", + map: value => userMetadata.labTitle(value) } + ], + [Env.FoursightTitleSmaht]: [ + { label: "Consortium", name: "consortium", + map: value => userMetadata.awardTitle(value) }, + { label: "Submission Center", name: "submission_center", + map: value => userMetadata.labTitle(value) } + ] + }; + let items = [ { label: "Email", name: "email" }, { label: "First Name", name: "first_name" }, { label: "Last Name", name: "last_name" }, { label: "Groups", name: "groups", map: value => userMetadata.titles(value) }, - { label: "Project", name: "project", map: value => userMetadata.projectTitle(value) }, { label: "Role", name: "role", map: value => userMetadata.userRoleTitle(props.user, props.user.project) }, { label: "Roles", name: "roles", ui: , toggle: true }, + { label: "Project", name: "project", map: value => userMetadata.projectTitle(value) }, { label: "Institution", name: "institution", map: value => userMetadata.institutionTitle(value), subComponent: (institution) => }, { label: "Status", name: "status", map: value => userMetadata.statusTitle(value) }, diff --git a/react/src/pages/UsersPage.js b/react/src/pages/UsersPage.js index c05563b34..2903e5719 100644 --- a/react/src/pages/UsersPage.js +++ b/react/src/pages/UsersPage.js @@ -4,6 +4,7 @@ import { Link } from '../Components'; import useFetch from '../hooks/Fetch'; import Char from '../utils/Char'; import Date from '../utils/Date'; +import Env from '../utils/Env'; import { FetchErrorBox } from '../Components'; import Server from '../utils/Server'; import Str from '../utils/Str'; @@ -13,9 +14,11 @@ import Tooltip from '../components/Tooltip'; import Time from '../utils/Time'; import Type from '../utils/Type'; import useUserMetadata from '../hooks/UserMetadata'; +import useHeader from '../hooks/Header'; const UsersPage = () => { + const header = useHeader(); const { environ } = useParams(); const [ args, setArgs ] = useSearchParams(); const users = useFetch(); @@ -39,12 +42,26 @@ const UsersPage = () => { if (users.error) return + const affiliations = { + [Env.FoursightTitleCgap]: [ + { label: "Project", key: "project" }, + { label: "Institution", key: "institution" }, + ], + [Env.FoursightTitleFourfront]: [ + { label: "Award", key: "award" }, + { label: "Lab", key: "lab" }, + ], + [Env.FoursightTitleSmaht]: [ + { label: "Consortia", key: "consortia" }, + { label: "Submission Centers", key: "submission_centers" }, + ] + }; + const columns = [ { label: "" }, { label: "User", key: "email" }, { label: "Groups", key: "groups" }, - { label: "Project", key: "project" }, - { label: "Institution", key: "institution" }, + ...affiliations[Env.FoursightFlavorTitle(header)], { label: "Role", key: "role" }, { label: "Status", key: "status" }, { label: "Updated", key: "data_modified" }, // DOES NOT WORK (nested in last_modified) @@ -156,14 +173,7 @@ const UsersPage = () => { {user.groups?.length > 0 ? (userMetadata.titles(user.groups) || Char.EmptySet) : Char.EmptySet} - - {userMetadata.projectTitle(user.project) || Char.EmptySet} - - - - {userMetadata.institutionTitle(user.institution) || Char.EmptySet} - - + {userMetadata.userRoleTitle(user, user.project) || Char.EmptySet} @@ -194,4 +204,49 @@ const UsersPage = () => { }; +const UserAffiliationItemsCgap = ({user, userMetadata, tdStyle}) => { + return <> + + {userMetadata.projectTitle(user.project) || Char.EmptySet} + + + + {userMetadata.institutionTitle(user.institution) || Char.EmptySet} + + + +} + +const UserAffiliationItemsFourfront = ({user, userMetadata, tdStyle}) => { + return <> + + {userMetadata.awardTitle(user.award) || Char.EmptySet} + + + + {userMetadata.labTitle(user.lab) || Char.EmptySet} + + + +} + +const UserAffiliationItemsSmaht = ({user, userMetadata, tdStyle}) => { + if (Type.IsArray(user.submission_centers) && (user.submission_centers.length > 0)) { + user.submission_center = user.submission_centers[0]; + } + if (Type.IsArray(user.consortia) && (user.consortia.length > 0)) { + user.consortium = user.consortia[0]; + } + return <> + + {userMetadata.consortiumTitle(user.consortium) || Char.EmptySet} + + + + {userMetadata.submissionCenterTitle(user.submission_center) || Char.EmptySet} + + + +} + export default UsersPage; diff --git a/react/src/utils/Env.js b/react/src/utils/Env.js index c90760f75..3b9752a39 100644 --- a/react/src/utils/Env.js +++ b/react/src/utils/Env.js @@ -8,6 +8,10 @@ import Path from './Path'; import Str from './Str'; import Type from './Type'; +const FoursightTitleFourfront = "Fourfront" +const FoursightTitleCgap = "CGAP" +const FoursightTitleSmaht = "SMaHT" + // ------------------------------------------------------------------------------------------------- // Known environments related functions. // ------------------------------------------------------------------------------------------------- @@ -285,25 +289,28 @@ function GetLegacyFoursightLink(header) { // ------------------------------------------------------------------------------------------------- const exports = { - AllowedEnvs: GetAllowedEnvs, - Current: GetCurrentEnv, - Default: GetDefaultEnv, - Equals: AreSameEnvs, - FoursightName: GetFoursightEnvName, - FullName: GetFullEnvName, - FoursightFlavorTitle: GetFoursightFlavorTitle, - FoursightGitHubBase: GetFoursightGitHubBaseRepoName, - IsAllowed: IsAllowedEnv, - IsCurrent: IsCurrentEnv, - IsDefault: IsDefaultEnv, - IsFoursightCgap: IsFoursightCgap, - IsFoursightFourfront: IsFoursightFourfront, - IsFoursightSmaht: IsFoursightSmaht, - IsKnown: IsKnownEnv, - KnownEnvs: GetKnownEnvs, - LegacyFoursightLink: GetLegacyFoursightLink, - PreferredName: GetPreferredEnvName, - PublicName: GetPublicEnvName, - RegularName: GetRegularEnvName, - ShortName: GetShortEnvName + AllowedEnvs: GetAllowedEnvs, + Current: GetCurrentEnv, + Default: GetDefaultEnv, + Equals: AreSameEnvs, + FoursightName: GetFoursightEnvName, + FoursightTitleCgap: FoursightTitleCgap, + FoursightTitleFourfront: FoursightTitleFourfront, + FoursightTitleSmaht: FoursightTitleSmaht, + FullName: GetFullEnvName, + FoursightFlavorTitle: GetFoursightFlavorTitle, + FoursightGitHubBase: GetFoursightGitHubBaseRepoName, + IsAllowed: IsAllowedEnv, + IsCurrent: IsCurrentEnv, + IsDefault: IsDefaultEnv, + IsFoursightCgap: IsFoursightCgap, + IsFoursightFourfront: IsFoursightFourfront, + IsFoursightSmaht: IsFoursightSmaht, + IsKnown: IsKnownEnv, + KnownEnvs: GetKnownEnvs, + LegacyFoursightLink: GetLegacyFoursightLink, + PreferredName: GetPreferredEnvName, + PublicName: GetPublicEnvName, + RegularName: GetRegularEnvName, + ShortName: GetShortEnvName }; export default exports;