Handling Authorization Parameters from a Transfer Operation¶
Globus Transfer interacts with Collections and their underlying Endpoints both synchronously and asynchronously.
The example below demonstrates a synchronous interaction – ls
on a
collection – with handling for the possibility that a session-related error is
returned.
The session error can be parsed into a Globus Auth Requirements Error document
– a globus_sdk.gare.GARE
– via globus_sdk.gare
.
Once the error is recognized, the parameters parsed from it can be fed back
into a login flow.
Doing so will update the user’s session, resolving the issue.
Creating a Test Collection¶
In order to test this sample script in full, you will need a collection with a session policy which requires users to login with a specific ID or within a certain time period. The following section covers a recommended test collection configuration. This will require administrative access to a Globus Connect Server endpoint.
Note
Globus tutorial resources do not contain session policies, making them easier to use. However, this means they are not suitable for the example script given below. Tutorial collections will work, but they will not demonstrate the interesting features in use.
Create a POSIX Storage Gateway With a Timeout Policy¶
On a Globus Connect Server Endpoint which you administer, create a new Storage Gateway with a policy explicitly designed for testing. The gateway will need the following properties, with the following recommended values:
gateway type:
posix
display name:
sdk-test-policy
root path:
/
domain requirements: require a domain in which you have an identity
authentication timeout in minutes: 1
You can create a gateway with a command like the following, which uses the
uchicago.edu
domain as an example:
globus-connect-server storage-gateway create posix \
sdk-test-gateway --authentication-timeout-mins 1 \
--domain uchicago.edu
Record the storage gateway ID:
STORAGE_GATEWAY_ID="..."
Using a one minute timeout will make the session handling very easy to test. Simply interact with the collection, wait one minute, and then try using it again – you should be prompted to log in a second time to meet the timeout requirement.
Tip
You do not need to be an administrator for the domain you set, but you do need an identity from that domain. You are setting a policy for your Storage Gateway with this setting to require identities from that domain.
If you don’t have a uchicago.edu
identity, you could create a gateway
like the example, but you wouldn’t be able to use it!
Create a Mapped Collection on that Gateway¶
The Storage Gateway encodes the requisite policy. No special configuration is needed for the mapped collection used for testing. Simply create a collection on the test gateway, e.g.:
globus-connect-server collection create \
"$STORAGE_GATEWAY_ID" / sdk-test-collection
Note the collection ID, we will need it in the following steps.
Run ls
on the Collection¶
Running a simple ls
on this collection is no different from doing so on any
other collection.
We have setup a 1 minute timeout for the test collection, so this will likely
work the first time that it runs.
import globus_sdk
# this is the SDK tutorial client ID, replace with your own ID
NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
# set the collection ID to your test collection
COLLECTION_ID = "..."
USER_APP = globus_sdk.UserApp("ls-session", client_id=NATIVE_CLIENT_ID)
client = globus_sdk.TransferClient(app=USER_APP)
# because the recommended test configuration uses a mapped collection
# without High Assurance capabilities, it will have a data_access scope
# requirement
# comment out this line if your collection does not use data_access
client.add_app_data_access_scope(COLLECTION_ID)
ls_result = client.operation_ls(COLLECTION_ID)
for item in ls_result:
name = item["name"]
if item["type"] == "dir":
name += "/"
print(name)
Run Again, Observe the Session Error¶
The 1 minute timeout guarantees that we can trivially produce a session timeout error by waiting one minute and running the script again. You should see a stacktrace like the following:
Traceback (most recent call last):
File "/home/demo-user/ls_session.py", line 18, in <module>
ls_result = client.operation_ls(COLLECTION_ID)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/demo-user/.venv/lib/python3.2/globus_sdk/services/transfer/client.py", line 1286, in operation_ls
self.get(f"operation/endpoint/{endpoint_id}/ls", query_params=query_params)
File "/home/demo-user/.venv/lib/python3.2/globus_sdk/client.py", line 273, in get
return self.request(
^^^^^^^^^^^^^
File "/home/demo-user/.venv/lib/python3.2/globus_sdk/client.py", line 461, in request
raise self.error_class(r)
globus_sdk.services.transfer.errors.TransferAPIError: ('GET', 'https://transfer.api.globus.org/v0.10/operation/endpoint/2a0d28b8-d2a3-4143-9f76-93ab09497b85/ls', 'Bearer', 502, 'ExternalError.DirListingFailed.LoginFailed', 'Command Failed: Error (login)\nEndpoint: uc-restricted-col (2a0d28b8-d2a3-4143-9f76-93ab09497b85)\nServer: 44.201.14.78:443\nMessage: Login Failed\n---\nDetails: 530-Login incorrect. : GlobusError: v=1 c=LOGIN_DENIED\\r\\n530-GridFTP-Message: None of your authenticated identities are from domains allowed by resource policies\\r\\n530-GridFTP-JSON-Result: {"DATA_TYPE": "result#1.1.0", "authorization_parameters": {"session_required_single_domain": ["uchicago.edu"]}, "code": "permission_denied", "detail": {"DATA_TYPE": "not_from_allowed_domain#1.0.0", "allowed_domains": ["uchicago.edu"]}, "has_next_page": false, "http_response_code": 403, "message": "None of your authenticated identities are from domains allowed by resource policies"}\\r\\n530 End.\\r\\n\n', '7U5dNqzDn')
The exact details will vary, but the primary content of the error is that you do not satisfy the session requirements of the collection.
Parse the Error and Confirm authorization_parameters
are present¶
Handling and parsing this error requires that we add an exception handler.
To enhance readability, we will wrap the ls
operation in a function.
The caught exception will have an info
property with populated
authorization_parameters
.
We can check for this easily:
import globus_sdk
# this is the SDK tutorial client ID, replace with your own ID
NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
# set the collection ID to your test collection
COLLECTION_ID = "..."
USER_APP = globus_sdk.UserApp("ls-session", client_id=NATIVE_CLIENT_ID)
client = globus_sdk.TransferClient(app=USER_APP)
# because the recommended test configuration uses a mapped collection
# without High Assurance capabilities, it will have a data_access scope
# requirement
# comment out this line if your collection does not use data_access
client.add_app_data_access_scope(COLLECTION_ID)
def print_ls_data():
ls_result = client.operation_ls(COLLECTION_ID)
for item in ls_result:
name = item["name"]
if item["type"] == "dir":
name += "/"
print(name)
# do the `ls` and detect the authorization_parameters as being present
try:
print_ls_data()
except globus_sdk.TransferAPIError as err:
if err.info.authorization_parameters:
print("An authorization requirement was not met.")
else:
raise
Convert the Error to GARE Format¶
Globus Auth Requirements Errors (“GAREs”) are a standardized format for
Globus Auth session information to be passed between components.
When these authorization_parameters
errors are encountered, they can be
converted to the GARE format using the tooling in globus_sdk.gare
:
import globus_sdk.gare
try:
print_ls_data()
except globus_sdk.TransferAPIError as err:
if err.info.authorization_parameters:
print("An authorization requirement was not met.")
gare = globus_sdk.gare.to_gare(err)
print("GARE data:", str(gare))
else:
raise
Redrive Logins with the Parsed Authorization Params¶
Now that we have the data parsed into GARE format, it is relatively simple to
pass the parsed authorization_parameters
data to a login flow.
Because the underlying requirement in this case is a new login flow, we must
also set prompt=login
in the parameters.
Having done so, we can repeat the login step using that information:
try:
print_ls_data()
except globus_sdk.TransferAPIError as err:
if err.info.authorization_parameters:
print("An authorization requirement was not met. Logging in again...")
gare = globus_sdk.gare.to_gare(err)
params = gare.authorization_parameters
# set 'prompt=login', which guarantees a fresh login without
# reliance on the browser session
params.prompt = "login"
# pass these parameters into a login flow
USER_APP.login(auth_params=params)
else:
raise
Summary: Complete Example¶
Combining the pieces above, we can construct a working complete example script. A collection with session requirements is needed in order to demonstrate the behavior.
import globus_sdk
import globus_sdk.gare
# this is the SDK tutorial client ID, replace with your own ID
NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
# set the collection ID to your test collection
COLLECTION_ID = "..."
USER_APP = globus_sdk.UserApp("ls-session", client_id=NATIVE_CLIENT_ID)
client = globus_sdk.TransferClient(app=USER_APP)
# because the recommended test configuration uses a mapped collection
# without High Assurance capabilities, it will have a data_access scope
# requirement
# comment out this line if your collection does not use data_access
client.add_app_data_access_scope(COLLECTION_ID)
def print_ls_data():
ls_result = client.operation_ls(COLLECTION_ID)
for item in ls_result:
name = item["name"]
if item["type"] == "dir":
name += "/"
print(name)
# try to run the desired operation (`print_ls_data`)
try:
print_ls_data()
# catch the possible API error
except globus_sdk.TransferAPIError as err:
# if there are authorization parameters data in the error,
# use it to redrive login
if err.info.authorization_parameters:
print("An authorization requirement was not met. Logging in again...")
gare = globus_sdk.gare.to_gare(err)
params = gare.authorization_parameters
# set 'prompt=login', which guarantees a fresh login without
# reliance on the browser session
params.prompt = "login"
# pass these parameters into a login flow
USER_APP.login(auth_params=params)
# rerun the desired print_ls_data() operation
print_ls_data()
# otherwise, there are no authorization parameters, so reraise the error
else:
raise