diff --git a/src/authorizer/globus_auth.py b/src/authorizer/globus_auth.py new file mode 100644 index 0000000..7fa2ab3 --- /dev/null +++ b/src/authorizer/globus_auth.py @@ -0,0 +1,228 @@ +""" +Models relating to Authorisation for the ESGF Next Gen Core Architecture. +""" + +import logging +import re +from typing import Any, Literal +from urllib.parse import urlparse + +from pydantic import BaseModel +from pydantic_core import ValidationError +from stac_fastapi.extensions.core.transaction.request import PartialItem +from stac_pydantic.item import Item + +from esgf_core_utils.models.exceptions import ( + AuthorizationException, + MissingPermissionException, +) +from esgf_core_utils.models.kafka.events import RequesterData + +logger = logging.getLogger("uvicorn.error") + +Role = Literal[ + "CREATE", + "UPDATE", + "DELETE", + "REPLICATE", + "REVOKE", +] + + +class Node(BaseModel): + """ + Model describing Node auth info of a ESGF publisher. + """ + + id: str + roles: set[Role] + + +class Project(BaseModel): + """ + Model describing Project auth info of a ESGF publisher. + """ + + id: str + roles: set[Role] + + +class Nodes(BaseModel): + """ + Model describing Project auth info of a ESGF publisher. + """ + + nodes: dict[str, Node] = {} + + def add(self, node: Node | dict[str, Any]) -> None: + """ + Add a new project or update roles if project already exists. + + Args: + node (Node | dict): node to be added + """ + if isinstance(node, dict): + node = Node(**node) + + if existing_node := self.nodes.get(node.id): + existing_node.roles.update(node.roles) + + else: + self.nodes[node.id] = node + + def authorize_href(self, asset_href: str, role: Role) -> None: + asset_url = urlparse(asset_href) + node_permission = self.nodes.get(asset_url.hostname or "", None) + if not node_permission: + node_permission = self.nodes.get("*", None) + + if not node_permission: + raise MissingPermissionException( + permission_type="node", + target=asset_href, + ) + + if role not in node_permission.roles: + raise MissingPermissionException( + permission_type="node", + role=role, + target=asset_href, + ) + + def authorize(self, assets: dict[str, Any], role: Role) -> None: + """Check for appropriate authorisation. + + Args: + assets (dict): item to be authorised + role (Role): required role for auhroisation + + Raises: + MissingPermissionException: Raised if either node or role permission is missing + """ + + for asset in assets.values(): + asset = asset.model_dump() if not isinstance(asset, dict) else asset + + if "href" in asset: + self.authorize_href(f"https://{asset.get("alternate:name")}", role) + + if alternates := asset.get("alternate"): + self.authorize(alternates, role) + + +class Projects(BaseModel): + """ + Model describing Project auth info of a ESGF publisher. + """ + + projects: dict[str, Project] = {} + + def add(self, project: Project | dict[str, Any]) -> None: + """ + Add a new project or update roles if project already exists. + + Args: + project (Project | dict): project to be added + """ + if isinstance(project, dict): + project = Project(**project) + + if existing_project := self.projects.get(project.id): + existing_project.roles.update(project.roles) + + else: + self.projects[project.id] = project + + def authorize(self, project: str, role: Role) -> None: + """Check for appropriate authorisation. + + Args: + item (Item): item to be authorised + role (Role): required role for auhroisation + + Raises: + MissingPermissionException: Raised if either node or role permission is missing + """ + project_permission = self.projects.get(project, None) + if not project_permission: + project_permission = self.projects.get("*", None) + + if not project_permission: + raise MissingPermissionException( + permission_type="project", + target=project, + ) + + if role not in project_permission.roles: + raise MissingPermissionException( + permission_type="project", + role=role, + target=project, + ) + + +class GlobusAuth(BaseModel): + """ + Model describing Authentication information of a ESGF publisher. + """ + + requester_data: RequesterData + nodes: Nodes = Nodes() + projects: Projects = Projects() + regex: str + + def authorize( + self, + collection_id: str, + item: Item | PartialItem, + role: Role, + request_id: str, + event_id: str, + ) -> None: + """Check for appropriate authorisation. + + Args: + collection_id: collection id of request + item (Item): item to be authorised + role (Role): required role for auhroisation + + Raises: + AuthorizationException: Raised if either node or role permission is missing + """ + try: + self.projects.authorize(collection_id, role) + self.nodes.authorize(item.assets or {}, role) + + except MissingPermissionException as exc: + raise AuthorizationException(instance=f"{request_id}:{event_id}") from exc + + def add(self, entitlements: list[str]) -> None: + """add entitlements to Authorizer. + + Args: + entitlements (list[str]): list of entitlements to be added + """ + for entitlement in entitlements: + match = re.search(self.regex, entitlement) + if match is None: + continue + + try: + if match.group("type") == "project": + self.projects.add( + Project( + id=match.group("id"), + roles=[match.group("role")], + ) + ) + + elif match.group("type") == "node": + self.nodes.add( + Node( + id=match.group("id"), + roles=[match.group("role")], + ) + ) + + except ValidationError: + logger.info("Entitlement skipped: %s", entitlement) diff --git a/src/authorizer/globus_authorizer.py b/src/authorizer/globus_authorizer.py index eac4abb..970df18 100644 --- a/src/authorizer/globus_authorizer.py +++ b/src/authorizer/globus_authorizer.py @@ -4,12 +4,15 @@ from dataclasses import dataclass from threading import Lock +import urllib3 +from esgf_core_utils.models.kafka.events import RequesterData from fastapi import Request from fastapi.responses import JSONResponse from globus_sdk import AccessTokenAuthorizer, GroupsClient from globus_sdk.scopes import GroupsScopes from starlette.middleware.base import BaseHTTPMiddleware +from authorizer.globus_auth import GlobusAuth from settings import settings logger = logging.getLogger("uvicorn.error") @@ -17,10 +20,7 @@ """ FastAPI Middleware Authorizer Authorizer type: FastAPI Middleware - Event payload: Token Token source: Authorization - Token validation: ^Bearer\\s[^\\s]+$ # noqa: W605 - ^Bearer\\s[0-9A-Za-z]+$ for access tokens issued by Globus Auth (?) # noqa: W605 Authorization caching: 300 seconds (TRANSACTION_CLIENT__AUTHORIZER_CACHE_TTL_SECONDS) """ @@ -77,6 +77,80 @@ def _evict_expired(self) -> None: _auth_cache = _AuthTTLCache() +@dataclass +class _CachedPolicy: + expires_at: float + policy: list[str] + + +_policy_cache: _CachedPolicy | None = None +_policy_lock = Lock() + + +def _load_access_control_policy(policy_path: str) -> list[str]: + logger.info("Loading access control policy from %s", policy_path) + parsed = urllib3.util.parse_url(policy_path) + if parsed.scheme == "file": + with open(parsed.path, encoding="utf-8") as file: + text = file.read() + else: + http = urllib3.PoolManager() + response = http.request("GET", policy_path) + if response.status != 200: + raise RuntimeError(f"Failed to load access control policy from {policy_path}: HTTP {response.status}") + text = response.data.decode("utf-8") + + lines = [line.strip() for line in text.splitlines() if line.strip()] + return lines + + +def get_access_control_policy() -> list[str]: + """Return access control policy, reloading from policy_path when cache expires.""" + global _policy_cache + + ttl = settings.client.policy_cache_ttl_seconds + now = time.monotonic() + + with _policy_lock: + if _policy_cache is not None and now < _policy_cache.expires_at: + return _policy_cache.policy + stale_policy = _policy_cache.policy if _policy_cache is not None else None + + try: + policy = _load_access_control_policy(settings.client.policy_path) + except Exception as exc: + if stale_policy is not None: + logger.warning("Access control policy refresh failed, using stale cache: %s", exc) + return stale_policy + raise + + with _policy_lock: + _policy_cache = _CachedPolicy(expires_at=now + ttl, policy=policy) + + return policy + + +def _authorizer_context(auth: dict) -> GlobusAuth: + """Build GlobusAuth from token auth and cached access control policy entitlements.""" + token_info = auth["token_info"] + user_group_ids = {group["group_id"] for group in auth["groups"]} + + entitlements = [entitlement for entitlement in get_access_control_policy() if entitlement.rsplit(":group:", 1)[-1] in user_group_ids] + + authorizer = GlobusAuth( + requester_data=RequesterData( + client_id=token_info.get("client_id"), + sub=token_info.get("sub"), + iss=token_info.get("iss"), + ), + regex=settings.client.regex, + ) + + authorizer.add(entitlements) + + return authorizer + + def _cache_ttl_seconds(token_info: dict, max_ttl: int) -> int: exp = token_info.get("exp") if exp is None: @@ -108,7 +182,7 @@ async def dispatch(self, request: Request, call_next): access_token = authorization_header[7:].strip() cached_auth = _auth_cache.get(access_token) if cached_auth is not None: - request.state.authorizer = cached_auth + request.state.authorizer = _authorizer_context(cached_auth) return await call_next(request) response = settings.client.confidential_client.oauth2_token_introspect(access_token, include="identity_set_detail") @@ -131,7 +205,7 @@ async def dispatch(self, request: Request, call_next): } ttl = _cache_ttl_seconds(token_info, settings.client.authorizer_cache_ttl_seconds) _auth_cache.set(access_token, auth, ttl) - request.state.authorizer = auth + request.state.authorizer = _authorizer_context(auth) return await call_next(request) def _validate_token_info(self, token_info: dict) -> JSONResponse | None: @@ -169,7 +243,6 @@ def get_groups(self, token): Amazon API Gateway Authorization caching setting can be use to cache the authorizer response, and if the a new request with the same bearer token """ - tokens = settings.client.confidential_client.oauth2_get_dependent_tokens(token, scope=GroupsScopes.view_my_groups_and_memberships) groups_token = tokens.by_resource_server[GroupsClient.resource_server] authorizer = AccessTokenAuthorizer(groups_token["access_token"]) diff --git a/src/client.py b/src/client.py index 4df0170..5bc35b8 100644 --- a/src/client.py +++ b/src/client.py @@ -3,7 +3,8 @@ from datetime import datetime from typing import Optional, Union -from esgf_core_utils.models.auth import Authorizer + +from authorizer.globus_auth import GlobusAuth from esgf_core_utils.models.exceptions import ( AuthorizationException, ExpectedExtensionsMissingException, @@ -23,10 +24,10 @@ Metadata, PatchPayload, Publisher, - RequesterData, ) from esgf_core_utils.models.kafka.producer import KafkaProducer from fastapi import Request, Response, status +from src.authorizer import Authorizer from stac_fastapi.extensions.transaction import BaseTransactionsClient from stac_fastapi.extensions.transaction.request import PartialItem, PatchOperation from stac_fastapi.types.stac import Collection @@ -54,61 +55,39 @@ class TransactionClient(BaseTransactionsClient): def __init__(self): self.producer = KafkaProducer() - def allowed_groups(self, properties, acp) -> list: - if isinstance(acp, list): - return acp - for facet, subpolicy in acp.items(): - if hasattr(properties, facet): - property_value = getattr(properties, facet) - if isinstance(property_value, str): - property_value = [property_value] - matches = list(set(property_value) & set(subpolicy.keys())) - for match in matches: - groups = self.allowed_groups(properties, subpolicy[match]) - if groups: - return groups - return [] - - def globus_authorize(self, item: Item, request: Request, collection_id: str) -> dict: - properties = item.properties - - if item.collection != collection_id: - raise ValueError("Item collection must match path collection_id") - if getattr(properties, "project", None) != collection_id: - raise ValueError("Item project must match path collection_id") - - allowed_groups = self.allowed_groups(properties, settings.client.access_control_policy) - allowed_groups_uuid = [g.get("uuid") for g in allowed_groups] - - authorizer = request.state.authorizer - token_info = authorizer.get("token_info") - user_groups = authorizer.get("groups") - - authorized_identities = [] - for group in user_groups: - if group.get("group_id") in allowed_groups_uuid: - authorized_identities.append( - { - "group_id": group.get("group_id"), - "identity_id": group.get("identity_id"), - } - ) - if not authorized_identities: - raise MissingPermissionException(permission_type="globus", target=collection_id) - - requester_data = RequesterData( - client_id=token_info.get("client_id"), - sub=token_info.get("sub"), - iss=token_info.get("iss"), - ) + def globus_authorize( + self, + collection_id: str, + item: Item, + role: str, + request: Request, + request_id: str, + event_id: str, + ) -> Auth: + """Auhorise request with Globus Auth - logger.info("REQUESTER DATA: %s", requester_data) + Args: + item (Item): item to check authorization for + role (str): role to check authorization for + request (Request): current request - auth = Auth( - requester_data=requester_data, + Returns: + Auth: Auth object if successful + """ + authorizer: GlobusAuth = request.state.authorizer + authorizer.authorize( + collection_id=collection_id, + item=item, + role=role, + request_id=request_id, + event_id=event_id, ) - return auth + logger.info("REQUESTER DATA: %s", authorizer.requester_data) + + return Auth( + requester_data=authorizer.requester_data.model_dump(), + ) def egi_authorize( self, @@ -155,7 +134,14 @@ def authorize( ) -> Auth: if settings.authorizer == "globus": - return self.globus_authorize(collection_id=collection_id, item=item, request=request) + return self.globus_authorize( + collection_id=collection_id, + item=item, + role=role, + request=request, + request_id=request_id, + event_id=event_id, + ) else: return self.egi_authorize( collection_id=collection_id, @@ -313,11 +299,16 @@ async def patch_item( user_agent = headers.get("user-agent", "/").split("/") + if isinstance(patch, list): + patch_body = [op.model_dump() for op in patch] + else: + patch_body = item.model_dump() + payload = PatchPayload( method="PATCH", collection_id=collection_id, item_id=item_id, - patch=patch_adapter.dump_python(patch), + patch=patch_body, ) data = Data(type="STAC", payload=payload) diff --git a/src/settings/config/access_control_policy.json b/src/settings/config/access_control_policy.json deleted file mode 100644 index 1476eaf..0000000 --- a/src/settings/config/access_control_policy.json +++ /dev/null @@ -1,1655 +0,0 @@ -{ - "project": { - "CMIP6": { - "cmip6:institution_id": { - "ALCF": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AS-RCEC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "EC-Earth-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "LLNL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AER": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AWI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "BCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CAMS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CAS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CCCma": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CCCR-IITM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CMCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CNRM-CERFACS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-ARCCSS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-COSIMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DKRZ": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DWD": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "E3SM-Project": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "FIO-QLNM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "HAMMOZ-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "INM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IPSL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "KIOST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MIROC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MOHC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MPI-M": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MRI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-GISS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCAR": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NERC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIMS-KMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIWA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NOAA-GFDL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NTU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NUIST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RTE-RRTMGP-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RUBISCO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "SNU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "THU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCSB": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UHH": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ] - } - }, - "obs4MIPs": { - "obs4mips:institution_id": { - "CNES": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "ECMWF": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "ESSO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MOHC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-GSFC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-JPL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-LaRC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NOAA-ESRL-PSD": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NOAA-NCEI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RSS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ] - } - }, - "CMIP6Plus": { - "cmip6plus:institution_id": { - "AER": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CNES": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CNRM-CERFACS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-ARCCSS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-COSIMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DKRZ": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DLR-BIRA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DWD": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "E3SM-Project": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "EAWAG": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "EC-Earth-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "ECMWF": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "ESPRI-IPSL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "ESSO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "FIO-QLNM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "FMI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "FUBerlin": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "HAMMOZ-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IACETH": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IAMC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IFM-GEOMAR": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IMPERIALCOLLEGE": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "INCOIS-NIO-IPSL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "INM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IPSL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "ISSI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "JAXA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "KIOST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "KIT": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "LLNL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "LPC2E": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MESSy-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MIROC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MOHC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MPI-B": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MPI-M": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MPS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MRI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-GISS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-GSFC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-JPL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-LaRC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCAR": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCAS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NERC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIMS-KMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIWA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NOAA-GFDL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NOAA-NCEI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NTU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NUIST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "OSU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "PCMDI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "PMOD": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "PNNL-JGCRI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "PNNL-WACCEM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RSS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RTE-RRTMGP-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RUBISCO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "SNU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "SOLARIS-HEPPA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "THU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCSB": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCSD-SIO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UColorado": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UHH": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UReading": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UW": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UoBergen": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UoLeeds": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UoM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UoMontreal": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UoOtago": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UoOulu": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UofMD": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "VUA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ] - } - }, - "CMIP7": { - "cmip7:institution_id": { - "ALCF": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AS-RCEC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "EC-Earth-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "LLNL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AER": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AWI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "BCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CAMS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CAS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CCCma": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CCCR-IITM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CMCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CNRM-CERFACS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-ARCCSS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-COSIMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DKRZ": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DWD": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "E3SM-Project": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "FIO-QLNM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "HAMMOZ-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "INM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IPSL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "KIOST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MIROC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MOHC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MPI-M": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MRI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-GISS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCAR": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NERC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIMS-KMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIWA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NOAA-GFDL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NTU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NUIST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RTE-RRTMGP-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RUBISCO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "SNU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "THU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCSB": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UHH": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ] - } - }, - "CORDEX-CMIP6": { - "cordex-cmip6:institution_id": { - "AER": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AS-RCEC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "AWI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "BCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "BNU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CAMS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CAS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CCCR-IITM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CCCma": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CMCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CNRM-CERFACS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIR-Wits-CSIRO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-ARCCSS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "CSIRO-COSIMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DKRZ": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "DWD": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "E3SM-Project": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "EC-Earth-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "ECMWF": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "FIO-QLNM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "HAMMOZ-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "INM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "INPE": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "IPSL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "KIOST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "LLNL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MESSy-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MIROC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MOHC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MPI-M": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "MRI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-GISS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NASA-GSFC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCAR": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NCC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NERC": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIMS-KMA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NIWA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NOAA-GFDL": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NTU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "NUIST": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "PCMDI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "PNNL-WACCEM": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RTE-RRTMGP-Consortium": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "RUBISCO": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "SNU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "THU": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UA": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UCI": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UHH": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UTAS": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ], - "UofT": [ - { - "name": "ESGFNG Integration", - "uuid": "e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3", - "url": "https://app.globus.org/groups/e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3" - } - ] - } - } - } -} diff --git a/src/settings/config/access_control_policy.txt b/src/settings/config/access_control_policy.txt new file mode 100644 index 0000000..3731200 --- /dev/null +++ b/src/settings/config/access_control_policy.txt @@ -0,0 +1,10 @@ +project:*:role=CREATE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +project:*:role=UPDATE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +project:*:role=REPLICATE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +project:*:role=DELETE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +project:*:role=REVOKE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +node:*:role=CREATE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +node:*:role=UPDATE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +node:*:role=REPLICATE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +node:*:role=DELETE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 +node:*:role=REVOKE:group:e3329078-b8f6-11f0-9fdd-0e7d9e9fc9e3 diff --git a/src/settings/example.env b/src/settings/example.env index 958b66e..e5a9972 100644 --- a/src/settings/example.env +++ b/src/settings/example.env @@ -11,4 +11,4 @@ TRANSACTION_CLIENT__GLOBUS_CLIENT_SECRET=GLOBUS_CLIENT_SECRET TRANSACTION_CLIENT__GLOBUS_ISSUER=issuer TRANSACTION_CLIENT__GLOBUS_SCOPE_STRING=scope_string TRANSACTION_CLIENT__GLOBUS_AUTHORIZER=athorizer -TRANSACTION_CLIENT__GLOBUS_POLICY_PATH=/policy/path +TRANSACTION_CLIENT__GLOBUS_POLICY_PATH=file:///var/task/settings/config/new_acp.txt diff --git a/src/settings/globus.py b/src/settings/globus.py index 5fe4461..8b5866f 100644 --- a/src/settings/globus.py +++ b/src/settings/globus.py @@ -1,8 +1,5 @@ -import json from typing import Any, Self -import boto3 -import urllib3 from globus_sdk import ConfidentialAppAuthClient from pydantic import BaseModel, model_validator @@ -17,66 +14,19 @@ class GlobusClientSettings(BaseModel): issuer: str scope_string: str - access_control_policy: dict confidential_client: Any policy_path: str - secret_name: str = "transaction-api/integration" - region: str = "us-east-1" authorizer_cache_ttl_seconds: int = 300 - - def load_access_control_policy(policy_path: str) -> dict: - """load access control policy - - Args: - data (dict): inital data - - Returns: - dict: data with access control policy - """ - parsed = urllib3.util.parse_url(policy_path) - if parsed.scheme == "file": - with open(parsed.path) as file: - print("Access Control Policy loaded") - return json.load(file) - else: - http = urllib3.PoolManager() - response = http.request("GET", policy_path) - if response.status == 200: - print("Access Control Policy loaded") - return json.loads(response.data.decode("utf-8")) - - def load_secrets(data: dict) -> dict: - """load secrets from AWS - - Args: - data (dict): inital data - - Returns: - dict: data with secrets - """ - try: - session = boto3.session.Session() - client = session.client("secretsmanager", region_name=data["region"]) - response = client.get_secret_value(SecretId=data["secret_name"]) - secrets = json.loads(response["SecretString"]) - except Exception as e: - print(f"Error loading secrets: {e}") - secrets = {} - - for key, value in secrets.items(): - data[key] = value - - return data + policy_cache_ttl_seconds: int = 300 + regex: str = r"(?P[^:]*)\:(?P[^:]*)(\:institution\:(?P[^:]*))?" r"\:role=(?P[^:]*)\:group\:(?P[^:]*)" @model_validator(mode="before") @classmethod def pre_load(self, data: dict) -> Self: """ - Load the access control policy, secrets, and confidential_client. + Create confidential_client. """ - data["access_control_policy"] = self.load_access_control_policy(data["policy_path"]) - data["confidential_client"] = ConfidentialAppAuthClient( client_id=data["client_id"], client_secret=data["client_secret"], diff --git a/src/utils.py b/src/utils.py index 624b3c9..a064df4 100644 --- a/src/utils.py +++ b/src/utils.py @@ -4,6 +4,7 @@ import httpx import jsonschema +from jsonschema.exceptions import relevance from esgf_core_utils.models.exceptions import ( ExpectedExtensionsMissingException, ExtensionBelowMinimumException, @@ -225,6 +226,8 @@ def validate_geometry(geometry: dict) -> None: def validate_patch( + event_id: str, + request_id: str, item_id: str, item: PartialItem, extensions: list[str], @@ -294,11 +297,26 @@ def validate_post( for extension in extensions: extension_validator = get_extension_validator(str(extension)) - raise_errors = [] - for error in extension_validator.iter_errors(json.loads(item.model_dump_json())): - raise_errors.append(error) + raise_errors = sorted( + extension_validator.iter_errors(json.loads(item.model_dump_json())), + key=relevance, + ) if raise_errors: - logger.error("STAC validation error: %s", item_id) - - raise STACValidationException() + error_parts = [] + for error in raise_errors: + # anyOf/oneOf failures expose root causes in context (jsonschema docs). + if error.context: + error_parts.append(error.context[0].message) + else: + error_parts.append(error.message) + + validation_errors = "; ".join(error_parts) + detail = ( + "Your request is invalid -- please ensure your request is valid and try again. " + f"Item `{item_id}` failed validation against `{extension}`: {validation_errors}" + ) + logger.error(f"STAC validation error: {item_id}") + exc = STACValidationException() + exc.detail = detail + raise exc