Skip to content
Snippets Groups Projects
Commit e839ada6 authored by Charles Daniels's avatar Charles Daniels
Browse files

Handle initially missing secrets

parent 430c8dc9
Branches main
No related tags found
No related merge requests found
%% Cell type:markdown id:331918b2-c03c-4d14-9737-d65b94ed7e73 tags:
# Add MAAP Secrets for Earthdata Login Credentials
Earthdata Login credentials are required for downloading files from NASA DAACs. In order for a DPS job to be able to use such credentials in a safe manner, it looks for them in the MAAP Secrets store. Therefore, you must add the necessary secrets before a DPS job that requires them is submitted, otherwise the job will fail due to the missing secrets.
%% Cell type:code id:cbdb9c92-6219-4d58-b9f4-206ee507ce51 tags:
``` python
from maap.maap import MAAP
```
%% Cell type:code id:e2f0c041-646c-47e3-9c9f-4937ed9f7907 tags:
``` python
def mask(value: str, *, show_last: int = 4) -> str:
"""Mask a value.
Parameters
----------
value:
Value to mask.
show_last:
Number of trailing characters to leave unmasked.
Returns
-------
Masked value, where all but `show_last` characters of the specified value are
replaced with an asterisk (`*`). If necessary, the value is further left-padded
with asterisks to ensure there are at least as many asterisks as there are
unmasked trailing characters.
"""
# Make sure the returned value contains at least the same number of masked
# characters as unmasked characters for a bit of added "security."
n_masked = max(len(value), 2 * show_last) - show_last
return f"{'*' * n_masked}{value[-show_last:]}"
def prompt_user(
prompt: str,
*,
value: str | None = None,
sensitive: bool = True,
) -> str | None:
"""Prompt user for a possibly sensitive input value.
Parameters
----------
prompt:
Human-readable prompt to present to the user, which is combined with `value`
when prompting the user.
value:
Current value (if any) of what to prompt the user for, which is combined with
`prompt` when prompting the user, and masked, if `senstive` is `True`.
sensitive:
If `True`, mask the current value (if any) when prompting the user, and also
avoid echoing user input. Otherwise, the current value is left unmasked and
user input is echoed.
Returns
-------
Value entered by the user at the prompt, or `None` if either the user pressed the
Return or Enter key without supplying any input, or a KeyboardInterrupt occurred.
"""
from getpass import getpass
from IPython.display import clear_output
masked_value = mask(value) if sensitive else value
masked_value = mask(value) if value and sensitive else value
get_user_input = getpass if sensitive else input
try:
return get_user_input(f"{prompt} [{masked_value}]:") or None
except KeyboardInterrupt:
clear_output() # Remove prompt from cell output
raise
def get_secret(maap: MAAP, name: str) -> str | None:
"""Get the value of a MAAP secret, or `None` if the secret does not exist.
Parameters
----------
maap:
MAAP instance to use for secrets management.
name:
Name of the secret to get the value of.
Returns
-------
Value of the secret, if it exists; otherwise `None`.
"""
# Calling maap.secrets.get_secret returns the value of the requested secret, if it
# exists, but oddly returns an object with an error code if it does not exist,
# rather than simply (and conveniently) returning `None`.
return value if isinstance(value := maap.secrets.get_secret(name), str) else None
def set_secret_interactively(
maap: MAAP,
*,
prompt: str,
name: str,
sensitive: bool = True,
) -> str | None:
"""Set the value of a secret by prompting the user for a value.
If the secret already exists, the user prompt will include the existing value,
which will be masked if `sensitive` is `True` (showing only the last 4
characters of the value).
If an empty input is provided (i.e., either the Enter or Return key is pressed at
the prompt, without any other input, or a KeyboardInterrupt occurs), the secret is
not set and `None` is returned. Otherwise, the secret is set to the user-supplied
value and that value is returned.
Parameters
----------
maap:
MAAP instance to use for secrets management.
prompt:
Human-readable prompt to display to user when prompting for input.
name:
Name of the secret.
sensitive:
If `True`, the prompt will mask the current secret value, if there is one.
Further, user input will not be echoed. Otherwise, the prompt will show the
full secret value, if there is one, and user input will be echoed.
Returns
-------
User-supplied input provided at the prompt, or `None` if the Enter or Return key
was pressed without input.
"""
if (value := prompt_user(prompt, value=get_secret(maap, name), sensitive=sensitive)):
maap.secrets.add_secret(name, value)
masked_value = mask(value) if sensitive else value
print(f"The secret {name} was set to {masked_value}.")
return value
print(f"The value for secret {name} was left unchanged.")
```
%% Cell type:markdown id:97611ae6-837e-4021-9e4e-bee447749d5e tags:
## Create MAAP Instance for Managing Secrets
%% Cell type:code id:10612951-3392-4e32-983e-6e9a85c8c938 tags:
``` python
maap = MAAP()
```
%% Cell type:markdown id:98d52db0-7187-4aa3-a24a-7f3cbfce63c5 tags:
## Store Earthdata Login Credentials as MAAP Secrets
%% Cell type:code id:986a47cb-0586-4f62-827b-6df6fcb609d4 tags:
``` python
set_secret_interactively(
maap, prompt="Earthdata Login username", name="EARTHDATA_USERNAME", sensitive=False
)
set_secret_interactively(
maap, prompt="Earthdata Login password", name="EARTHDATA_PASSWORD"
)
);
```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment