osp 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Release gem to https://rubygems.org.
4
+
5
+ SCRIPT_BASEDIR=$(dirname "$0")
6
+
7
+
8
+ set -e
9
+ which mv &> /dev/null || { echo 'ERROR: mv not found in PATH'; exit 1; }
10
+ which gem &> /dev/null || { echo 'ERROR: gem not found in PATH'; exit 1; }
11
+
12
+ cd "${SCRIPT_BASEDIR}/.."
13
+
14
+ # Load Environment Variables
15
+ [[ -f .env ]] && source .env
16
+
17
+ if [[ -z "${GEMSPEC_FILE}" ]] ; then
18
+ echo 'ERROR: one of the environment variables is missing'
19
+
20
+ echo "GEMSPEC_FILE: '${GEMSPEC_FILE}'"
21
+
22
+ exit 1
23
+ fi
24
+
25
+ gem_file=$(gem build "${GEMSPEC_FILE}" 2> /dev/null | grep 'File:' | tail -1 | awk '{ print $2 }')
26
+
27
+ echo "push gem file '$gem_file'"
28
+ gem push "$gem_file"
29
+
30
+ # Create tmp directory.
31
+ if [[ ! -d tmp/releases ]]; then
32
+ mkdir -p tmp/releases
33
+
34
+ chmod u=rwx,go-rwx tmp
35
+ chmod u=rwx,go-rwx tmp/releases
36
+ fi
37
+ mv -v -i "$gem_file" tmp/releases
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Run Tests.
4
+
5
+ SCRIPT_BASEDIR=$(dirname "$0")
6
+ RUBYOPT=-w
7
+ TZ=Europe/Vienna
8
+
9
+
10
+ set -e
11
+ which bundler &> /dev/null || { echo 'ERROR: bundler not found in PATH'; exit 1; }
12
+
13
+ cd "${SCRIPT_BASEDIR}/.."
14
+
15
+ bundler exec ./test/suite_all.rb
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Uninstall gem.
4
+
5
+ SCRIPT_BASEDIR=$(dirname "$0")
6
+
7
+
8
+ set -e
9
+ which gem &> /dev/null || { echo 'ERROR: gem not found in PATH'; exit 1; }
10
+
11
+ cd "${SCRIPT_BASEDIR}/.."
12
+
13
+ # Load Environment Variables
14
+ [[ -f .env ]] && source .env
15
+
16
+ if [[ -z "${GEM_NAME}" ]] ; then
17
+ echo 'ERROR: one of the environment variables is missing'
18
+
19
+ echo "GEM_NAME: '${GEM_NAME}'"
20
+
21
+ exit 1
22
+ fi
23
+
24
+ gem uninstall "${GEM_NAME}" --all --executables
@@ -2,7 +2,7 @@
2
2
  require 'thefox-ext'
3
3
 
4
4
  class String
5
- def is_valid?
6
- is_upper? || is_lower? || is_digit?
7
- end
5
+ def is_valid?
6
+ is_upper? || is_lower? || is_digit?
7
+ end
8
8
  end
@@ -3,188 +3,199 @@ require 'pathname'
3
3
  require 'fileutils'
4
4
 
5
5
  module TheFox
