-
Notifications
You must be signed in to change notification settings - Fork 2
/
.bash_onepassword_specific
385 lines (330 loc) · 9.7 KB
/
.bash_onepassword_specific
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
84
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# vim: set ft=bash:
export OP_CONFIG_DIR="$HOME/.config/op"
export OP_DISABLED="${OP_DISABLED:-false}"
export OP_CACHED_CREDENTIALS_FILE="$OP_CONFIG_DIR/credentials.yaml"
_op_sops() {
if ! test -f "$OP_CACHED_CREDENTIALS_FILE"
then
log_error "1Password cached credentials file not found; run 'configure_1password_cli' to \
refresh"
return 1
fi
if test -z "$OP_CREDENTIALS_SIGNING_KEY"
then
log_error "OP_CREDENTIALS_SIGNING_KEY is not set. Set it or import from .bash_secret_exports, \
then source .bash_onepassword_specific again."
return 1
fi
sops --pgp "$OP_CREDENTIALS_SIGNING_KEY" "$@"
}
_create_op_config_dir() {
test -d "$OP_CONFIG_DIR" && return 0
mkdir -p "$OP_CONFIG_DIR"
chmod -R 700 "$OP_CONFIG_DIR"
}
_create_op_credential_cache() {
test -e "$OP_CACHED_CREDENTIALS_FILE" && return 0
cat >"$OP_CACHED_CREDENTIALS_FILE" <<-YAML
---
credentials:
1password_cli_token:
1password_otp:
1password_password:
1password_sk:
1password_device_key:
1password_email:
1password_default_vault:
YAML
_op_sops -e -i "$OP_CACHED_CREDENTIALS_FILE"
}
_get_cached_op_credential() {
key="$(printf '["credentials"]["%s"]' "$1")"
val="$(_op_sops -d --extract "$key" "$OP_CACHED_CREDENTIALS_FILE")"
test "$val" == 'null' && val=""
echo "$val"
}
_clear_cached_op_credential() {
key="$(printf '["credentials"]["%s"] ""' "$1")"
_op_sops --set "$key" "$OP_CACHED_CREDENTIALS_FILE"
}
_set_cached_op_credential() {
if test -z "$2"
then
log_error "value cannot be blank; please try again."
return 1
fi
key="$(printf '["credentials"]["%s"] "%s"' "$1" "$2")"
_op_sops --set "$key" "$OP_CACHED_CREDENTIALS_FILE"
}
_get_or_ask_for_cached_op_credential() {
key="$1"
key_type="$2"
no_print="${3:-false}"
existing_val="$(_get_cached_op_credential "$key")"
if test -n "$existing_val"
then
echo "$existing_val"
return 0
fi
prompt="Saving your 1Password $key_type locally to your Mac. Enter it here"
val=""
while true
do
if grep -Eiq '^true$' <<< "$no_print"
then
prompt="$prompt (You won't see any output)"
>&2 printf "\n%s: " "$prompt"
read -rs val
else
>&2 printf "%s: " "$prompt"
read -r val
fi
test -n "$val" && break
log_error "Value can't be empty."
echo
done
_set_cached_op_credential "$1" "$val" &&
_get_cached_op_credential "$1"
}
get_or_ask_for_cached_op_credential() {
_get_or_ask_for_cached_op_credential "$1" "$2"
}
get_or_ask_for_sensitive_cached_op_credential() {
_get_or_ask_for_cached_op_credential "$1" "$2" true
}
_get_1pass_token() {
_get_cached_op_credential '1password_cli_token'
}
_op_cli_unauthenticated() {
"$(brew --prefix)/bin/op" "$@"
}
_1pass_token_valid() {
existing_token="$(_get_1pass_token)"
test -z "$existing_token" && return 1
_op_cli_unauthenticated vault list --session "$existing_token" &>/dev/null
}
_op_cli_authenticated() {
_1pass_token_valid || _sign_into_1p_via_cli
"$(brew --prefix)/bin/op" --account my --session "$(_get_1pass_token)" "$@"
}
_get_1pass_device_key() {
_get_cached_op_credential '1password_device_key'
}
_get_1pass_otp() {
_clear_cached_op_credential '1password_otp'
get_or_ask_for_cached_op_credential "1password_otp" "OTP" || return 1
}
_get_1pass_password() {
get_or_ask_for_sensitive_cached_op_credential "1password_password" "password"
}
_get_1pass_secret_key() {
get_or_ask_for_sensitive_cached_op_credential "1password_sk" "secret key"
}
_get_1pass_email() {
get_or_ask_for_cached_op_credential "1password_email" "email address"
}
_clear_1pass_email() {
_clear_cached_op_credential '1password_email'
}
_clear_1pass_sk() {
_clear_cached_op_credential '1password_sk'
}
_clear_1pass_pw() {
_clear_cached_op_credential '1password_password'
}
_get_1pass_default_vault() {
get_or_ask_for_cached_op_credential "1password_default_vault" "default vault"
}
_sign_into_1p_via_cli() {
_op_account_exists() {
_op_cli_unauthenticated account list | grep -q "$1"
}
_add_account_to_op() {
email=$(_get_1pass_email) || return 1
_op_account_exists && return 0
secret_key=$(_get_1pass_secret_key) || return 1
export OP_SECRET_KEY="$secret_key"
password=$(_get_1pass_password) || return 1
# This is a bug with the terminal package in Golang.
# https://github.com/golang/go/issues/19909
otp="$(_get_1pass_otp)" || return 1
{ sleep 2; \
echo "$password"; \
echo "$otp"; } | _op_cli_unauthenticated account add --address my --email "$email" && return 0
rc="$?"
_clear_1pass_email
_clear_1pass_sk
_clear_1pass_pw
return "$rc"
}
_get_signin_token() {
secret_key=$(_get_1pass_secret_key) || return 1
export OP_SECRET_KEY="$secret_key"
password=$(_get_1pass_password) || return 1
# This is a bug with the terminal package in Golang.
# https://github.com/golang/go/issues/19909
if test -n "$REENTER_OP_OTP"
then
otp="$(_get_1pass_otp)" || return 1
token_data="$({ sleep 2; \
echo "$password"; \
echo "$otp"; } | _op_cli_unauthenticated signin -f)" || return 1
else
token_data="$({ sleep 2; \
echo "$password"; } | _op_cli_unauthenticated signin -f)" || return 1
fi
grep -E '^export' <<< "$token_data" | cut -f2 -d '=' | tr -d '"'
}
_1pass_token_valid && return 0
_add_account_to_op || return 1
token=$(_get_signin_token "$1") || return 1
_set_cached_op_credential '1password_cli_token' "$token"
}
_1pass_app_integration_enabled() {
settings_file="$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/Library/Application Support/1Password/Data/settings/settings.json"
test -e "$settings_file" &&
test "$(jq -r '."developers.cliSharedLockState.enabled"' "$settings_file")" == 'true'
}
_ensure_onepassword_configured() {
ensure_jq() {
if ! &>/dev/null which jq
then
log_error "jq isn't installed. Run 'source ~/.bash_install' to \
fix this."
return 1
fi
}
ensure_not_disabled() {
if grep -Eiq '^true$' <<< "$OP_DISABLED"
then
log_error "1Password helpers are disabled. Use 'op' directly."
return 1
fi
}
ensure_not_disabled && ensure_jq
}
_op_verify_that_we_are_on_a_mac() {
if ! test "$(get_os_type)" == "Darwin"
then
log_warning "1Password CLI is not supported on $(get_os_type) yet."
return 0
fi
}
_generate_1pass_device_key() {
existing_device_key="$(_get_cached_op_credential '1password_device_key')"
test -n "$existing_device_key" && return 0
_set_cached_op_credential '1password_device_key' \
"$(head -c 16 /dev/urandom | base32 | tr -d = | tr '[:upper:]' '[:lower:]')"
}
list_password_titles() {
vault="$1"
list_titles() {
vault="$1"
if test -z "$vault"
then
_op_cli_authenticated item list --format=json | jq -r .[].title
else
_op_cli_authenticated item list --format=json --vault "$vault" | jq -r .[].title
fi
}
_ensure_onepassword_configured && list_titles "$vault"
}
list_password_titles_matching_regex() {
list_password_titles | grep -E "$1"
}
list_password_titles_in_vault() {
list_password_titles "$1"
}
list_password_titles_in_vault_matching_regex() {
list_password_titles "$1" | grep -E "$2"
}
list_vaults() {
list() {
_op_cli_authenticated vault list --format=json | jq -r .[].name
}
_ensure_onepassword_configured && list
}
get_password_field() {
get() {
title="$1"
field="$2"
vault="$3"
if test -z "$vault"
then
_op_cli_authenticated item get "$title" --fields="$field"
else
_op_cli_authenticated item get --vault "$vault" "$title" --fields="$field"
fi
}
_ensure_onepassword_configured || return 1
result=$(get "$1" "$2" "$3")
test -z "$result" && result="no password found"
echo "$result"
}
get_password() {
get_password_field "$1" 'password' "$2"
}
get_password_by_name_regexp() {
pattern="$1"
vault="$2"
list_password_titles "$vault" |
grep -E "$pattern" |
while read -r password
do get_password "$password" "$vault"
done
}
get_password_by_name() {
pattern="$1"
vault="$2"
get_password "$pattern" "$vault"
}
_get_1pass_functions() {
grep -E '^[a-z].*() {' $HOME/src/setup/.bash_onepassword_specific |
grep -Ev '^ensure' |
grep -Ev '_get_1pass_functions' |
sed 's/() {//'
}
# op_cli: Convenience function for other scripts wanting to interact with
# 1Password (since newer versions of op are more anal about session keys
# and stuff).
op_cli() {
_op_cli_authenticated "$@"
}
_configure_1password_cli() {
_onepass_has_not_been_run_before() {
! test -d "$OP_CONFIG_DIR"
}
if _1pass_app_integration_enabled
then
log_info "INFO: 1Password CLI app integration is enabled; there's nothing to configure!"
return 0
fi
_ensure_onepassword_configured || return 1
_op_verify_that_we_are_on_a_mac
if _onepass_has_not_been_run_before
then
_create_op_config_dir && _create_op_credential_cache && _generate_1pass_device_key
fi
export OP_DEVICE=$(_get_1pass_device_key)
_sign_into_1p_via_cli && return 0
log_error "Signing into 1Password unsuccessful. If you saw a prompt to enter an OTP, \
run this command again, but with REENTER_OP_OTP=1 behind it."
return 1
}
configure_1password_cli() {
if test "$FROM_BASH_INSTALL" != 'true' && test -t 1
then
log_error 'Run "eval $(configure_1password_cli)" instead.'
return 1
fi
_configure_1password_cli
}
alias op-cli=_op_cli_authenticated
alias get_1pass_functions=_get_1pass_functions
alias onepass_functions=_get_1pass_functions
if ! _1pass_app_integration_enabled
then
log_info "Run 'configure_1password_cli' to start using 1Password in your terminal."
log_info "If your account has two-factor authentication enabled, \
provide ONEPASSWORD_OTP=[YOUR_OTP] before running 'eval \$(configure_1password_cli)'."
fi