sym 2.8.0 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +30 -31
  3. data/.envrc +7 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +150 -928
  6. data/.travis.yml +16 -26
  7. data/CHANGELOG.md +206 -167
  8. data/Gemfile +1 -0
  9. data/README.adoc +650 -0
  10. data/Rakefile +9 -3
  11. data/bin/{sym.completion → sym.completion.bash} +9 -14
  12. data/bin/sym.symit.bash +781 -0
  13. data/codecov.yml +29 -0
  14. data/design/sym-help.png +0 -0
  15. data/exe/keychain +1 -1
  16. data/exe/sym +5 -2
  17. data/lib/ruby_warnings.rb +7 -0
  18. data/lib/sym.rb +1 -7
  19. data/lib/sym/app.rb +1 -1
  20. data/lib/sym/app/args.rb +3 -2
  21. data/lib/sym/app/cli.rb +1 -2
  22. data/lib/sym/app/cli_slop.rb +1 -1
  23. data/lib/sym/app/commands.rb +1 -1
  24. data/lib/sym/app/commands/base_command.rb +1 -1
  25. data/lib/sym/app/commands/bash_completion.rb +20 -8
  26. data/lib/sym/app/commands/open_editor.rb +1 -1
  27. data/lib/sym/app/commands/password_protect_key.rb +4 -4
  28. data/lib/sym/app/commands/show_examples.rb +1 -1
  29. data/lib/sym/app/input/handler.rb +7 -1
  30. data/lib/sym/app/keychain.rb +15 -9
  31. data/lib/sym/app/output/noop.rb +2 -1
  32. data/lib/sym/app/password/cache.rb +1 -1
  33. data/lib/sym/app/password/providers.rb +2 -3
  34. data/lib/sym/app/private_key/decryptor.rb +2 -2
  35. data/lib/sym/app/private_key/detector.rb +4 -7
  36. data/lib/sym/application.rb +6 -11
  37. data/lib/sym/constants.rb +28 -13
  38. data/lib/sym/data/wrapper_struct.rb +20 -12
  39. data/lib/sym/errors.rb +11 -2
  40. data/lib/sym/extensions/instance_methods.rb +7 -8
  41. data/lib/sym/extensions/stdlib.rb +0 -1
  42. data/lib/sym/extensions/with_retry.rb +1 -1
  43. data/lib/sym/extensions/with_timeout.rb +1 -1
  44. data/lib/sym/version.rb +30 -5
  45. data/sym.gemspec +35 -35
  46. metadata +88 -71
  47. data/.codeclimate.yml +0 -30
  48. data/README.md +0 -623
  49. data/bin/sym.symit +0 -565
  50. data/lib/sym/app/password/providers/drb_provider.rb +0 -41
data/Rakefile CHANGED
@@ -1,7 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
3
4
  require 'yard'
4
-
5
+ require 'timeout'
5
6
 
6
7
  def shell(*args)
7
8
  puts "running: #{args.join(' ')}"
@@ -25,12 +26,17 @@ task :build => :permissions
25
26
 
26
27
  YARD::Rake::YardocTask.new(:doc) do |t|
