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

Handle initially missing secrets

parent 430c8dc9
No related branches found
No related tags found
1 merge request!5Handle initially missing secrets
%% Cell type:markdown id:331918b2-c03c-4d14-9737-d65b94ed7e73 tags: %% Cell type:markdown id:331918b2-c03c-4d14-9737-d65b94ed7e73 tags:
# Add MAAP Secrets for Earthdata Login Credentials # 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. 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: %% Cell type:code id:cbdb9c92-6219-4d58-b9f4-206ee507ce51 tags:
``` python ``` python
from maap.maap import MAAP from maap.maap import MAAP
``` ```
%% Cell type:code id:e2f0c041-646c-47e3-9c9f-4937ed9f7907 tags: %% Cell type:code id:e2f0c041-646c-47e3-9c9f-4937ed9f7907 tags:
``` python ``` python
def mask(value: str, *, show_last: int = 4) -> str: def mask(value: str, *, show_last: int = 4) -> str:
"""Mask a value. """Mask a value.
Parameters Parameters
---------- ----------
value: value:
Value to mask. Value to mask.
show_last: show_last:
Number of trailing characters to leave unmasked. Number of trailing characters to leave unmasked.
Returns Returns
------- -------
Masked value, where all but `show_last` characters of the specified value are 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 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 with asterisks to ensure there are at least as many asterisks as there are
unmasked trailing characters. unmasked trailing characters.
""" """
# Make sure the returned value contains at least the same number of masked # Make sure the returned value contains at least the same number of masked
# characters as unmasked characters for a bit of added "security." # characters as unmasked characters for a bit of added "security."
n_masked = max(len(value), 2 * show_last) - show_last n_masked = max(len(value), 2 * show_last) - show_last
return f"{'*' * n_masked}{value[-show_last:]}" return f"{'*' * n_masked}{value[-show_last:]}"
def prompt_user( def prompt_user(
prompt: str, prompt: str,
*, *,
value: str | None = None, value: str | None = None,
sensitive: bool = True, sensitive: bool = True,
) -> str | None: ) -> str | None:
"""Prompt user for a possibly sensitive input value. """Prompt user for a possibly sensitive input value.
Parameters Parameters
---------- ----------
prompt: prompt:
Human-readable prompt to present to the user, which is combined with `value` Human-readable prompt to present to the user, which is combined with `value`
when prompting the user. when prompting the user.
value: value:
Current value (if any) of what to prompt the user for, which is combined with 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`. `prompt` when prompting the user, and masked, if `senstive` is `True`.
sensitive: sensitive:
If `True`, mask the current value (if any) when prompting the user, and also 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 avoid echoing user input. Otherwise, the current value is left unmasked and
user input is echoed. user input is echoed.
Returns Returns
------- -------
Value entered by the user at the prompt, or `None` if either the user pressed the 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. Return or Enter key without supplying any input, or a KeyboardInterrupt occurred.
""" """
from getpass import getpass from getpass import getpass
from IPython.display import clear_output 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 get_user_input = getpass if sensitive else input
try: try:
return get_user_input(f"{prompt} [{masked_value}]:") or None return get_user_input(f"{prompt} [{masked_value}]:") or None
except KeyboardInterrupt: except KeyboardInterrupt:
clear_output() # Remove prompt from cell output clear_output() # Remove prompt from cell output
raise raise
def get_secret(maap: MAAP, name: str) -> str | None: def get_secret(maap: MAAP, name: str) -> str | None:
"""Get the value of a MAAP secret, or `None` if the secret does not exist. """Get the value of a MAAP secret, or `None` if the secret does not exist.
Parameters Parameters
---------- ----------
maap: maap:
MAAP instance to use for secrets management. MAAP instance to use for secrets management.
name: name:
Name of the secret to get the value of. Name of the secret to get the value of.
Returns Returns
------- -------
Value of the secret, if it exists; otherwise `None`. Value of the secret, if it exists; otherwise `None`.
""" """
# Calling maap.secrets.get_secret returns the value of the requested secret, if it # 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, # exists, but oddly returns an object with an error code if it does not exist,
# rather than simply (and conveniently) returning `None`. # rather than simply (and conveniently) returning `None`.
return value if isinstance(value := maap.secrets.get_secret(name), str) else None return value if isinstance(value := maap.secrets.get_secret(name), str) else None
def set_secret_interactively( def set_secret_interactively(
maap: MAAP, maap: MAAP,
*, *,
prompt: str, prompt: str,
name: str, name: str,
sensitive: bool = True, sensitive: bool = True,
) -> str | None: ) -> str | None:
"""Set the value of a secret by prompting the user for a value. """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, 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 which will be masked if `sensitive` is `True` (showing only the last 4
characters of the value). characters of the value).
If an empty input is provided (i.e., either the Enter or Return key is pressed at 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 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 not set and `None` is returned. Otherwise, the secret is set to the user-supplied
value and that value is returned. value and that value is returned.
Parameters Parameters
---------- ----------
maap: maap:
MAAP instance to use for secrets management. MAAP instance to use for secrets management.
prompt: prompt:
Human-readable prompt to display to user when prompting for input. Human-readable prompt to display to user when prompting for input.
name: name:
Name of the secret. Name of the secret.
sensitive: sensitive:
If `True`, the prompt will mask the current secret value, if there is one. 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 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. full secret value, if there is one, and user input will be echoed.
Returns Returns
------- -------
User-supplied input provided at the prompt, or `None` if the Enter or Return key User-supplied input provided at the prompt, or `None` if the Enter or Return key
was pressed without input. was pressed without input.
""" """
if (value := prompt_user(prompt, value=get_secret(maap, name), sensitive=sensitive)): if (value := prompt_user(prompt, value=get_secret(maap, name), sensitive=sensitive)):
maap.secrets.add_secret(name, value) maap.secrets.add_secret(name, value)
masked_value = mask(value) if sensitive else value masked_value = mask(value) if sensitive else value
print(f"The secret {name} was set to {masked_value}.") print(f"The secret {name} was set to {masked_value}.")
return value return value
print(f"The value for secret {name} was left unchanged.") print(f"The value for secret {name} was left unchanged.")
``` ```
%% Cell type:markdown id:97611ae6-837e-4021-9e4e-bee447749d5e tags: %% Cell type:markdown id:97611ae6-837e-4021-9e4e-bee447749d5e tags:
## Create MAAP Instance for Managing Secrets ## Create MAAP Instance for Managing Secrets
%% Cell type:code id:10612951-3392-4e32-983e-6e9a85c8c938 tags: %% Cell type:code id:10612951-3392-4e32-983e-6e9a85c8c938 tags:
``` python ``` python
maap = MAAP() maap = MAAP()
``` ```
%% Cell type:markdown id:98d52db0-7187-4aa3-a24a-7f3cbfce63c5 tags: %% Cell type:markdown id:98d52db0-7187-4aa3-a24a-7f3cbfce63c5 tags:
## Store Earthdata Login Credentials as MAAP Secrets ## Store Earthdata Login Credentials as MAAP Secrets
%% Cell type:code id:986a47cb-0586-4f62-827b-6df6fcb609d4 tags: %% Cell type:code id:986a47cb-0586-4f62-827b-6df6fcb609d4 tags:
``` python ``` python
set_secret_interactively( set_secret_interactively(
maap, prompt="Earthdata Login username", name="EARTHDATA_USERNAME", sensitive=False maap, prompt="Earthdata Login username", name="EARTHDATA_USERNAME", sensitive=False
) )
set_secret_interactively( set_secret_interactively(
maap, prompt="Earthdata Login password", name="EARTHDATA_PASSWORD" 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