From f2b6b8fde555ee535890a8e4c292d88c321ac9f0 Mon Sep 17 00:00:00 2001 From: Derek Schlabach Date: Mon, 22 Jul 2024 15:16:55 -0500 Subject: [PATCH 1/2] Added an 'Updated Example' for GCS Guest Collection Creation --- .../create_guest_collection_client_owned.py | 72 +++++++++++++++++++ ... => create_guest_collection_user_owned.py} | 23 +++++- .../create_guest_collection/index.rst | 60 ++++++++++++++-- 3 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py rename docs/experimental/examples/endpoints_and_collections/create_guest_collection/{user_guest_collection.py => create_guest_collection_user_owned.py} (62%) diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py new file mode 100644 index 000000000..7a2b24848 --- /dev/null +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py @@ -0,0 +1,72 @@ +import globus_sdk +from globus_sdk.experimental.globus_app import ClientApp + +# Confidential Client ID/Secret - +CONFIDENTIAL_CLIENT_ID = "..." +CONFIDENTIAL_CLIENT_SECRET = "..." +CLIENT_APP = ClientApp( + "my-simple-client-collection", + client_id=CONFIDENTIAL_CLIENT_ID, + client_secret=CONFIDENTIAL_CLIENT_SECRET, +) + + +# Globus Tutorial Collection 1 +# https://app.globus.org/file-manager/collections/6c54cade-bde5-45c1-bdea-f4bd71dba2cc +ENDPOINT_HOSTNAME = "https://b7a4f1.75bc.data.globus.org" +STORAGE_GATEWAY_ID = "947460f6-3fcd-4acc-9683-d71e14e5ace1" +MAPPED_COLLECTION_ID = "6c54cade-bde5-45c1-bdea-f4bd71dba2cc" + + +def main(): + gcs_client = globus_sdk.GCSClient(ENDPOINT_HOSTNAME, app=CLIENT_APP) + + # Comment out this line if the mapped collection is high assurance + attach_data_access_scope(gcs_client, MAPPED_COLLECTION_ID) + + ensure_user_credential(gcs_client) + + collection_request = globus_sdk.GuestCollectionDocument( + public=True, + collection_base_path="/", + display_name="example_guest_collection", + mapped_collection_id=MAPPED_COLLECTION_ID, + ) + + collection = gcs_client.create_collection(collection_request) + print(f"Created guest collection. Collection ID: {collection['id']}") + + +def attach_data_access_scope(gcs_client, collection_id): + """Compose and attach a ``data_access`` scope for the supplied collection""" + endpoint_scopes = gcs_client.get_gcs_endpoint_scopes(gcs_client.endpoint_client_id) + collection_scopes = gcs_client.get_gcs_collection_scopes(collection_id) + + manage_collections = globus_sdk.Scope(endpoint_scopes.manage_collections) + data_access = globus_sdk.Scope(collection_scopes.data_access, optional=True) + + manage_collections.add_dependency(data_access) + + gcs_client.add_app_scope(manage_collections) + + +def ensure_user_credential(gcs_client): + """ + Ensure that the client has a user credential on the client. + This is the mapping between Globus Auth (OAuth2) and the local system's permissions. + """ + try: + # Depending on the endpoint & storage gateway, this request document may need to + # include more complex information such as a local username. + # Consult with the endpoint owner for more detailed info on user mappings and + # other specific requirements. + req = globus_sdk.UserCredentialDocument(storage_gateway_id=STORAGE_GATEWAY_ID) + gcs_client.create_user_credential(req) + except globus_sdk.GCSAPIError as err: + # If a User Credential already exists; no need to create it. + if err.http_status != 409: + raise + + +if __name__ == "__main__": + main() diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/user_guest_collection.py b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_user_owned.py similarity index 62% rename from docs/experimental/examples/endpoints_and_collections/create_guest_collection/user_guest_collection.py rename to docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_user_owned.py index dd6aca773..6caeea394 100644 --- a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/user_guest_collection.py +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_user_owned.py @@ -3,11 +3,12 @@ # Tutorial Client ID - NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2" -USER_APP = UserApp("my-simple-transfer", client_id=NATIVE_CLIENT_ID) +USER_APP = UserApp("my-simple-user-collection", client_id=NATIVE_CLIENT_ID) # Globus Tutorial Collection 1 # https://app.globus.org/file-manager/collections/6c54cade-bde5-45c1-bdea-f4bd71dba2cc ENDPOINT_HOSTNAME = "https://b7a4f1.75bc.data.globus.org" +STORAGE_GATEWAY_ID = "947460f6-3fcd-4acc-9683-d71e14e5ace1" MAPPED_COLLECTION_ID = "6c54cade-bde5-45c1-bdea-f4bd71dba2cc" @@ -17,6 +18,8 @@ def main(): # Comment out this line if the mapped collection is high assurance attach_data_access_scope(gcs_client, MAPPED_COLLECTION_ID) + ensure_user_credential(gcs_client) + collection_request = globus_sdk.GuestCollectionDocument( public=True, collection_base_path="/", @@ -41,5 +44,23 @@ def attach_data_access_scope(gcs_client, collection_id): gcs_client.add_app_scope(manage_collections) +def ensure_user_credential(gcs_client): + """ + Ensure that the user has a user credential on the client. + This is the mapping between Globus Auth (OAuth2) and the local system's permissions. + """ + try: + # Depending on the endpoint & storage gateway, this request document may need to + # include more complex information such as a local username. + # Consult with the endpoint owner for more detailed info on user mappings and + # other particular requirements. + req = globus_sdk.UserCredentialDocument(storage_gateway_id=STORAGE_GATEWAY_ID) + gcs_client.create_user_credential(req) + except globus_sdk.GCSAPIError as err: + # If a User Credential already exists; no need to create it. + if err.http_status != 409: + raise + + if __name__ == "__main__": main() diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst index 80eaff68e..4d1aeecf5 100644 --- a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst @@ -2,8 +2,60 @@ Creating a Guest Collection =========================== -TODO +Within the Globus Ecosystem, data is managed through the abstraction of *Collections*. +The example included on this page demonstrate how to create specifically a +*Guest Collection* using the Globus Python SDK. Guest collections, formerly known as +"Shares", are collections which provide access to a subdirectory of an existing +collection through a particular user or client's local permissions. -.. literalinclude:: user_guest_collection.py - :caption: ``user_guest_collection.py`` [:download:`download `] - :language: python +Guest collections are a great way to set up data automation. They may be scoped down +to a particular directory within an existing "Mapped Collection" and don't implicitly +inherit the same authorization timeout requirements as their parent Mapped Collection. +Once created, they can be shared to other users/entities, in effect giving another +entity access, through you, to some underlying data. + +.. Note:: + + While guest collections don't implicitly inherit their parent mapped collection's + authorization timeout in some cases they do or alternatively may be disabled + entirely. This is a decision made by the endpoint owner, not Globus. + + Because requirements can vary so drastically between endpoints, we recommend + consulting with the particular endpoint's documentation and/or owner to determine + whether guest collections provide the desired level of access with the desired + minimization of authorization. + +.. Note:: + + The scripts reference a globus hosted "tutorial" mapped collection. This is just + to provide as simple of a functioning example out of the box as possible. + + For actual application, replace the ids with the actual relevant collection and + storage gateway IDs. + + +.. tab-set:: + + .. tab-item:: User-owned Collection + + This script demonstrates how to create a guest collection owned by a human. + + It will prompt the user to authenticate through a browser and authorize the + script to act on their behalf. + + .. literalinclude:: create_guest_collection_user_owned.py + :caption: ``create_guest_collection_user_owned.py`` [:download:`download `] + :language: python + + + .. tab-item:: Client-owned Collection + + This script demonstrates how to create a guest collection owned by a client ( + e.g. a service account). + + It will automatically request and use client access tokens based on the supplied + client id and secret. + + .. literalinclude:: create_guest_collection_client_owned.py + :caption: ``create_guest_collection_client_owned.py`` [:download:`download `] + :language: python From d8eb7d86eecec7c9df8a2eb427c70410469b1bf9 Mon Sep 17 00:00:00 2001 From: Derek Schlabach Date: Wed, 24 Jul 2024 11:22:16 -0500 Subject: [PATCH 2/2] PR feedback --- .../create_guest_collection_client_owned.py | 12 ++++++------ .../create_guest_collection_user_owned.py | 12 ++++++------ .../create_guest_collection/index.rst | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py index 7a2b24848..8b7cedcb5 100644 --- a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_client_owned.py @@ -55,15 +55,15 @@ def ensure_user_credential(gcs_client): Ensure that the client has a user credential on the client. This is the mapping between Globus Auth (OAuth2) and the local system's permissions. """ + # Depending on the endpoint & storage gateway, this request document may need to + # include more complex information such as a local username. + # Consult with the endpoint owner for more detailed info on user mappings and + # other specific requirements. + req = globus_sdk.UserCredentialDocument(storage_gateway_id=STORAGE_GATEWAY_ID) try: - # Depending on the endpoint & storage gateway, this request document may need to - # include more complex information such as a local username. - # Consult with the endpoint owner for more detailed info on user mappings and - # other specific requirements. - req = globus_sdk.UserCredentialDocument(storage_gateway_id=STORAGE_GATEWAY_ID) gcs_client.create_user_credential(req) except globus_sdk.GCSAPIError as err: - # If a User Credential already exists; no need to create it. + # A user credential already exists, no need to create it. if err.http_status != 409: raise diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_user_owned.py b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_user_owned.py index 6caeea394..c85fdf49c 100644 --- a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_user_owned.py +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/create_guest_collection_user_owned.py @@ -49,15 +49,15 @@ def ensure_user_credential(gcs_client): Ensure that the user has a user credential on the client. This is the mapping between Globus Auth (OAuth2) and the local system's permissions. """ + # Depending on the endpoint & storage gateway, this request document may need to + # include more complex information such as a local username. + # Consult with the endpoint owner for more detailed info on user mappings and + # other particular requirements. + req = globus_sdk.UserCredentialDocument(storage_gateway_id=STORAGE_GATEWAY_ID) try: - # Depending on the endpoint & storage gateway, this request document may need to - # include more complex information such as a local username. - # Consult with the endpoint owner for more detailed info on user mappings and - # other particular requirements. - req = globus_sdk.UserCredentialDocument(storage_gateway_id=STORAGE_GATEWAY_ID) gcs_client.create_user_credential(req) except globus_sdk.GCSAPIError as err: - # If a User Credential already exists; no need to create it. + # A user credential already exists, no need to create it. if err.http_status != 409: raise diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst index 4d1aeecf5..8a04dae91 100644 --- a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst @@ -14,7 +14,7 @@ inherit the same authorization timeout requirements as their parent Mapped Colle Once created, they can be shared to other users/entities, in effect giving another entity access, through you, to some underlying data. -.. Note:: +.. Warning:: While guest collections don't implicitly inherit their parent mapped collection's authorization timeout in some cases they do or alternatively may be disabled @@ -30,7 +30,7 @@ entity access, through you, to some underlying data. The scripts reference a globus hosted "tutorial" mapped collection. This is just to provide as simple of a functioning example out of the box as possible. - For actual application, replace the ids with the actual relevant collection and + For actual application, replace the IDs with the relevant collection and storage gateway IDs. @@ -50,11 +50,11 @@ entity access, through you, to some underlying data. .. tab-item:: Client-owned Collection - This script demonstrates how to create a guest collection owned by a client ( - e.g. a service account). + This script demonstrates how to create a guest collection owned by a client + (i.e. a service account). It will automatically request and use client access tokens based on the supplied - client id and secret. + client ID and secret. .. literalinclude:: create_guest_collection_client_owned.py :caption: ``create_guest_collection_client_owned.py`` [:download:`download `]