safedb 0.01.0001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +793 -0
  7. data/Rakefile +16 -0
  8. data/bin/safe +5 -0
  9. data/lib/configs/README.md +58 -0
  10. data/lib/extension/array.rb +162 -0
  11. data/lib/extension/dir.rb +35 -0
  12. data/lib/extension/file.rb +123 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +572 -0
  15. data/lib/factbase/facts.safedb.net.ini +38 -0
  16. data/lib/interprete.rb +462 -0
  17. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  18. data/lib/keytools/kdf.api.rb +243 -0
  19. data/lib/keytools/kdf.bcrypt.rb +265 -0
  20. data/lib/keytools/kdf.pbkdf2.rb +262 -0
  21. data/lib/keytools/kdf.scrypt.rb +190 -0
  22. data/lib/keytools/key.64.rb +326 -0
  23. data/lib/keytools/key.algo.rb +109 -0
  24. data/lib/keytools/key.api.rb +1391 -0
  25. data/lib/keytools/key.db.rb +330 -0
  26. data/lib/keytools/key.docs.rb +195 -0
  27. data/lib/keytools/key.error.rb +110 -0
  28. data/lib/keytools/key.id.rb +271 -0
  29. data/lib/keytools/key.ident.rb +243 -0
  30. data/lib/keytools/key.iv.rb +107 -0
  31. data/lib/keytools/key.local.rb +259 -0
  32. data/lib/keytools/key.now.rb +402 -0
  33. data/lib/keytools/key.pair.rb +259 -0
  34. data/lib/keytools/key.pass.rb +120 -0
  35. data/lib/keytools/key.rb +585 -0
  36. data/lib/logging/gem.logging.rb +132 -0
  37. data/lib/modules/README.md +43 -0
  38. data/lib/modules/cryptology/aes-256.rb +154 -0
  39. data/lib/modules/cryptology/amalgam.rb +70 -0
  40. data/lib/modules/cryptology/blowfish.rb +130 -0
  41. data/lib/modules/cryptology/cipher.rb +207 -0
  42. data/lib/modules/cryptology/collect.rb +138 -0
  43. data/lib/modules/cryptology/crypt.io.rb +225 -0
  44. data/lib/modules/cryptology/engineer.rb +99 -0
  45. data/lib/modules/mappers/dictionary.rb +288 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/modules/storage/git.store.rb +399 -0
  48. data/lib/session/fact.finder.rb +334 -0
  49. data/lib/session/require.gem.rb +112 -0
  50. data/lib/session/time.stamp.rb +340 -0
  51. data/lib/session/user.home.rb +49 -0
  52. data/lib/usecase/cmd.rb +487 -0
  53. data/lib/usecase/config/README.md +57 -0
  54. data/lib/usecase/docker/README.md +146 -0
  55. data/lib/usecase/docker/docker.rb +49 -0
  56. data/lib/usecase/edit/README.md +43 -0
  57. data/lib/usecase/edit/delete.rb +46 -0
  58. data/lib/usecase/export.rb +40 -0
  59. data/lib/usecase/files/README.md +37 -0
  60. data/lib/usecase/files/eject.rb +56 -0
  61. data/lib/usecase/files/file_me.rb +78 -0
  62. data/lib/usecase/files/read.rb +169 -0
  63. data/lib/usecase/files/write.rb +89 -0
  64. data/lib/usecase/goto.rb +57 -0
  65. data/lib/usecase/id.rb +36 -0
  66. data/lib/usecase/import.rb +157 -0
  67. data/lib/usecase/init.rb +63 -0
  68. data/lib/usecase/jenkins/README.md +146 -0
  69. data/lib/usecase/jenkins/jenkins.rb +208 -0
  70. data/lib/usecase/login.rb +71 -0
  71. data/lib/usecase/logout.rb +28 -0
  72. data/lib/usecase/open.rb +71 -0
  73. data/lib/usecase/print.rb +40 -0
  74. data/lib/usecase/put.rb +81 -0
  75. data/lib/usecase/set.rb +44 -0
  76. data/lib/usecase/show.rb +138 -0
  77. data/lib/usecase/terraform/README.md +91 -0
  78. data/lib/usecase/terraform/terraform.rb +121 -0
  79. data/lib/usecase/token.rb +35 -0
  80. data/lib/usecase/update/README.md +55 -0
  81. data/lib/usecase/update/rename.rb +180 -0
  82. data/lib/usecase/use.rb +41 -0
  83. data/lib/usecase/verse.rb +20 -0
  84. data/lib/usecase/view.rb +71 -0
  85. data/lib/usecase/vpn/README.md +150 -0
  86. data/lib/usecase/vpn/vpn.ini +31 -0
  87. data/lib/usecase/vpn/vpn.rb +54 -0
  88. data/lib/version.rb +3 -0
  89. data/safedb.gemspec +34 -0
  90. metadata +193 -0