6
- module OSP
7
-
8
- class Database
9
-
10
- attr_accessor :has_changed
11
-
12
- def initialize(file_path, osp)
13
- @file_path = file_path
14
- @osp = osp
15
- @load_callback_method = nil
16
- @write_callback_method = nil
17
- @has_changed = false
18
-
19
- @data = {
20
- 'meta' => {
21
- 'version' => 1,
22
- 'created_at' => DateTime.now.to_s,
23
- 'updated_at' => DateTime.now.to_s,
24
- },
25
- 'hosts' => Hash.new,
26
- }
27
- end
28
-
29
- def load_callback_method=(m)
30
- @load_callback_method = m
31
- end
32
-
33
- def load_callback(*o)
34
- if !@load_callback_method.nil?
35
- @load_callback_method.call(*o)
36
- end
37
- end
38
-
39
- def load
40
- load_callback(1000, 'Check for existing database file.')
41
-
42
- if @file_path.exist?
43
- load_callback(1050, "Use database file: #{@file_path}")
44
-
45
- load_callback(1100, "Read file '#{@file_path}'.")
46
- db_meta = File.binread(@file_path)
47
-
48
- load_callback(1200, 'Process database metadata.')
49
- db_meta = Base64.strict_decode64(db_meta)
50
- db_meta = MessagePack.unpack(db_meta)
51
-
52
- db_e = Base64.strict_decode64(db_meta['db'])
53
- mac = OpenSSL::Digest::SHA256.digest(db_e)
54
- if db_meta['mac'] == mac
55
- load_callback(1300, 'Setup database decryption.')
56
- dk_sha256 = OpenSSL::Digest::SHA256.digest(@osp.dk)
57
- iv = Base64.strict_decode64(db_meta['iv'])
58
-
59
- aes = OpenSSL::Cipher::Cipher.new('AES-256-CBC')
60
- aes.decrypt
61
- aes.key = dk_sha256
62
- aes.iv = iv
63
-
64
- begin
65
- load_callback(1350, 'Decrypt database.')
66
- db_b64 = aes.update(db_e)
67
- db_b64 << aes.final
68
- rescue Exception #=> e
69
- raise 'Incorrect email and password combination.'
70
- end
71
-
72
- load_callback(1400, 'Build database.')
73
- @data = MessagePack.unpack(Base64.strict_decode64(db_b64))
74
-
75
- @data['hosts'] = @data['hosts'].map{ |name, host|
76
- host_o = TheFox::OSP::Host.from_h(host)
77
- host_o.osp = @osp
78
- [name, host_o]
79
- }.to_h
80
-
81
- load_callback(9000, 'Database startup done.')
82
- else
83
- raise 'Database integrity check failed.'
84
- end
85
- else
86
- load_callback(9500, 'Database startup done.')
87
- end
88
- end
89
-
90
- def write_callback_method=(m)
91
- @write_callback_method = m
92
- end
93
-
94
- def write_callback(*o)
95
- unless @write_callback_method.nil?
96
- @write_callback_method.call(*o)
97
- end
98
- end
99
-
100
- def write
101
- write_callback(1000, 'Check database for changes.')
102
-
103
- if @has_changed
104
- tmp = Pathname.new("#{@file_path}~").expand_path
105
-
106
- # http://stackoverflow.com/questions/9049789/aes-encryption-key-versus-iv
107
- # http://keepass.info/help/base/security.html
108
- # https://gist.github.com/byu/99651
109
-
110
- write_callback(1100, 'Make temp database.')
111
- db_c = @data.clone
112
- db_c['hosts'] = db_c['hosts'].map{ |name, host| [name, host.clone.to_h] }.to_h
113
-
114
- write_callback(1200, 'Setup database encryption.')
115
- dk_sha256 = OpenSSL::Digest::SHA256.digest(@osp.dk)
116
- iv = OpenSSL::Cipher::Cipher.new('AES-256-CBC').random_iv
117
-
118
- aes = OpenSSL::Cipher::Cipher.new('AES-256-CBC')
119
- aes.encrypt
120
- aes.key = dk_sha256
121
- aes.iv = iv
122
-
123
- write_callback(1250, 'Encrypt database.')
124
- db_e = aes.update(Base64.strict_encode64(db_c.to_msgpack))
125
- db_e << aes.final
126
-
127
- mac = OpenSSL::Digest::SHA256.digest(db_e)
128
-
129
- db_out = {
130
- 'version' => 1,
131
- 'iv' => Base64.strict_encode64(iv),
132
- 'db' => Base64.strict_encode64(db_e),
133
- 'mac' => mac,
134
- }
135
- db_out = db_out.to_msgpack
136
- db_out = Base64.strict_encode64(db_out)
137
-
138
- write_callback(1300, "Write temp file to '#{tmp}'.")
139
- File.write(tmp, 'tmp')
140
- tmp.chmod(0600)
141
- File.binwrite(tmp, db_out)
142
-
143
- backup_dts = Time.now.strftime('%Y%m%d-%H%M%S')
144
- backup = Pathname.new("#{@file_path}~backup_#{backup_dts}_" << Digest::SHA256.file(tmp.to_s).hexdigest[0..7])
145
-
146
- write_callback(1350, "Backup temp file to '#{backup}'.")
147
- File.write(backup, 'tmp')
148
- backup.chmod(0600)
149
- FileUtils.cp(tmp, backup)
150
-
151
- write_callback(1390, "Finally, move temp file to '#{@file_path}'.")
152
- File.write(@file_path, 'tmp')
153
- @file_path.chmod(0600)
154
- tmp.rename(@file_path)
155
-
156
- @has_changed = false
157
- else
158
- write_callback(9500, 'Nothing changed, nothing written.')
159
- end
160
- end
161
-
162
- def update
163
- @data['meta']['updated_at'] = DateTime.now.to_s
164
- end
165
-
166
- def hosts
167
- @data['hosts']
168
- end
169
-
170
- def add_host(host)
171
- @data['hosts'][host.name] = host
172
- update
173
- @has_changed = true
174
- end
175
-
176
- def remove_host(host)
177
- if @data['hosts'].has_key?(host.name)
178
- @data['hosts'].delete(host.name)
179
- update
180
- @has_changed = true
181
- true
182
- else
183
- false
184
- end
185
- end
186
-
187
- end
188
-
189
- end
6
+ module OSP
7
+
8
+ class Database
9
+
10
+ attr_accessor :has_changed
11
+
12
+ def initialize(file_path, osp)
13
+ @file_path = file_path
14
+ @osp = osp
15
+ @load_callback_method = nil
16
+ @write_callback_method = nil
17
+ @has_changed = false
18
+
19
+ @data = {
20
+ 'meta' => {
21
+ 'version' => 1,
22
+ 'created_at' => DateTime.now.to_s,
23
+ 'updated_at' => DateTime.now.to_s,
24
+ },
25
+ 'hosts' => Hash.new,
26
+ }
27
+ end
28
+
29
+ def load_callback_method=(m)
30
+ @load_callback_method = m
31
+ end
32
+
33
+ def load_callback(*o)
34
+ if !@load_callback_method.nil?
35
+ @load_callback_method.call(*o)
36
+ end
37
+ end
38
+
39
+ def load
40
+ load_callback(1000, 'Check for existing database file.')
41
+
42
+ if @file_path.exist?
43
+ load_callback(1050, "Use database file: #{@file_path}")
44
+
45
+ load_callback(1100, "Read file '#{@file_path}'.")
46
+ db_meta = File.binread(@file_path)
47
+
48
+ load_callback(1200, 'Process database metadata.')
49
+ db_meta = Base64.strict_decode64(db_meta)
50
+ db_meta = MessagePack.unpack(db_meta)
51
+
52
+ db_e = Base64.strict_decode64(db_meta['db'])
53
+ mac = OpenSSL::Digest::SHA256.digest(db_e)
54
+ if db_meta['mac'] == mac
55
+ load_callback(1300, 'Setup database decryption.')
56
+ dk_sha256 = OpenSSL::Digest::SHA256.digest(@osp.dk)
57
+ iv = Base64.strict_decode64(db_meta['iv'])
58
+
59
+ aes = OpenSSL::Cipher::AES.new(256, 'CBC')
60
+ aes.decrypt
61
+ aes.key = dk_sha256
62
+ aes.iv = iv
63
+
64
+ begin
65
+ load_callback(1350, 'Decrypt database.')
66
+ db_b64 = aes.update(db_e)
67
+ db_b64 << aes.final
68
+ rescue Exception #=> e
69
+ raise 'Incorrect email and password combination.'
70
+ end
71
+
72
+ load_callback(1400, 'Build database.')
73
+ @data = MessagePack.unpack(Base64.strict_decode64(db_b64))
74
+
75
+ @data['hosts'] = @data['hosts'].map{ |name, host|
76
+ host_o = TheFox::OSP::Host.from_h(host)
77
+ host_o.osp = @osp
78
+ [name, host_o]
79
+ }.to_h
80
+
81
+ load_callback(9000, 'Database startup done.')
82
+ else
83
+ raise 'Database integrity check failed.'
84
+ end
85
+ else
86
+ load_callback(9500, 'Database startup done.')
87
+ end
88
+ end
89
+
90
+ def write_callback_method=(m)
91
+ @write_callback_method = m
92
+ end
93
+
94
+ def write_callback(*o)
95
+ unless @write_callback_method.nil?
96
+ @write_callback_method.call(*o)
97
+ end
98
+ end
99
+
100
+ def write
101
+ write_callback(1000, 'Check database for changes.')
102
+
103
+ if @has_changed
104
+ tmp = Pathname.new("#{@file_path}~").expand_path
105
+
106
+ # http://stackoverflow.com/questions/9049789/aes-encryption-key-versus-iv
107
+ # http://keepass.info/help/base/security.html
108
+ # https://gist.github.com/byu/99651
109
+
110
+ write_callback(1100, 'Make temp database.')
111
+ db_c = @data.clone
112
+ db_c['hosts'] = db_c['hosts'].map{ |name, host| [name, host.clone.to_h] }.to_h
113
+
114
+ write_callback(1200, 'Setup database encryption.')
115
+ dk_sha256 = OpenSSL::Digest::SHA256.digest(@osp.dk)
116
+ iv = OpenSSL::Cipher::AES.new(256, 'CBC').random_iv
117
+
118
+ aes = OpenSSL::Cipher::AES.new(256, 'CBC')
119
+ aes.encrypt
120
+ aes.key = dk_sha256
121
+ aes.iv = iv
122
+
123
+ write_callback(1250, 'Encrypt database.')
124
+ db_e = aes.update(Base64.strict_encode64(db_c.to_msgpack))
125
+ db_e << aes.final
126
+
127
+ mac = OpenSSL::Digest::SHA256.digest(db_e)
128
+
129
+ db_out = {
130
+ 'version' => 1,
131
+ 'iv' => Base64.strict_encode64(iv),
132
+ 'db' => Base64.strict_encode64(db_e),
133
+ 'mac' => mac,
134
+ }
135
+ db_out = db_out.to_msgpack
136
+ db_out = Base64.strict_encode64(db_out)
137
+
138
+ write_callback(1300, "Write temp file to '#{tmp}'.")
139
+ File.write(tmp, 'tmp')
140
+ tmp.chmod(0600)
141
+ File.binwrite(tmp, db_out)
142
+
143
+ backup_dts = Time.now.strftime('%Y%m%d-%H%M%S')
144
+ backup = Pathname.new("#{@file_path}~backup_#{backup_dts}_" << Digest::SHA256.file(tmp.to_s).hexdigest[0..7])
145
+
146
+ write_callback(1350, "Backup temp file to '#{backup}'.")
147
+ File.write(backup, 'tmp')
148
+ backup.chmod(0600)
149
+ FileUtils.cp(tmp, backup)
150
+
151
+ write_callback(1390, "Finally, move temp file to '#{@file_path}'.")
152
+ File.write(@file_path, 'tmp')
153
+ @file_path.chmod(0600)
154
+ tmp.rename(@file_path)
155
+
156
+ @has_changed = false
157
+ else
158
+ write_callback(9500, 'Nothing changed, nothing written.')
159
+ end
160
+ end
161
+
162
+ def update
163
+ @data['meta']['updated_at'] = DateTime.now.to_s
164
+ end
165
+
166
+ def hosts
167
+ @data['hosts']
168
+ end
169
+
170
+ def add_host(host)
171
+ @data['hosts'][host.name] = host
172
+ update
173
+ @has_changed = true
174
+ end
175
+
176
+ def remove_host(host)
177
+ if @data['hosts'].has_key?(host.name)
178
+ @data['hosts'].delete(host.name)
179
+ update
180
+ @has_changed = true
181
+ true
182
+ else
183
+ false
184
+ end
185
+ end
186
+
187
+ ##
188
+ # Return all hosts and passwords as CSV string.
189
+ def csv
190
+ "HOST,PASSWORD\r\n"
191
+ @data['hosts']
192
+ .map{ |id, host|
193
+ "#{host.name},#{host.password}"
194
+ }
195
+ .join("\r\n")
196
+ end
197
+
198
+ end
199
+
200
+ end
190
201
  end