27
28
  t.files = %w(lib/**/*.rb exe/*.rb - README.md LICENSE)
28
- t.options.unshift('--title','"Sym – Symmetric Key Encryption for Your Data"')
29
- t.after = ->() { exec('open doc/index.html') }
29
+ t.options.unshift('--title', '"Sym – Symmetric Key Encryption for Your Data"')
30
+ t.after = -> { exec('open doc/index.html') }
30
31
  end
31
32
 
32
33
  RSpec::Core::RakeTask.new(:spec)
33
34
 
35
+ RuboCop::RakeTask.new
36
+
34
37
  task :default => :spec
35
38
 
36
39
 
40
+
41
+
42
+
@@ -11,24 +11,19 @@
11
11
  ( [[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || \
12
12
  [[ -n $BASH_VERSION && $0 != "$BASH_SOURCE" ]]) && _s_=1 || _s_=0
13
13
 
14
- bash_version=$(/usr/bin/env bash --version | awk '{FS="version"}{print $4}')
14
+ bash_version=$(bash --version | awk '{FS="version"}{print $4}')
15
15
  bash_version=${bash_version:0:1}
16
16
 
17
- if [[ "${bash_version}" -lt 4 ]]; then
18
- echo "Sym BASH helpers are incompatible with BASH version 3 or older."
19
- echo "Please upgrade your BASH to version 4+ released in 2009. Srsly."
20
- (( $_s_ )) && return 1
21
- (( $_s_ )) || exit 1
22
- fi
23
-
24
- declare -a bash_completion_locations=(/usr/local/etc/bash_completion /usr/etc/bash_completion /etc/bash_completion)
25
- loaded=false
26
- for file in ${bash_completion_locations[@]}; do
27
- [[ -s $file ]] && {
28
- source $file
17
+ [[ -z $(type _filedir 2>/dev/null) ]] && {
18
+ declare -a bash_completion_locations=(/usr/local/etc/bash_completion /usr/etc/bash_completion /etc/bash_completion)
19
+ loaded=false
20
+ for file in ${bash_completion_locations[@]}; do
21
+ [[ -s ${file} ]] && {
22
+ source ${file}
29
23
  break
30
24
  }
31
- done
25
+ done
26
+ }
32
27
 
33
28
  _sym_long_opts() {
34
29
  sym -h | grep -- '--' | egrep '^ -' | awk '{print $2}' | sort
@@ -0,0 +1,781 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # (c) 2017-2018 Konstantin Gredeskoul
4
+ #
5
+ # MIT License, distributed as part of `sym` ruby gem.
6
+ # • https://github.com/kigster/sym
7
+ #
8
+ #==============================================================================
9
+ #
10
+ # The purpose of this script is to transparently edit application secrets in
11
+ # Rails apps or other projects. It simplifies the process of key import, as well
12
+ # as the direct editing, as well as multi-file encryption/decryption routines.
13
+ #
14
+ # The idea is that you set some of the variables below to values specific to your
15
+ # system and working with encrypted files will become very easy.
16
+ #
17
+ # SYMIT__FOLDER is a relative folder to your project root, under which you
18
+ # might keep ALL of your encrypted files. Alternatively, if you keep encrypted
19
+ # files sprinkled around your project, just leave it out, because it defaults
20
+ # to "." — the current folder, and search anything beneath.
21
+ #
22
+ # Variables:
23
+ #
24
+ # # only search ./config folder
25
+ # export SYMIT__FOLDER="config"
26
+ #
27
+ # # this will be the name of your key in OS-X KeyChain
28
+ # export SYMIT__KEY="my-org.engineering.dev" # just a name
29
+ #
30
+ # # This is the extension given to the encrypted files. Ideally, leave it
31
+ # # be as ".enc"
32
+ # export SYMIT__EXTENSION=".enc"
33
+ #
34
+ # And then
35
+ #
36
+ # symit import key [ insecure ] # import a key and password-protect it (or not)
37
+ # symit auto application.yml.enc # auto-decrypts it
38
+ # symit auto application.yml # auto-encrypts it
39
+ # symit decrypt application.yml # finds application.yml.enc and decrypts that.
40
+ #
41
+ #
42
+ # ...and vola! You are editing the encrypted file with sym from the root of
43
+ # your Rails application. Neat, no?
44
+ #
45
+
46
+
47
+ # Check if we are being sourced in, or run as a script:
48
+ ( [[ -n ${ZSH_EVAL_CONTEXT} && ${ZSH_EVAL_CONTEXT} =~ :file$ ]] || \
49
+ [[ -n $BASH_VERSION && $0 != "$BASH_SOURCE" ]]) && _s_=1 || _s_=0
50
+
51
+ (( $_s_ )) && _is_sourced=1
52
+ (( $_s_ )) || _is_sourced=0
53
+
54
+ # Set all the defaults
55
+ function __symit::init() {
56
+ export SYMIT__EXTENSION=${SYMIT__EXTENSION:-'.enc'}
57
+ export SYMIT__FOLDER=${SYMIT__FOLDER:-'.'}
58
+ export SYMIT__KEY=${SYMIT__KEY}
59
+ export SYMIT__MIN_VERSION='latest'
60
+ }
61
+
62
+ # Returns name of the current shell, eg 'bash'
63
+ function __lib::shell::name() {
64
+ echo $(basename $(printf $SHELL))
65
+ }
66
+
67
+ # Returns 'yes' if current shell is BASH
68
+ function __lib::shell::is_bash() {
69
+ [[ $(__lib::shell::name) == "bash" ]] && echo yes
70
+ }
71
+
72
+ # Returns a number representing shell version, eg.
73
+ # 3 or 4 for BASH v3 and v4 respectively.
74
+ function __lib::bash::version_number() {
75
+ echo $BASH_VERSION | awk 'BEGIN{FS="."}{print $1}'
76
+ }
77
+
78
+ # Enable all colors, but only if the STDOUT is a terminal
79
+ function __lib::color::setup() {
80
+ if [[ -t 1 ]]; then
81
+ export txtblk='\e[0;30m' # Black - Regular
82
+ export txtred='\e[0;31m' # Red
83
+ export txtgrn='\e[0;32m' # Green
84
+ export txtylw='\e[0;33m' # Yellow
85
+ export txtblu='\e[0;34m' # Blue
86
+ export txtpur='\e[0;35m' # Purple
87
+ export txtcyn='\e[0;36m' # Cyan
88
+ export txtwht='\e[0;37m' # White
89
+
90
+ export bldblk='\e[1;30m' # Black - Bold
91
+ export bldred='\e[1;31m' # Red
92
+ export bldgrn='\e[1;32m' # Green
93
+ export bldylw='\e[1;33m' # Yellow
94
+ export bldblu='\e[1;34m' # Blue
95
+ export bldpur='\e[1;35m' # Purple
96
+ export bldcyn='\e[1;36m' # Cyan
97
+ export bldwht='\e[1;37m' # White
98
+
99
+ export unkblk='\e[4;30m' # Black - Underline
100
+ export undred='\e[4;31m' # Red
101
+ export undgrn='\e[4;32m' # Green
102
+ export undylw='\e[4;33m' # Yellow
103
+ export undblu='\e[4;34m' # Blue
104
+ export undpur='\e[4;35m' # Purple
105
+ export undcyn='\e[4;36m' # Cyan
106
+ export undwht='\e[4;37m' # White
107
+
108
+ export bakblk='\e[40m' # Black - Background
109
+ export bakred='\e[41m' # Red
110
+ export bakgrn='\e[42m' # Green
111
+ export bakylw='\e[43m' # Yellow
112
+ export bakblu='\e[44m' # Blue
113
+ export bakpur='\e[45m' # Purple
114
+ export bakcyn='\e[46m' # Cyan
115
+ export bakwht='\e[47m' # White
116
+
117
+ export clr='\e[0m' # Text Reset
118
+ export txtrst='\e[0m' # Text Reset
119
+ export rst='\e[0m' # Text Reset
120
+ fi
121
+ }
122
+
123
+ # Unset all the colors, in case we a being piped into
124
+ # something else.
125
+ function __lib::color::reset() {
126
+ export txtblk=
127
+ export txtred=
128
+ export txtgrn=
129
+ export txtylw=
130
+ export txtblu=
131
+ export txtpur=
132
+ export txtcyn=
133
+ export txtwht=
134
+
135
+ export bldblk=
136
+ export bldred=
137
+ export bldgrn=
138
+ export bldylw=
139
+ export bldblu=
140
+ export bldpur=
141
+ export bldcyn=
142
+ export bldwht=
143
+
144
+ export unkblk=
145
+ export undred=
146
+ export undgrn=
147
+ export undylw=
148
+ export undblu=
149
+ export undpur=
150
+ export undcyn=
151
+ export undwht=
152
+
153
+ export bakblk=
154
+ export bakred=
155
+ export bakgrn=
156
+ export bakylw=
157
+ export bakblu=
158
+ export bakpur=
159
+ export bakcyn=
160
+ export bakwht=
161
+
162
+ export clr=
163
+ export txtrst=
164
+ export rst=
165
+ }
166
+
167
+ # Enable or disable the colors based on whether the STDOUT
168
+ # is a proper terminal, or a pipe.
169
+ function __lib::stdout::configure() {
170
+ if [[ -t 1 ]]; then
171
+ __lib::color::setup
172
+ else
173
+ __lib::color::reset
174
+ fi
175
+ }
176
+
177
+ __lib::stdout::configure
178
+
179
+ # Check if we are being run as a script, and if so — bail.
180
+ (( $_s_ )) || {
181
+ printf "${bldred}This script is meant to be sourced into your environment,\n"
182
+ printf "not run on a command line.${clr} \n\n"
183
+
184
+ printf "Please add 'source $0' to your BASH initialization file,\n"
185
+ printf "or run the following command:\n\n"
186
+
187
+ printf " \$ ${bldgrn}sym -B ~/.bash_profile${clr}\n\n"
188
+
189
+ printf "${bldblu}Thanks for using Sym!${clr}\n"
190
+ exit 1
191
+ }
192
+
193
+ # Horizontal line, width of the full terminal
194
+ function __lib::color::hr() {
195
+ local cols=${1:-${COLUMNS}}
196
+ local char=${2:-"—"}
197
+ local color=${3:-${txtylw}}
198
+
199
+ printf "${color}"
200
+ eval "printf \"%0.s${char}\" {1..${cols}}"
201
+ printf "${clr}\n"
202
+ }
203
+
204
+ # Large header, all caps
205
+ function __lib::color::h1() {
206
+ local title=$(echo "$*" | tr 'a-z' 'A-Z')
207
+ len=${#title}
208
+ printf "${bldylw}${title}\n"
209
+ __lib::color::hr ${len} '─'
210
+ }
211
+
212
+ # Smaller header
213
+ function __lib::color::h2() {
214
+ printf "${bldpur}$*${clr}\n"
215
+ }
216
+
217
+ # Shift cursor by N positions to the right
218
+ function __lib::color::cursor-right-by() {
219
+ position=$1
220
+ printf "\e[${position}C"
221
+ }
222
+
223
+ # Shift cursor by N positions to the left
224
+ function __lib::color::cursor-left-by() {
225
+ position=$1
226
+ printf "\e[${position}D"
227
+ }
228
+
229
+ # Shift cursor by N positions up
230
+ function __lib::color::cursor-up-by() {
231
+ position=$1
232
+ printf "\e[${position}A"
233
+ }
234
+
235
+ # Shift cursor by N positions down
236
+ function __lib::color::cursor-down-by() {
237
+ position=$1
238
+ printf "\e[${position}B"
239
+ }
240
+
241
+ # Convert a version string such as "1.50.17" to an integer
242
+ # 101050017 for numeric comparison:
243
+ function __lib::ver-to-i() {
244
+ version=${1}
245
+ echo ${version} | awk 'BEGIN{FS="."}{ printf "1%02d%03.3d%03.3d", $1, $2, $3}'
246
+ }
247
+
248
+ # Convert a result of __lib::ver-to-i() back to a regular version.
249
+ function __lib::i-to-ver() {
250
+ version=${1}
251
+ /usr/bin/env ruby -e "ver='${version}'; printf %Q{%d.%d.%d}, ver[1..2].to_i, ver[3..5].to_i, ver[6..8].to_i"
252
+ }
253
+
254
+ # Prints Usage
255
+ function __symit::usage() {
256
+ echo
257
+ __lib::color::h1 "symit"
258
+
259
+ printf "
260
+ ${bldylw}symit${bldgrn} is a powerful BASH helper, that enhances the CLI encryption
261
+ tool called ${bldred}Sym${clr}, which is a Ruby Gem.
262
+
263
+ Sym has an extensive CLI interface, but it only handles one
264
+ encryption/decryption operation per invocation. With this script, you can
265
+ auto decrypt all files in a given folder, you can import the key in a
266
+ simpler way, and you can save into the environment sym configuration that
267
+ will be used. It also streamlines editing of encrypted files in a given
268
+ folder. Symit can be configured either with the ENV variables, or using
269
+ the CLI flags.\n"
270
+
271
+ printf "
272
+ The recommended way to use ${bldred}symit${clr} is to set the following
273
+ environment variables, which removes the need to pass these values via the
274
+ flags. These variables default to the shown values if not set elsewhere:
275
+
276
+ Perhaps the most critically important variable to set is ${txtylw}SYMIT__KEY${clr}:
277
+ ${txtylw}
278
+ export SYMIT__KEY='my-org.my-app.dev'
279
+ eg: export SYMIT__KEY='github.web.development'
280
+ ${clr}
281
+ The ${txtcya}key${clr} can resolve to a file name, or a name of ENV variable,
282
+ a keychain entry, or be the actual key (not recommended!). See the following
283
+ link for more info:
284
+
285
+ ${undblu}https://github.com/kigster/sym#resolving-the--k-argument${clr}
286
+
287
+ Additional configuration is available through these variables:
288
+ ${txtylw}
289
+ export SYMIT__EXTENSION='${SYMIT__EXTENSION}'
290
+ export SYMIT__FOLDER='${SYMIT__FOLDER}'
291
+ export SYMIT__MIN_VERSION='latest'
292
+ ${clr}
293
+ The last variable defines the minimum Sym version desired. Set it to
294
+ 'latest' to have symit auto-upgrade Sym every time it is invoked.
295
+
296
+ ${clr}\n"
297
+
298
+ __lib::color::h2 "Usage:"
299
+ printf " ${bldgrn}symit [ action ] [ file-path/pattern ] [ flags ]${clr}\n\n"
300
+
301
+ __lib::color::h2 "Actions:"
302
+ printf " Action is the first word that defaults to ${bldylw}edit${clr}.\n\n"
303
+ printf " ${bldcya}Valid actions are below, starting with the Key import or creation:${clr}\n\n"
304
+ printf " ${bldylw}— generate ${clr}create a new secure key, and copies it to the\n"
305
+ printf " clipboard (if supported), otherwise prints to STDOUT\n"
306
+ printf " Key name (set via SYMIT__KEY or -k flag) is required,\n"
307
+ printf " and is used as the KeyChain entry name for the new key.\n\n"
308
+ printf " ${bldylw}— import [insecure]\n"
309
+ printf " ${clr}imports the key from clipboard and adds password\n"
310
+ printf " encryption unless 'insecure' is passed in. Same as above\n"
311
+ printf " in relation with the key parameter.\n\n"
312
+ printf " ${bldcya}The following actions require the file pattern/path argument:${clr}\n"
313
+ printf " ${bldylw}— edit ${clr}Finds all files, and opens them in $EDITOR\n"
314
+ printf " ${bldylw}— encrypt ${clr}Encrypts files matching file-path\n"
315
+ printf " ${bldylw}— decrypt ${clr}Adds the extension to file pattern and decrypts\n"
316
+ printf " ${bldylw}— auto ${clr}encrypts decrypted file, and vice versa\n"
317
+
318
+ echo
319
+ __lib::color::h2 "Flags:"
320
+ printf " -f | --folder DIR ${clr}Top level folder to search.${clr}\n"
321
+ printf " -k | --key KEY ${clr}Key identifier${clr}\n"
322
+ printf " -x | --extension EXT ${clr}Default extension of encrypted files.${clr}\n"
323
+ printf " -n | --dry-run ${clr}Print stuff, but dont do it${clr}\n"
324
+ printf " -a | --all-files ${clr}If provided ALL FILES are operated on${clr}\n"
325
+ printf " ${clr}Use with CAUTION!${clr}\n"
326
+ printf " -v | --verbose ${clr}Print more stuff${clr}\n"
327
+ printf " -q | --quiet ${clr}Print less stuff${clr}\n"
328
+ printf " -h | --help ${clr}Show this help message${clr}\n"
329
+
330
+ echo
331
+ __lib::color::h2 'Encryption key identifier can be:'
332
+ printf "${clr}"
333
+
334
+ printf '
335
+ 1. name of the keychain item storing the keychain (secure)
336
+ 2. name of the environment variable storing the Key (*)
337
+ 3. name of the file storing the key (*)
338
+ 4. the key itself (*)'
339
+
340
+ echo
341
+ printf "${bldred}"
342
+ printf '
343
+ (*) 2-4 are insecure UNLESS the key is encrypted with a password.'; echo
344
+ printf "${clr}\
345
+ Please refer to README about generating password protected keys:\n
346
+ ${bldblu}${undblu}https://github.com/kigster/sym#generating-the-key--examples${clr}\n\n"
347
+ echo
348
+
349
+ __lib::color::h1 'Examples:'
350
+
351
+ printf " To import a key securely, first copy the key to your clipboard,\n"
352
+ printf " and then run the following command, pasting the key when asked:\n\n"
353
+ printf " ❯ ${bldgrn}symit${bldblu} import key ${clr}\n\n"
354
+
355
+ printf " To encrypt or decrypt ALL files in the 'config' directory:${clr}\n\n"
356
+ printf " ❯ ${bldgrn}symit${bldblu} encrypt|decrypt -a -f config ${clr}\n\n"
357
+
358
+ printf " To decrypt all *.yml.enc files in the 'config' directory:${clr}\n\n"
359
+ printf " ❯ ${bldgrn}symit${bldblu} decrypt '*.yml' -f config ${clr}\n\n"
360
+
361
+ printf " To edit an encrypted file ${txtblu}config/application.yml.enc${clr}\n\n"
362
+ printf " ❯ ${bldgrn}symit${bldblu} application.yml${clr}\n\n"
363
+
364
+ printf " To auto decrypt a file ${txtblu}config/settings/crypt/pass.yml.enc${clr}\n\n"
365
+ printf " ❯ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n"
366
+
367
+ printf " To automatically decide to either encrypt or decrypt a file,\n"
368
+ printf " based on the file extension use 'auto' command. The first line below\n"
369
+ printf " encrypts the file, second decrypts it, because the file extension is .enc:${clr}\n\n"
370
+
371
+ printf " ❯ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml${clr}\n"
372
+ printf " ❯ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n"
373
+
374
+ printf " To encrypt a file ${txtblu}config/settings.yml${clr}\n"
375
+ printf " ❯ ${bldgrn}symit${bldblu} encrypt config/settings.yml${clr}\n\n"
376
+ }
377
+
378
+ function __datum() {
379
+ date +"%m/%d/%Y.%H:%M:%S"
380
+ }
381
+
382
+ function __warn() {
383
+ __lib::color::cursor-left-by 1000
384
+ printf "${bldylw}$* ${bldylw}\n"
385
+ }
386
+ function __err() {
387
+ __lib::color::cursor-left-by 1000
388
+ printf "${bldred}ERROR: ${txtred}$* ${bldylw}\n"
389
+ }
390
+
391
+ function __inf() {
392
+ [[ ${cli__opts__quiet} ]] && return
393
+ __lib::color::cursor-left-by 1000
394
+ printf "${txtblu}$*${clr}\n"
395
+ }
396
+
397
+ function __dbg() {
398
+ [[ ${cli__opts__verbose} ]] || return
399
+ __lib::color::cursor-left-by 1000
400
+ printf "${txtgrn}$*${clr}\n"
401
+ }
402
+
403
+ function __lib::command::print() {
404
+ __inf "${bldylw}❯ ${bldcya}$*${clr}"
405
+ }
406
+
407
+ function __symit::sym::installed_version() {
408
+ __lib::ver-to-i $(gem list | grep sym | awk '{print $2}' | sed 's/(//g;s/)//g')
409
+ }
410
+
411
+ function __symit::sym::latest_version() {
412
+ __lib::ver-to-i $(gem query --remote -n '^sym$' | awk '{print $2}' | sed 's/(//g;s/)//g')
413
+ }
414
+
415
+ function __symit::install::update() {
416
+ local desired_version=$1
417
+ shift
418
+ local current_version=$2
419
+ shift
420
+ local version_args=$*
421
+
422
+ __inf "updating sym to version ${bldylw}$(__lib::i-to-ver ${desired_version})${clr}..."
423
+ printf "${bldblu}" >&1
424
+ echo y | gem uninstall sym --force -x 2>/dev/null
425
+ printf "${clr}" >&1
426
+
427
+ command="gem install sym ${version_args} "
428
+ eval "${command}" >/dev/null
429
+ code=$?
430
+ printf "${clr}" >&2
431
+ if [[ ${code} != 0 ]]; then
432
+ __err "gem install returned ${code}, with command ${bldylw}${command}"
433
+ return 127
434
+ fi
435
+ current_version=$(__symit::sym::installed_version)
436
+ __inf "sym version ${bldylw}$(__lib::i-to-ver ${current_version}) was successfully installed."
437
+ }
438
+
439
+ function __symit::install::gem() {
440
+ if [[ -n ${__symit_last_checked_at} ]]; then
441
+ now=$(date +'%s')
442
+ if [[ $(( $now - ${__symit_last_checked_at} )) -lt 3600 ]]; then
443
+ return
444
+ fi
445
+ fi
446
+
447
+ export __symit_last_checked_at=${now:-$(date +'%s')}
448
+
449
+ __inf "Verifying current sym version, please wait..."
450
+ current_version=$(__symit::sym::installed_version)
451
+ if [[ -n ${SYMIT__MIN_VERSION} ]]; then
452
+ if [[ ${SYMIT__MIN_VERSION} -eq 'latest' ]]; then
453
+ desired_version=$(__symit::sym::latest_version)
454
+ version_args=''
455
+ else
456
+ desired_version=$( __lib::ver-to-i ${SYMIT__MIN_VERSION})
457
+ version_args=" --version ${SYMIT__MIN_VERSION}"
458
+ fi
459
+
460
+ if [[ "${desired_version}" != "${current_version}" ]]; then
461
+ __symit::install::update "${desired_version}" "${current_version}" "${version_args}"
462
+ else
463
+ __inf "${bldgrn}sym${clr} ${txtblu}is on the correct version ${bldylw}$(__lib::i-to-ver ${desired_version})${txtblu} already"
464
+ fi
465
+ else
466
+ if [[ -z ${current_version} ]] ; then
467
+ __dbg "installing latest version of ${bldylw}sym..."
468
+ fi
469
+ fi
470
+ }
471
+
472
+ function __symit::files() {
473
+ eval $(__symit::files::cmd)
474
+ }
475
+
476
+ function __symit::files::cmd() {
477
+ if [[ -n ${cli__opts__file} && -n ${cli__opts__extension} ]]; then
478
+
479
+ local folder=${cli__opts__folder}
480
+ local file="${cli__opts__file}"
481
+ local ext="${cli__opts__extension}"
482
+
483
+ if [[ ${file} =~ '/' ]]; then
484
+ if [[ ${folder} == '.' ]]; then
485
+ folder="$(dirname ${file})"
486
+ else
487
+ folder="${folder}/$(dirname ${file})"
488
+ fi
489
+ file="$(basename ${file})"
490
+ fi
491
+
492
+ if [[ "${cli__opts__action}" == "encrypt" ]] ; then
493
+ printf "find ${folder} -name '${file}' -and -not -name '*${ext}'"
494
+ elif [[ "${cli__opts__action}" == "auto" ]] ; then
495
+ printf "find ${folder} -name '${file}'"
496
+ else # edit, decrypt
497
+ [[ ${file} =~ "${ext}" ]] || file="${file}${ext}"
498
+ printf "find ${folder} -name '${file}'"
499
+ fi
500
+ fi
501
+ }
502
+
503
+ function __symit::command() {
504
+ file=${1}
505
+ if [[ -n "${cli__opts__key}" && -n "${cli__opts__extension}" ]]; then
506
+ action="${cli__opts__action}"
507
+ v="sym__actions__${action}"
508
+ flags="${!v}"
509
+ if [[ ${action} =~ "key" ]]; then
510
+ [[ -n ${cli__opts__verbose} ]] && printf "processing key import action ${bldylw}${action}${clr}\n" >&2
511
+ printf "sym ${flags} ${cli__opts__key} "
512
+ elif [[ ${action} =~ "generate" ]] ; then
513
+ [[ -n ${cli__opts__verbose} ]] && printf "processing generate key action ${bldylw}${action}${clr}\n" >&2
514
+ if [[ -n $(which pbcopy) ]]; then
515
+ out_key=/tmp/outkey
516
+ command="sym ${flags} ${cli__opts__key} -q -o ${out_key}; cat ${out_key} | pbcopy; rm -f ${out_key}"
517
+ printf "${command}"
518
+ else
519
+ printf "sym ${flags} ${cli__opts__key} "
520
+ fi
521
+ elif [[ -n ${file} ]] ; then
522
+ ext="${cli__opts__extension}"
523
+ [[ -z ${ext} ]] && ext='.enc'
524
+ ext=$(echo ${ext} | sed -E 's/[\*\/,.]//g')
525
+ if [[ ${action} =~ "encrypt" ]]; then
526
+ printf "sym ${flags} ${file} -ck ${cli__opts__key} -o ${file}.${ext}"
527
+ elif [[ ${action} =~ "decrypt" ]]; then
528
+ new_name=$(echo ${file} | sed "s/\.${ext}//g")
529
+ [[ "${new_name}" == "${file}" ]] && name="${file}.decrypted"
530
+ printf "sym ${flags} ${file} -ck ${cli__opts__key} -o ${new_name}"
531
+ else
532
+ printf "sym ${flags} ${file} -ck ${cli__opts__key} "
533
+ fi
534
+ else
535
+ printf "printf \"ERROR: not sure how to generate a correct command\\n\""
536
+ fi
537
+ fi
538
+ }
539
+
540
+ function __symit::cleanup() {
541
+ unset sym__actions
542
+ unset cli__opts
543
+ }
544
+
545
+ function __symit::exit() {
546
+ code=${1:-0}
547
+ __symit::cleanup
548
+ echo -n ${code}
549
+ }
550
+
551
+ function __symit::print_cli_args() {
552
+ __dbg "action ${bldylw}: ${cli__opts__action}${clr}"
553
+ __dbg "key ${bldylw}: ${cli__opts__key}${clr}"
554
+ __dbg "file ${bldylw}: ${cli__opts__file}${clr}"
555
+ __dbg "extension ${bldylw}: ${cli__opts__extension}${clr}"
556
+ __dbg "folder ${bldylw}: ${cli__opts__folder}${clr}"
557
+ __dbg "verbose ${bldylw}: ${cli__opts__verbose}${clr}"
558
+ __dbg "dry_run ${bldylw}: ${cli__opts__dry_run}${clr}"
559
+ }
560
+
561
+ function __symit::args::needs_file() {
562
+ if [[ "${cli__opts__action}" == 'edit' || \
563
+ "${cli__opts__action}" == 'auto' || \
564
+ "${cli__opts__action}" == 'encrypt' || \
565
+ "${cli__opts__action}" == 'decrypt' ]]; then
566
+ printf 'yes'
567
+ fi
568
+ }
569
+
570
+ function __symit::validate_args() {
571
+ if [[ -n $(__symit::args::needs_file) && -z ${cli__opts__file} ]]; then
572
+ __err "missing file argument, config/application.yml"
573
+ return $(__symit::exit 2)
574
+ fi
575
+
576
+ if [[ -z "${cli__opts__key}" ]]; then
577
+ __err "Key was not defined, pass it with ${bldblu}-k KEY_ID${bldred}"
578
+ __err "or set it via ${bldgrn}\$SYMIT__KEY${bldred} variable."
579
+ return $(__symit::exit 4)
580
+ fi
581
+
582
+ if [[ -z ${cli__opts__extension} ]]; then
583
+ cli__opts__extension='.enc'
584
+ fi
585
+ }
586
+
587
+ function __symit::run() {
588
+ __symit::cleanup
589
+ __symit::init
590
+
591
+ cli__opts__verbose=''
592
+ cli__opts__quiet=''
593
+ cli__opts__key=${SYMIT__KEY}
594
+ cli__opts__extension=${SYMIT__EXTENSION}
595
+ cli__opts__folder=${SYMIT__FOLDER}
596
+ cli__opts__dry_run=''
597
+ cli__opts__action=edit
598
+ cli__opts__file=''
599
+
600
+ sym__actions__generate=' -cpgx '
601
+ sym__actions__edit=' -t '
602
+ sym__actions__encrypt='-e -f '
603
+ sym__actions__decrypt='-d -f '
604
+ sym__actions__auto=' -n '
605
+ sym__actions__key_secure=' -iqcpx '
606
+ sym__actions__key_insecure=' -iqcx '
607
+ sym__actions__install='install'
608
+
609
+ if [[ -z $1 ]]; then
610
+ __symit::usage
611
+ return $(__symit::exit 0)
612
+ fi
613
+
614
+ while :; do
615
+ case $1 in
616
+ -h|-\?|--help)
617
+ shift
618
+ __symit::usage
619
+ __symit::cleanup
620
+ return $(__symit::exit 0)
621
+ ;;
622
+
623
+ -k|--key)
624
+ shift
625
+ if [[ -z $1 ]]; then
626
+ __err "-k/--key requires an argument" && return $(__symit::exit 1)
627
+ else
628
+ cli__opts__key=$1
629
+ shift
630
+ fi
631
+ ;;
632
+
633
+ -x|--extension)
634
+ shift
635
+ if [[ -z $1 ]]; then
636
+ __err "-x/--extension requires an argument" && return $(__symit::exit 1)
637
+ else
638
+ cli__opts__extension=${1}
639
+ shift
640
+ fi
641
+ ;;
642
+
643
+ -f|--folder)
644
+ shift
645
+ if [[ -z $1 ]]; then
646
+ __err "-f/--folder requires an argument" && return $(__symit::exit 1)
647
+ else
648
+ cli__opts__folder=${1}
649
+ shift
650
+ fi
651
+ ;;
652
+
653
+ -a|--all-files)
654
+ shift
655
+ cli__opts__file="'*'"
656
+ ;;
657
+
658
+ -n|--dry-run)
659
+ shift
660
+ cli__opts__dry_run="yes"
661
+ ;;
662
+
663
+ -v|--verbose)
664
+ shift
665
+ cli__opts__verbose="yes"
666
+ ;;
667
+
668
+ -q|--quiet)
669
+ shift
670
+ cli__opts__quiet="yes"
671
+ ;;
672
+
673
+ import|key)
674
+ shift
675
+ cli__opts__action="key_secure"
676
+ ;;
677
+
678
+ insecure)
679
+ shift
680
+ if [[ "${cli__opts__action}" == 'key_secure' ]] ; then
681
+ cli__opts__action="key_insecure"
682
+ fi
683
+ ;;
684
+
685
+ --) # End of all options.
686
+ shift
687
+ break
688
+ ;;
689
+
690
+ -?*)
691
+ __err 'WARN: Unknown option: %s\n' "$1" >&2
692
+ return $(__symit::exit 127)
693
+ shift
694
+ ;;
695
+
696
+
697
+ ?*)
698
+ param=$1
699
+ v="sym__actions__${param}"
700
+ if [[ ! ${param} =~ '.' && -n "${!v}" ]]; then
701
+ __dbg "Action ${bldylw}${param}${clr} is a valid action."
702
+ cli__opts__action=${param}
703
+ else
704
+ __dbg "Parameter ${bldylw}${param}${clr} is not a valid action,"
705
+ __dbg "therefore it must be a file pattern."
706
+ cli__opts__file=${1}
707
+ fi
708
+ shift
709
+ ;;
710
+
711
+ *) # Default case: If no more options then break out of the loop.
712
+ break
713
+ shift
714
+ esac
715
+ done
716
+
717
+ [[ -n "${cli__opts__verbose}" ]] && __symit::print_cli_args
718
+
719
+ if [[ "${cli__opts__action}" == 'install' ]]; then
720
+ if [[ -n ${cli__opts__dry_run} ]]; then
721
+ __dbg "This command verifies that Sym is properly installed,"
722
+ __dbg "and if not found — installs it."
723
+ return $(__symit::exit 0)
724
+ else
725
+ __symit::install::gem
726
+ return $(__symit::exit 0)
727
+ fi
728
+ fi
729
+
730
+ __symit::validate_args
731
+
732
+ code=$?
733
+ if [[ ${code} != 0 ]]; then
734
+ return $(__symit::exit ${code})
735
+ fi
736
+
737
+ __symit::install::gem
738
+
739
+ changed_count=0
740
+
741
+ if [[ -n "${cli__opts__dry_run}" ]] ; then
742
+ __lib::color::h1 "DRY RUN"
743
+ for file in $(__symit::files); do
744
+ printf " \$ ${bldblu}$(__symit::command ${file})${clr}\n"
745
+ done
746
+ else
747
+ if [[ -n "${cli__opts__file}" ]]; then
748
+ [[ -n ${cli__opts__verbose} ]] && __dbg $(__symit::files)
749
+ declare -a file_list
750
+
751
+ for file in $(__symit::files); do
752
+ local cmd="$(__symit::command ${file})"
753
+ __lib::command::print "${cmd}"
754
+ eval "${cmd}"
755
+ code=$?; [[ ${code} != 0 ]] && __err "command '${bldblu}${cmd}${bldred}' exited with code ${bldylw}${code}"
756
+ changed_count=$(( ${changed_count} + 1))
757
+ done
758
+
759
+ if [[ ${changed_count} == 0 ]]; then
760
+ printf "${undylw}Bad news:${clr}\n\n"
761
+ __warn " No files matched your specification. The following 'find' command"
762
+ __warn " ran to find the file you requested. Please change the name, and "
763
+ __warn " try again.\n"
764
+ __warn " ${bldblu}$(__symit::files::cmd)${clr}\n\n"
765
+ return $(__symit::exit 5)
766
+ fi
767
+
768
+ else # opts[file]
769
+ cmd=$(__symit::command)
770
+ __lib::command::print "${cmd}"
771
+ eval "${cmd}"
772
+ code=$?; [[ ${code} != 0 ]] && return $(__symit::exit ${code})
773
+ changed_count=$(( ${changed_count} + 1))
774
+ fi
775
+ fi
776
+ }
777
+
778
+ function symit() {
779
+ __lib::stdout::configure
780
+ __symit::run $@
781
+ }