onboardbase 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b06da9457180637a621c7bb90cea5717e2290d23f42d64241e7632fd9e98a32b
4
+ data.tar.gz: 964d051e732645c0176ade32486eeb3bf53ef81b472c0933b721822652ec7504
5
+ SHA512:
6
+ metadata.gz: ccf1384fdd1a642a950347f3545d2cb757b0ef9d0b7d8102788b421febe3d9c25eaf242af1a710e04223789e28de690d1d00f319b89e152a823e1898ccc0c5a1
7
+ data.tar.gz: 1338c5e937270ad6dd67af4d928fe8f35958397fb5d11cfd59570f73486b1b91ee405542440d7772bcad791533049549e45609962a37eba6f33d5acac5a2f9aa
data/CHANGELOG.md ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/README.md ADDED
File without changes
data/Rakefile ADDED
File without changes
data/lib/AES.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'base64'
2
+ require 'digest'
3
+ require 'openssl'
4
+
5
+ module AESCrypt
6
+ def AESCrypt.encrypt(password, iv, cleardata)
7
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
8
+ cipher.encrypt # set cipher to be encryption mode
9
+ cipher.key = password
10
+ cipher.iv = iv
11
+ encrypted = ''
12
+ encrypted << cipher.update(cleardata)
13
+ encrypted << cipher.final
14
+ AESCrypt.b64enc(encrypted)
15
+ end
16
+
17
+ def AESCrypt.decrypt(password, iv, secretdata)
18
+ secretdata = Base64::decode64(secretdata)
19
+ decipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
20
+ decipher.decrypt
21
+ decipher.key = password
22
+ decipher.iv = iv if iv != nil
23
+ decipher.update(secretdata) + decipher.final
24
+ end
25
+
26
+ def AESCrypt.b64enc(data)
27
+ Base64.encode64(data).gsub(/\n/, '')
28
+ end
29
+
30
+ end
@@ -0,0 +1,3 @@
1
+ module Onboardbase
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,268 @@
1
+ require 'yaml'
2
+ require 'httparty'
3
+ require 'json'
4
+ require 'gibberish'
5
+ require 'digest/md5'
6
+ require 'AES'
7
+ require 'machineid'
8
+
9
+ module Onboardbase
10
+ class << self
11
+ attr_accessor :env, :is_dev
12
+
13
+ def is_dev
14
+ @is_dev.nil? ? false : @is_dev
15
+ end
16
+
17
+ def env
18
+ @env.nil?
19
+ end
20
+
21
+
22
+ def encrypt(password, iv, cleardata)
23
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
24
+ cipher.encrypt # set cipher to be encryption mode
25
+ cipher.key = password
26
+ cipher.iv = iv
27
+ encrypted = ''
28
+ encrypted << cipher.update(cleardata)
29
+ encrypted << cipher.final
30
+ b64enc(encrypted)
31
+ end
32
+
33
+ def decrypt(password, iv, secretdata)
34
+ secretdata = Base64::decode64(secretdata)
35
+ decipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
36
+ decipher.decrypt
37
+ decipher.key = password
38
+ decipher.iv = iv if iv != nil
39
+ decipher.update(secretdata) + decipher.final
40
+ end
41
+
42
+ def b64enc(data)
43
+ Base64.encode64(data).gsub(/\n/, '')
44
+ end
45
+
46
+ def apiURL
47
+ return "https://devapi.onboardbase.com/graphql" if self.is_dev
48
+ "https://api.onboardbase.com/graphql"
49
+ end
50
+
51
+ def configuration
52
+ @configuration ||= {}
53
+ end
54
+ def initialize
55
+ super
56
+ end
57
+
58
+ def config
59
+ yield self
60
+ end
61
+
62
+ def getWorkingDirectory
63
+ Dir.pwd
64
+ end
65
+
66
+ def getOnboardbaseDir
67
+ "#{Dir.home}/.onboardbase"
68
+ end
69
+
70
+ def getFallbackDir
71
+ "#{self.getOnboardbaseDir}/fallback"
72
+ end
73
+
74
+
75
+ def getProjectFallbackDir
76
+ project = self.configuration['setup']['project']
77
+ "#{self.getFallbackDir}/#{project}"
78
+ end
79
+
80
+ def getEnvironmentFallbackDir
81
+ environment = self.configuration['setup']['environment']
82
+ "#{self.getProjectFallbackDir}_#{environment}"
83
+ end
84
+
85
+ def config_exists?(directory)
86
+ return File.exist?(directory)
87
+ end
88
+
89
+
90
+ def loadConfig
91
+ configPath = self.getWorkingDirectory + '/onboardbase.yaml'
92
+ unless self.config_exists?(configPath)
93
+ puts "Please create onboardbase.yaml in the root of the project at: " + configPath
94
+ exit 1
95
+ end
96
+ config = YAML.load_file(configPath)
97
+ if (config['api_key'] == nil)
98
+ puts "Your onboardbase.yaml file does not have an api_key"
99
+ exit 1
100
+ end
101
+
102
+ if (config['passcode'] == nil)
103
+ puts "Your onboardbase.yaml file does not have a passcode"
104
+ exit 1
105
+ end
106
+ @configuration = config
107
+ end
108
+
109
+ def makeRequest
110
+ url = self.apiURL
111
+ headers = {
112
+ KEY: self.configuration['api_key'],
113
+ }
114
+ body = {
115
+ query: %{
116
+ query {
117
+ generalPublicProjects(filterOptions: { title: "#{self .configuration['setup']['project']}", disableCustomSelect: true }) {
118
+ list {
119
+ id
120
+ title
121
+ publicEnvironments(filterOptions: { title: "#{self .configuration['setup']['environment']}" }) {
122
+ list {
123
+ id
124
+ key
125
+ title
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ response = HTTParty.post(url, headers: headers, body: body)
134
+ JSON.parse(response.body)
135
+ end
136
+
137
+ def parseResponse?(response)
138
+ error = response["errors"]
139
+ data = response["data"]
140
+ return data["generalPublicProjects"] if error == nil
141
+ {:error=> true, :message => error[0]["message"] }
142
+ end
143
+
144
+ def getProject?(data)
145
+ project = data["list"][0]
146
+ return project if project != nil
147
+ false
148
+ end
149
+
150
+
151
+ def getSecrets?(project)
152
+ env = project["publicEnvironments"]["list"][0]
153
+ return JSON.parse(env["key"]) if env != nil
154
+ false
155
+ end
156
+
157
+ def bytes_to_key(data, salt, output=48)
158
+ merged = data + salt
159
+ key = Digest::MD5.digest(merged)
160
+ final_key = key
161
+ while final_key.length < output
162
+ key = Digest::MD5.digest(key + merged)
163
+ final_key = final_key + key
164
+ end
165
+ final_key[0..output-1]
166
+ end
167
+
168
+ def aes256_cbc_decrypt(key, data, iv)
169
+ key = Digest::SHA256.digest(key) if(key.kind_of?(String) && 32 != key.bytesize)
170
+ iv = Digest::MD5.digest(iv) if(iv.kind_of?(String) && 16 != iv.bytesize)
171
+ aes = OpenSSL::Cipher.new('AES-256-CBC')
172
+ aes.decrypt
173
+ aes.key = key
174
+ aes.iv = iv
175
+ aes.update(data) + aes.final
176
+ end
177
+
178
+ def parseSecrets(secrets)
179
+ secrets.each_with_index do |secret, i|
180
+ secret = Base64.decode64(secret)
181
+ unless secret[0..7] == 'Salted__'
182
+ puts "Invalid encrypted data"
183
+ exit(1)
184
+ end
185
+ salt = secret[8..15]
186
+ key_iv = bytes_to_key(self.configuration["passcode"], salt, 48)
187
+ key = key_iv[0..31]
188
+ iv = key_iv[32..]
189
+ parsedSecret = aes256_cbc_decrypt(key, secret[16..], iv)
190
+ secrets[i] = JSON.parse(parsedSecret)
191
+ end
192
+ secrets
193
+ end
194
+
195
+ def setEnv(secretsHash)
196
+ secretsHash.keys.sort.each do |key|
197
+ ENV["#{key}"] = "#{secretsHash[key]}"
198
+ end
199
+ # Overried local secrets
200
+ configSecrets = self.configuration["secrets"]
201
+ unless configSecrets
202
+ configSecrets = { "local" => {} }
203
+ end
204
+ configSecrets["local"].keys.sort.each do |key|
205
+ ENV["#{key}"] = "#{configSecrets["local"][key]}"
206
+ end
207
+ ENV.to_hash
208
+ end
209
+
210
+ def hashSecrets?(secretsArr)
211
+ secretsHash = Hash.new
212
+ secretsArr.each do |secret|
213
+ secretsHash["#{secret["key"]}"] = "#{secret["value"]}"
214
+ end
215
+ secretsHash
216
+ end
217
+
218
+ def storeToFallback?(secrets)
219
+ unless File.directory?(self.getOnboardbaseDir)
220
+ Dir.new(self.getOnboardbaseDir)
221
+ end
222
+ unless File.directory?(self.getFallbackDir)
223
+ Dir.new(self.getFallbackDir)
224
+ end
225
+ password = MachineID.ID?
226
+ cipher = Gibberish::AES::CBC.new(password)
227
+ cipher_text = cipher.encrypt(JSON.generate(secrets))
228
+ data = cipher_text
229
+ File.write(self.getEnvironmentFallbackDir, data, nil , mode: 'w')
230
+ end
231
+
232
+
233
+ def readFallback
234
+ unless File.directory?(self.getOnboardbaseDir)
235
+ Dir.new(self.getOnboardbaseDir)
236
+ end
237
+ unless File.directory?(self.getFallbackDir)
238
+ Dir.new(self.getFallbackDir)
239
+ end
240
+
241
+ if !File.exist?(self.getEnvironmentFallbackDir) || File.read(self.getEnvironmentFallbackDir).length <= 0
242
+ puts "No valid fallback for #{self .configuration['setup']['project']} project using #{self .configuration['setup']['environment']} environment"
243
+ return JSON.parse("{}") # Graceful failure, ensure the application continues to run without secrets.
244
+ end
245
+ data = File.read(self.getEnvironmentFallbackDir)
246
+ password = MachineID.ID?
247
+ cipher = Gibberish::AES::CBC.new(password)
248
+ decoded_data = cipher.decrypt(data)
249
+ JSON.parse(decoded_data)
250
+ end
251
+
252
+ def loadSecrets
253
+ self.loadConfig
254
+ response = self.parseResponse?(self.makeRequest)
255
+ if response[:error]
256
+ puts "Unable to fetch secrets with the specified api key, reading from fallback file"
257
+ secrets = self.readFallback
258
+ else
259
+ project = self.getProject?(response)
260
+ projectSecrets = self.getSecrets?(project)
261
+ parsedSecrets = self.parseSecrets(projectSecrets)
262
+ secrets = self.hashSecrets?(parsedSecrets)
263
+ end
264
+ finalEnvs = self.setEnv(secrets)
265
+ self.storeToFallback?(finalEnvs)
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path('lib/onboardbase/version', __dir__)
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'onboardbase'
5
+ spec.version = Onboardbase::VERSION
6
+ spec.authors = ['Onboardbase']
7
+ spec.email = ['admin@treadie.com']
8
+ spec.description = "Access onboardbase secrets in your Ruby projects"
9
+ spec.summary = "Use Onboardbase in your Ruby projects"
10
+ spec.homepage= 'https://github.com/Onboardbase/onboardbase-ruby'
11
+ spec.license = 'MIT'
12
+ spec.platform = Gem::Platform::RUBY
13
+ spec.required_ruby_version = '>= 2.5.0'
14
+
15
+ spec.files = Dir['README.md', 'LICENSE', 'CHANGELOG.md', 'lib/**/*.rb', 'lib/**/*.rake', 'onboardbase.gemspec', '.github/*.md', 'Gemfile', 'Rakefile']
16
+
17
+ spec.extra_rdoc_files = ['README.md']
18
+
19
+ spec.add_dependency 'rubyzip', '~> 2.3'
20
+ spec.add_dependency 'http', '~> 5.0.4'
21
+ spec.add_dependency 'httparty', '~> 0.20.0'
22
+ spec.add_dependency 'aes', '~> 0.5.1'
23
+ spec.add_dependency 'machineid', '~> 1.0.0'
24
+ spec.add_dependency 'gibberish', '~> 2.1.1'
25
+
26
+ spec.add_development_dependency 'rubocop', '~> 0.60'
27
+ spec.add_development_dependency 'rubocop-performance', '~> 1.5'
28
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.37'
29
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: onboardbase
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Onboardbase
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-02-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubyzip
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: http
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 5.0.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 5.0.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.20.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.20.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: aes
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: machineid
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.0.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.0.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: gibberish
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.1.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.1.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.60'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.60'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-performance
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.5'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.5'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.37'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.37'
139
+ description: Access onboardbase secrets in your Ruby projects
140
+ email:
141
+ - admin@treadie.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files:
145
+ - README.md
146
+ files:
147
+ - CHANGELOG.md
148
+ - Gemfile
149
+ - README.md
150
+ - Rakefile
151
+ - lib/AES.rb
152
+ - lib/onboardbase.rb
153
+ - lib/onboardbase/version.rb
154
+ - onboardbase.gemspec
155
+ homepage: https://github.com/Onboardbase/onboardbase-ruby
156
+ licenses:
157
+ - MIT
158
+ metadata: {}
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: 2.5.0
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubygems_version: 3.0.3.1
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: Use Onboardbase in your Ruby projects
178
+ test_files: []