data/lib/usecase/id.rb ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+
6
+ class Id < UseCase
7
+
8
+
9
+ def execute
10
+
11
+ puts ""
12
+ puts KeyNow.grab()
13
+ puts KeyNow.fetch()
14
+ puts ""
15
+
16
+ return
17
+
18
+ end
19
+
20
+
21
+ # Perform pre-conditional validations in preparation to executing the main flow
22
+ # of events for this use case. This method may throw the below exceptions.
23
+ #
24
+ # @raise [SafeDirNotConfigured] if the safe's url has not been configured
25
+ # @raise [EmailAddrNotConfigured] if the email address has not been configured
26
+ # @raise [StoreUrlNotConfigured] if the crypt store url is not configured
27
+ def pre_validation
28
+
29
+
30
+ end
31
+
32
+
33
+ end
34
+
35
+
36
+ end
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+ # The <b>import use case</b> follows <b>open</b> and it pulls a file into an
6
+ # <em>(encrypted at rest)</em> <b>envelope</b> while writing metadata about
7
+ # the file into the opened tree dictionary position.
8
+ #
9
+ # == import and reimport commands
10
+ #
11
+ # - the import command expects a path parameter and errors if not recvd
12
+ # - the reimport command is happy with either one or zero parameters
13
+ #
14
+ # If the reimport command has no parameters it expects that the opened path
15
+ # already contains an imported file. It uses the import.path key to locate
16
+ # the file.
17
+ #
18
+ # If the path parameter is given to reimport it uses it and also resets the
19
+ # import.path key to reflect the path it was given.
20
+ #
21
+ # == garbage collect dangling files
22
+ #
23
+ # Like dangling envelopes - dangling files will pop up when re-imported.
24
+ # These are handled by the garbage collection policy which can be to
25
+ # remove immediately - remove on next login - remove after a time period
26
+ # or to never remove (manual garbage collection).
27
+ #
28
+ class Import < UseCase
29
+
30
+ attr_writer :secret_id, :secret_value
31
+
32
+ # The <b>put use case</b> follows <b>open</b> and it adds secrets into an
33
+ # <em>(encrypted at rest)</em> envelope. Put can be called many times to
34
+ # add secrets. Finally the <b>lock use case</b> commits all opened secrets
35
+ # into the configured storage engines.
36
+ #
37
+ # Calling <em>put</em> <b>before</b> calling open or <b>after</b> calling lock
38
+ # is not allowed and will result in an error.
39
+ #
40
+ # == Put Pre-Conditions
41
+ #
42
+ # When the put use case is called - the below conditions ring true.
43
+ #
44
+ # - the <b>folder path</b> ending in ../../my must exist
45
+ # - a session id, filename and encryption key ( in workstation config )
46
+ #
47
+ # == Observable Value
48
+ #
49
+ # The observable value delivered by +put+ boils down to
50
+ #
51
+ # - a new <b>friends.xyz123abc.os.txt</b> file if this is the first put.
52
+ # - a new group_name/key_name (like monica/surname) entry is added if required
53
+ # - a secret value is added against the key or updated if it already exists
54
+ # - a new session id and encryption key is generated and used to re-encrypt
55
+ def execute
56
+
57
+ return unless ops_key_exists?
58
+ master_db = KeyApi.read_master_db()
59
+
60
+ puts "---\n"
61
+ puts "--- The Master Database (Before)\n"
62
+ puts "---\n"
63
+ puts JSON.pretty_generate( master_db )
64
+ puts "---\n"
65
+
66
+ return if unopened_envelope?( master_db )
67
+
68
+ envelope_id = ENVELOPE_KEY_PREFIX + master_db[ ENV_PATH ]
69
+ has_content = KeyApi.db_envelope_exists?( master_db[ envelope_id ] )
70
+
71
+ # --
72
+ # -- To get hold of the content we must either
73
+ # --
74
+ # -- a) unlock it using the breadcrumbs or
75
+ # -- b) start afresh with a new content db
76
+ # --
77
+ content_box = KeyDb.from_json( KeyApi.content_unlock( master_db[ envelope_id ] ) ) if has_content
78
+ content_box = KeyDb.new() unless has_content
79
+ content_hdr = create_header()
80
+
81
+ # --
82
+ # -- If no content envelope exists we need to place
83
+ # -- an empty one inside the appdb content database.
84
+ # --
85
+ master_db[ envelope_id ] = {} unless has_content
86
+
87
+ # --
88
+ # -- This is the PUT use case so we append a
89
+ # --
90
+ # -- a) key for the new dictionary entry
91
+ # -- b) value for the new dictionary entry
92
+ # --
93
+ # -- into the current content envelope and write
94
+ # -- the envelope to the content filepath.
95
+ # --
96
+ crumbs_dict = master_db[ envelope_id ]
97
+ content_box.create_entry( master_db[ KEY_PATH ], @secret_id, @secret_value )
98
+ KeyApi.content_lock( crumbs_dict, content_box.to_json, content_hdr )
99
+
100
+ puts "---\n"
101
+ puts "--- The Master Database (After)\n"
102
+ puts "---\n"
103
+ puts JSON.pretty_generate( master_db )
104
+ puts "---\n"
105
+
106
+ # --
107
+ # -- Three envelope crumbs namely the external ID, the
108
+ # -- random iv and the crypt key are written afreshinto
109
+ # -- the master database.
110
+ # --
111
+ KeyApi.write_master_db( content_hdr, master_db )
112
+ print_put_success
113
+
114
+ return
115
+
116
+
117
+ # ---> secret_ids = @secret_id.split("/")
118
+ # ---> if ( envelope.has_key? secret_ids.first )
119
+ # ---> envelope[secret_ids.first][secret_ids.last] = @secret_value
120
+ # ---> else
121
+ # ---> envelope[secret_ids.first] = { secret_ids.last => @secret_value }
122
+ # ---> end
123
+
124
+ end
125
+
126
+
127
+ private
128
+
129
+
130
+ def print_put_success
131
+
132
+ puts ""
133
+ puts "Success putting a key/value pair into the open envelope."
134
+ puts "You can put more in and then close the envelope."
135
+ puts ""
136
+ puts " #{COMMANDMENT} close"
137
+ puts ""
138
+
139
+ end
140
+
141
+
142
+ # Perform pre-conditional validations in preparation to executing the main flow
143
+ # of events for this use case. This method may throw the below exceptions.
144
+ #
145
+ # @raise [SafeDirNotConfigured] if the safe's url has not been configured
146
+ # @raise [EmailAddrNotConfigured] if the email address has not been configured
147
+ # @raise [StoreUrlNotConfigured] if the crypt store url is not configured
148
+ def pre_validation
149
+
150
+
151
+ end
152
+
153
+
154
+ end
155
+
156
+
157
+ end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+ # The <b>init use case</b> initializes safe thus preparing it
6
+ # for the ability to lock secrets, unlock them, transport their keys and
7
+ # much more.
8
+ #
9
+ # safe is a <b>(glorified) placeholder</b>. It takes things in now,
10
+ # keeps them safe and gives them back later, in a <b>helpful manner</b>.
11
+ #
12
+ # == Alternat Error Flows
13
+ #
14
+ # An error will be thrown
15
+ #
16
+ # - if safe cannot create, extend, read or write the drive folder
17
+ # - if the domain is already in the configuration file
18
+ # - if domain has non alphanums, excl hyphens, underscores, @ symbols, periods
19
+ # - if domain does not begin or end with alphanums.
20
+ # - if non alpha-nums (excl at signs) appear consecutively
21
+ # - if no alpha-nums appear in the string
22
+ # - if the domain string's length is less than 5
23
+ # - if "safedb.net" appears twice (or more) in a directory tree
24
+ #
25
+ class Init < UseCase
26
+
27
+ attr_writer :master_p4ss, :domain_name, :base_path
28
+
29
+
30
+ # The init use case prepares the <b>safe</b> so that you can <b>open</b> an envelope,
31
+ # <b>put</b> secrets into it and then <b>seal</b> (lock) it. Locking effectively writes
32
+ # crypted blocks to both keystore and crypt store.
33
+ def execute
34
+
35
+ return unless ops_key_exists?
36
+
37
+ KeyApi.init_app_domain( @domain_name, @base_path )
38
+ keys_setup = KeyApi.is_domain_keys_setup?( @domain_name )
39
+
40
+ if ( keys_setup )
41
+ print_already_initialized
42
+ return
43
+ end
44
+
45
+ domain_password = KeyPass.password_from_shell( true )
46
+ KeyApi.setup_domain_keys( @domain_name, domain_password, create_header() )
47
+ print_domain_initialized
48
+
49
+ # --> unless @base_path.nil?
50
+ # --> key_api.register_keystore( @base_path )
51
+ # --> end
52
+
53
+ end
54
+
55
+
56
+ def pre_validation
57
+ end
58
+
59
+
60
+ end
61
+
62
+
63
+ end
@@ -0,0 +1,146 @@
1
+
2
+ # safe jenkins <command>
3
+
4
+
5
+ ### safe jenkins post [aws|docker|git] <<jenkins-host-url>> | introduction
6
+
7
+ Use **`safe jenkins post`** to inject both your **AWS IAM User** and **docker login/password** credentials into your Jenkins 2.0 continuous integration portal reachable by the **jenkins host url** given in the 4th parameter of the safe command.
8
+
9
+ ---
10
+
11
+ ## safe jenkins post | prerequisite
12
+
13
+ Before you can inject credentials into jenkins using **`safe jenkins post`** you must
14
+
15
+ - be logged into your safe
16
+ - have opened the appropriate chapter/verse
17
+ - have put the required credential key/value pairs into the safe
18
+ - have the jenkins service up and running
19
+
20
+ After the post (to jenkins), your continuous integration jobs will be able to access the credential values via their IDs as stated in the below table.
21
+
22
+ ---
23
+
24
+ ## safe jenkins post aws | key names table
25
+
26
+ As credentials are WORO (write once, read often), safe makes the reading part very very easy (and secure) so your effort is frontloaded.
27
+
28
+ | Safe Key | Jenkins Credential IDs | Environment Variable | Description |
29
+ |:-----------:|:----------------------:|:--------------------- |:-------------------------------------------------------- |
30
+ | @access.key | safe.aws.access.key | AWS_ACCESS_KEY_ID | The AWS IAM user's access key credential. |
31
+ | @secret.key | safe.aws.secret.key | AWS_SECRET_ACCESS_KEY | The AWS IAM user's secret key credential. |
32
+ | region.key | safe.aws.region.key | AWS_REGION | The AWS region key that your Jenkins service points to. |
33
+
34
+ So you can see that by convention, safe expects the credential keys in the safe to be named a particular way, and likewise, you can be assured of the IDs it gives those credentials when posted to Jenkins.
35
+
36
+
37
+ ## safe jenkins post | credentials lifecycle
38
+
39
+ The life of the credentials begins when you create an IAM user and record its access and secret keys. Then
40
+
41
+ - you login to safe and store the 3 keys and their values
42
+ - safe jenkins post will read the values and post them to Jenkins
43
+ - Jenkins stores the values in conjunction with the Jenkins Credential IDs
44
+ - pipeline jobs ask Jenkins to put the Credential ID values against environment variables
45
+ - tools like Terraform and AwsCli use the environment variables to work in the cloud
46
+
47
+
48
+ ## Jenkinsfile | Usage in Pipeline Jobs
49
+
50
+ Here is a pipeline declaration within a Jenkinsfile that asks Jenkins to put the credential values in its secrets store into the stated environment variables.
51
+
52
+ environment
53
+ {
54
+ AWS_ACCESS_KEY_ID = credentials( 'safe.aws.access.key' )
55
+ AWS_SECRET_ACCESS_KEY = credentials( 'safe.aws.secret.key' )
56
+ AWS_REGION = credentials( 'safe.aws.region.key' )
57
+ }
58
+
59
+ After **`safe jenkins post aws`** you can **click into the Credentials item in the Jenkins main menu** to assure yourself that the credentials have indeed been properly injected.
60
+
61
+ ---
62
+
63
+ ## How to Write AWS Credentials into your Safe
64
+
65
+ In order to **`safe terraform apply`** or **`safe jenkins post aws <<jenkins-host-url>>`** or `safe visit` you must first put those ubiquitous IAM programmatic user credentials into your safe.
66
+
67
+ $ safe login joebloggs.com # open the book
68
+
69
+ $ safe open iam dev.s3.reader # open chapter and verse
70
+ $ safe put @access.key ABCD1234EFGH5678 # Put IAM access key in safe
71
+ $ safe put @secret.key xyzabcd1234efgh5678 # Put IAM secret key in safe
72
+ $ safe put region.key eu-west-3 # infrastructure in Paris
73
+
74
+ $ safe open iam canary.admin # open chapter and verse
75
+ $ safe put @access.key 4321DCBA8765WXYZ # Put IAM access key in safe
76
+ $ safe put @secret.key 5678uvwx4321abcd9876 # Put IAM secret key in safe
77
+ $ safe put region.key eu-west-1 # infrastructure in Dublin
78
+
79
+ $ safe logout
80
+
81
+
82
+ ---
83
+
84
+
85
+ ## How to write DockerHub Credentials into your Safe
86
+
87
+ #### safe jenkins post docker https://jenkins.example.com
88
+
89
+ Before you can issue a **`safe jenkins post docker http://localhost:8080`** you must insert your docker login credentials in the form of a username and @password into your safe. Remember that any key starting with the `@ sign` tells the safe to keep it a secret like when you issue a **`safe show`** command.
90
+
91
+ $ safe login joebloggs.com # open the book
92
+ $ safe open docker production # at the docker (for production) chapter and verse
93
+ $ safe put username admin # Put the Docker repository login username into the safe
94
+ $ safe put @password secret12345 # Put the Docker repository login @password into the safe
95
+ $ safe logout
96
+
97
+ When docker credentials are injected into a Jenkins service the safe will expect to find a key at the open chapter and verse called username and another one called password.
98
+
99
+ The safe promises to inject credentials with an ID of **safe.docker.login.id** so any jenkins jobs that need to use the docker login username and password must specify this ID when talking to the Jenkins credentials service.
100
+
101
+
102
+ ### DockerHub Credentials Inject Response
103
+
104
+ Here is an example of posting dockerhub credentials into a Jenkins service running on the local machine.
105
+
106
+ ``` bash
107
+ safe jenkins post docker http://localhost:8080
108
+ ```
109
+
110
+ If successful safe provides a polite response detailing what just happened.
111
+
112
+ ```
113
+ - Jenkins Host Url : http://localhost:8080/credentials/store/system/domain/_/createCredentials
114
+ - Credentials ID : safe.docker.login.id
115
+ - Inject Username : devops4me
116
+ - So what is this? : The docker repository login credentials in the shape of a username and password.
117
+
118
+ % Total % Received % Xferd Average Speed Time Time Time Current
119
+ Dload Upload Total Spent Left Speed
120
+ 100 428 0 0 100 428 0 47555 --:--:-- --:--:-- --:--:-- 47555
121
+ ```
122
+
123
+ ---
124
+
125
+
126
+ ## safe integrations | we need your help
127
+
128
+ **You can help to extend safe's integrations.**
129
+
130
+ By design - safe integrations are simple to write. They primarily integrate with producers and consumers. To deliver efficacy to devops engineers safe will endeavour to
131
+
132
+ - **send** credentials to **downstream consumers** and
133
+ - **receive** credentials from **upstream producers**
134
+
135
+ safe needs pull requests from the devops community and it promises to always strive to keep the task of writing an integration extremely simple.
136
+
137
+ ### integrations | what giving takes?
138
+
139
+ Currently, writing an integration entails delivering 3 or 4 artifacts which are
140
+
141
+ - 1 simple Ruby class
142
+ - 1 README.md documenting the command structure, the prerequisites and the expected outcome
143
+ - 1 class containing unit tests
144
+ - (optionaly) an INI file if many configuration and facts are involved
145
+
146
+ Giving doesn't take much so roll up your sleeves (or frocks) and get writing.
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+ # This Jenkins use case handles the to and fro integration of secrets and sensitive information
6
+ # between the safe database under management and a Jenkins service pinpointed by an incoming
7
+ # host url parameter.
8
+ #
9
+ # This Jenkins use case injects for example the AWS IAM user access key, secret key and region key
10
+ # into a running Jenkins CI (Continuous Integration) service at the specified (url) location.
11
+ #
12
+ # safe jenkins post <<[ aws | docker | git ]>> <<jenkins-host-url>>
13
+
14
+ class Jenkins < UseCase
15
+
16
+ # The three instance variables provided through the command line like
17
+ # for example $ safe jenkins post aws http://localhost:8080
18
+ # For more info visit the documentation in the command interpreter class.
19
+ attr_writer :command, :service, :url
20
+
21
+ # If string variables EXPLODE throughout (and come to dominate) this class
22
+ # we should consider introducing an INI factfile like the [vpn] use case.
23
+ JENKINS_URI_PATH = "credentials/store/system/domain/_/createCredentials"
24
+
25
+ # If string variables EXPLODE throughout (and come to dominate) this class
26
+ # we should consider introducing an INI factfile like the [vpn] use case.
27
+ SECRET_KEY_VALUE_PAIR_DICTIONARY =
28
+ {
29
+ "scope" => "GLOBAL",
30
+ "$class" => "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl"
31
+ }
32
+
33
+ # If string variables EXPLODE throughout (and come to dominate) this class
34
+ # we should consider introducing an INI factfile like the [vpn] use case.
35
+ SECRET_KEY_VALUE_PAIR_TO_POST = { "" => "0", "credentials" => SECRET_KEY_VALUE_PAIR_DICTIONARY }
36
+
37
+
38
+ # If string variables EXPLODE throughout (and come to dominate) this class
39
+ # we should consider introducing an INI factfile like the [vpn] use case.
40
+ USERNAME_AND_PASSWORD_DICTIONARY =
41
+ {
42
+ "scope" => "GLOBAL",
43
+ "$class" => "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"
44
+ }
45
+
46
+ # If string variables EXPLODE throughout (and come to dominate) this class
47
+ # we should consider introducing an INI factfile like the [vpn] use case.
48
+ USERNAME_AND_PASSWORD_TO_POST = { "" => "0", "credentials" => USERNAME_AND_PASSWORD_DICTIONARY }
49
+
50
+
51
+
52
+ # Inject a Jenkins credential key-value pair that is secret and/or sensitive and
53
+ # needs to be referenced by executing continuous integration jobs.
54
+ #
55
+ # @param jenkins_base_url [String]
56
+ #
57
+ # This base url includes the scheme (protocol) which can be either http
58
+ # or https. It can include the port if it is not either 80 or 443. A common
59
+ # example is http://localhost:8080 but can also be https://jenkins.example.com
60
+ # It pays not to provide a trailing backslash on this url.
61
+ #
62
+ # @param credentials_id [String]
63
+ #
64
+ # The ID that Jenkins jobs will use to reference this credential's value.
65
+ #
66
+ # @param secret_value [String]
67
+ #
68
+ # The value of this credential (secret) that will be injected for SafeKeeping
69
+ # to the Jenkins service at the provided URL.
70
+ #
71
+ # @param description [String]
72
+ #
73
+ # Description of the credential that will be posted and can be viewed via
74
+ # the Jenkins user interface.
75
+ def inject_secret_key_value_pair( jenkins_base_url, credentials_id, secret_value, description )
76
+
77
+ jenkins_url = File.join( jenkins_base_url, JENKINS_URI_PATH )
78
+
79
+ credentials_dictionary = SECRET_KEY_VALUE_PAIR_DICTIONARY
80
+ credentials_dictionary.store( "id", credentials_id )
81
+ credentials_dictionary.store( "secret", secret_value )
82
+ credentials_dictionary.store( "description", description )
83
+
84
+ curl_cmd = "curl -X POST '#{jenkins_url}' --data-urlencode 'json=#{SECRET_KEY_VALUE_PAIR_TO_POST.to_json}'"
85
+
86
+ puts ""
87
+ puts " - Jenkins Host Url : #{jenkins_url}"
88
+ puts " - Credentials ID : #{credentials_id}"
89
+ puts " - So what is this? : #{description}"
90
+ puts ""
91
+
92
+ %x[ #{curl_cmd} ]
93
+
94
+ puts ""
95
+
96
+ end
97
+
98
+
99
+
100
+ # Inject into Jenkins a username and password pairing against an ID key that the
101
+ # continuous integration jobs know and can use to access the credentials pair.
102
+ #
103
+ # @param jenkins_base_url [String]
104
+ #
105
+ # This base url includes the scheme (protocol) which can be either http
106
+ # or https. It can include the port if it is not either 80 or 443. A common
107
+ # example is http://localhost:8080 but can also be https://jenkins.example.com
108
+ # It pays not to provide a trailing backslash on this url.
109
+ #
110
+ # @param credentials_id [String]
111
+ #
112
+ # The ID that Jenkins jobs will use to reference this credential's value.
113
+ #
114
+ # @param username [String]
115
+ #
116
+ # The value of this username (secret) that will be injected for SafeKeeping
117
+ # to the Jenkins service at the provided URL.
118
+ #
119
+ # @param password [String]
120
+ #
121
+ # The value of this password (secret) that will be injected for SafeKeeping
122
+ # to the Jenkins service at the provided URL.
123
+ #
124
+ # @param description [String]
125
+ #
126
+ # Description of the username and password pairing that will be posted and
127
+ # can be viewed via the Jenkins user interface.
128
+ def inject_username_and_password( jenkins_base_url, credentials_id, username, password, description )
129
+
130
+ jenkins_url = File.join( jenkins_base_url, JENKINS_URI_PATH )
131
+
132
+ credentials_dictionary = USERNAME_AND_PASSWORD_DICTIONARY
133
+ credentials_dictionary.store( "id", credentials_id )
134
+ credentials_dictionary.store( "username", username )
135
+ credentials_dictionary.store( "password", password )
136
+ credentials_dictionary.store( "description", description )
137
+
138
+ curl_cmd = "curl -X POST '#{jenkins_url}' --data-urlencode 'json=#{USERNAME_AND_PASSWORD_TO_POST.to_json}'"
139
+
140
+ puts ""
141
+ puts " - Jenkins Host Url : #{jenkins_url}"
142
+ puts " - Credentials ID : #{credentials_id}"
143
+ puts " - Inject Username : #{username}"
144
+ puts " - So what is this? : #{description}"
145
+ puts ""
146
+
147
+ %x[ #{curl_cmd} ]
148
+
149
+ puts ""
150
+
151
+ end
152
+
153
+
154
+
155
+ def execute
156
+
157
+ return unless ops_key_exists?
158
+ master_db = get_master_database()
159
+ return if unopened_envelope?( master_db )
160
+
161
+ # Get the open chapter identifier (id).
162
+ # Decide whether chapter already exists.
163
+ # Then get (or instantiate) the chapter's hash data structure
164
+ chapter_id = ENVELOPE_KEY_PREFIX + master_db[ ENV_PATH ]
165
+ verse_id = master_db[ KEY_PATH ]
166
+ chapter_exists = KeyApi.db_envelope_exists?( master_db[ chapter_id ] )
167
+
168
+ # Unlock the chapter data structure by supplying
169
+ # key/value mini-dictionary breadcrumbs sitting
170
+ # within the master database at the section labelled
171
+ # envelope@<<actual_chapter_id>>.
172
+ chapter_data = KeyDb.from_json( KeyApi.content_unlock( master_db[ chapter_id ] ) )
173
+
174
+ key_value_dictionary = chapter_data[ verse_id ]
175
+
176
+ inject_aws_credentials( key_value_dictionary ) if @service.eql?( "aws" )
177
+ inject_docker_credentials( key_value_dictionary ) if @service.eql?( "docker" )
178
+
179
+ end
180
+
181
+
182
+
183
+ def inject_aws_credentials( mini_dictionary )
184
+
185
+ access_key_desc = "The access key of the AWS IAM (programmatic) user credentials."
186
+ secret_key_desc = "The secret key of the AWS IAM (programmatic) user credentials."
187
+ region_key_desc = "The AWS region key for example eu-west-1 for Dublin in Ireland."
188
+
189
+ inject_secret_key_value_pair( @url, "safe.aws.access.key", mini_dictionary[ "@access.key" ], access_key_desc )
190
+ inject_secret_key_value_pair( @url, "safe.aws.secret.key", mini_dictionary[ "@secret.key" ], secret_key_desc )
191
+ inject_secret_key_value_pair( @url, "safe.aws.region.key", mini_dictionary[ "region.key" ], region_key_desc )
192
+
193
+ end
194
+
195
+
196
+ def inject_docker_credentials( mini_dictionary )
197
+
198
+ docker_desc = "The docker repository login credentials in the shape of a username and password."
199
+
200
+ inject_username_and_password( @url, "safe.docker.login.id", mini_dictionary[ "docker.username" ], mini_dictionary[ "@docker.password" ], docker_desc )
201
+
202
+ end
203
+
204
+
205
+ end
206
+
207
+
208
+ end
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+ # The <b>login use case</b> is given the domain name and if needs be
6
+ # it collects the password then (if correct) logs the user in.
7
+ #
8
+ # Here are some key facts about the login command
9
+ #
10
+ # - its domain name parameter is mandatory
11
+ # - it is called at the start of every session
12
+ # - it is undone by the logout command
13
+ # - it requires the shell token environment variable to be set
14
+ # - you can nest login commands thus using multiple domains
15
+ # - you can call it with a --with=password switch
16
+ # - a space before the command prevents it being logged in .bash_history
17
+ # - you can deliver the password in multiple ways
18
+ class Login < UseCase
19
+
20
+ attr_writer :master_p4ss, :domain_name
21
+
22
+
23
+ def execute
24
+
25
+ return unless ops_key_exists?
26
+
27
+ unless ( KeyApi.is_domain_keys_setup?( @domain_name ) )
28
+ print_not_initialized
29
+ return
30
+ end
31
+
32
+ ############## Call [[ KeyApi.is_logged_in? ]] - then print msg and skip password collection below
33
+ ############## Call [[ KeyApi.is_logged_in? ]] - then print msg and skip password collection below
34
+ ############## Call [[ KeyApi.is_logged_in? ]] - then print msg and skip password collection below
35
+ ############## Call [[ KeyApi.is_logged_in? ]] - then print msg and skip password collection below
36
+ ############## Call [[ KeyApi.is_logged_in? ]] - then print msg and skip password collection below
37
+ ############## Call [[ KeyApi.is_logged_in? ]] - then print msg and skip password collection below
38
+
39
+ domain_secret = KeyPass.password_from_shell( false )
40
+
41
+ ############## Use [[ KeyApi.valid_password? ]] and give error if not valid
42
+ ############## Use [[ KeyApi.valid_password? ]] and give error if not valid
43
+ ############## Use [[ KeyApi.valid_password? ]] and give error if not valid
44
+ ############## Use [[ KeyApi.valid_password? ]] and give error if not valid
45
+ ############## Use [[ KeyApi.valid_password? ]] and give error if not valid
46
+
47
+ KeyApi.do_login( @domain_name, domain_secret, create_header() )
48
+
49
+ view_uc = View.new
50
+ view_uc.flow_of_events
51
+
52
+ end
53
+
54
+
55
+ # Perform pre-conditional validations in preparation to executing the main flow
56
+ # of events for this use case. This method may throw the below exceptions.
57
+ #
58
+ # @raise [SafeDirNotConfigured] if the safe's url has not been configured
59
+ # @raise [EmailAddrNotConfigured] if the email address has not been configured
60
+ # @raise [StoreUrlNotConfigured] if the crypt store url is not configured
61
+ def pre_validation
62
+
63
+ end
64
+
65
+
66
+ end
67
+
68
+
69
+ end
70
+
71
+