onboardbase 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []