Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
{
"cells": [
{
"cell_type": "markdown",
"id": "331918b2-c03c-4d14-9737-d65b94ed7e73",
"metadata": {},
"source": [
"# Add MAAP Secrets for Earthdata Login Credentials\n",
"\n",
"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",
"execution_count": null,
"id": "cbdb9c92-6219-4d58-b9f4-206ee507ce51",
"metadata": {},
"outputs": [],
"source": [
"from maap.maap import MAAP"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2f0c041-646c-47e3-9c9f-4937ed9f7907",
"metadata": {},
"outputs": [],
"source": [
"def mask(value: str, *, show_last: int = 4) -> str:\n",
" \"\"\"Mask a value.\n",
" \n",
" Parameters\n",
" ----------\n",
" value:\n",
" Value to mask.\n",
" show_last:\n",
" Number of trailing characters to leave unmasked.\n",
" \n",
" Returns\n",
" -------\n",
" Masked value, where all but `show_last` characters of the specified value are\n",
" replaced with an asterisk (`*`). If necessary, the value is further left-padded\n",
" with asterisks to ensure there are at least as many asterisks as there are\n",
" unmasked trailing characters.\n",
" \"\"\"\n",
"\n",
" # Make sure the returned value contains at least the same number of masked\n",
" # characters as unmasked characters for a bit of added \"security.\"\n",
" n_masked = max(len(value), 2 * show_last) - show_last\n",
" \n",
" return f\"{'*' * n_masked}{value[-show_last:]}\"\n",
"\n",
"\n",
"def prompt_user(\n",
" prompt: str,\n",
" *,\n",
" value: str | None = None,\n",
" sensitive: bool = True,\n",
") -> str | None:\n",
" \"\"\"Prompt user for a possibly sensitive input value.\n",
"\n",
" Parameters\n",
" ----------\n",
" prompt:\n",
" Human-readable prompt to present to the user, which is combined with `value`\n",
" when prompting the user.\n",
" value:\n",
" Current value (if any) of what to prompt the user for, which is combined with\n",
" `prompt` when prompting the user, and masked, if `senstive` is `True`.\n",
" sensitive:\n",
" If `True`, mask the current value (if any) when prompting the user, and also\n",
" avoid echoing user input. Otherwise, the current value is left unmasked and\n",
" user input is echoed.\n",
"\n",
" Returns\n",
" -------\n",
" Value entered by the user at the prompt, or `None` if either the user pressed the\n",
" Return or Enter key without supplying any input, or a KeyboardInterrupt occurred.\n",
" \"\"\"\n",
" from getpass import getpass\n",
" from IPython.display import clear_output\n",
"\n",
" masked_value = mask(value) if value and sensitive else value\n",
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
" get_user_input = getpass if sensitive else input\n",
"\n",
" try:\n",
" return get_user_input(f\"{prompt} [{masked_value}]:\") or None\n",
" except KeyboardInterrupt:\n",
" clear_output() # Remove prompt from cell output\n",
" raise\n",
"\n",
" \n",
"def get_secret(maap: MAAP, name: str) -> str | None:\n",
" \"\"\"Get the value of a MAAP secret, or `None` if the secret does not exist.\n",
" \n",
" Parameters\n",
" ----------\n",
" maap:\n",
" MAAP instance to use for secrets management.\n",
" name:\n",
" Name of the secret to get the value of.\n",
"\n",
" Returns\n",
" -------\n",
" Value of the secret, if it exists; otherwise `None`.\n",
" \"\"\"\n",
"\n",
" # Calling maap.secrets.get_secret returns the value of the requested secret, if it\n",
" # exists, but oddly returns an object with an error code if it does not exist,\n",
" # rather than simply (and conveniently) returning `None`.\n",
" \n",
" return value if isinstance(value := maap.secrets.get_secret(name), str) else None\n",
"\n",
"\n",
"def set_secret_interactively(\n",
" maap: MAAP,\n",
" *,\n",
" prompt: str,\n",
" name: str,\n",
" sensitive: bool = True,\n",
") -> str | None:\n",
" \"\"\"Set the value of a secret by prompting the user for a value.\n",
"\n",
" If the secret already exists, the user prompt will include the existing value,\n",
" which will be masked if `sensitive` is `True` (showing only the last 4\n",
" characters of the value).\n",
"\n",
" If an empty input is provided (i.e., either the Enter or Return key is pressed at\n",
" the prompt, without any other input, or a KeyboardInterrupt occurs), the secret is\n",
" not set and `None` is returned. Otherwise, the secret is set to the user-supplied\n",
" value and that value is returned.\n",
"\n",
" Parameters\n",
" ----------\n",
" maap:\n",
" MAAP instance to use for secrets management.\n",
" prompt:\n",
" Human-readable prompt to display to user when prompting for input.\n",
" name:\n",
" Name of the secret.\n",
" sensitive:\n",
" If `True`, the prompt will mask the current secret value, if there is one.\n",
" Further, user input will not be echoed. Otherwise, the prompt will show the\n",
" full secret value, if there is one, and user input will be echoed.\n",
"\n",
" Returns\n",
" -------\n",
" User-supplied input provided at the prompt, or `None` if the Enter or Return key\n",
" was pressed without input.\n",
" \"\"\"\n",
" if (value := prompt_user(prompt, value=get_secret(maap, name), sensitive=sensitive)):\n",
" maap.secrets.add_secret(name, value)\n",
" masked_value = mask(value) if sensitive else value\n",
" print(f\"The secret {name} was set to {masked_value}.\")\n",
" return value\n",
"\n",
" print(f\"The value for secret {name} was left unchanged.\")"
]
},
{
"cell_type": "markdown",
"id": "97611ae6-837e-4021-9e4e-bee447749d5e",
"metadata": {},
"source": [
"## Create MAAP Instance for Managing Secrets"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10612951-3392-4e32-983e-6e9a85c8c938",
"metadata": {},
"outputs": [],
"source": [
"maap = MAAP()"
]
},
{
"cell_type": "markdown",
"id": "98d52db0-7187-4aa3-a24a-7f3cbfce63c5",
"metadata": {},
"source": [
"## Store Earthdata Login Credentials as MAAP Secrets"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "986a47cb-0586-4f62-827b-6df6fcb609d4",
"metadata": {},
"outputs": [],
"source": [
"set_secret_interactively(\n",
" maap, prompt=\"Earthdata Login username\", name=\"EARTHDATA_USERNAME\", sensitive=False\n",
")\n",
"set_secret_interactively(\n",
" maap, prompt=\"Earthdata Login password\", name=\"EARTHDATA_PASSWORD\"\n",
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}