osp 0.6.1 → 0.7.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 +5 -5
- data/.editorconfig +5 -4
- data/.env.example +2 -0
- data/.gitignore +4 -3
- data/.gitlab-ci.yml +37 -37
- data/.travis.yml +17 -16
- data/README.md +6 -2
- data/Vagrantfile +34 -0
- data/bin/.gitignore +1 -0
- data/bin/dev_setup.sh +19 -0
- data/bin/install.sh +51 -0
- data/bin/osp +342 -329
- data/bin/release.sh +37 -0
- data/bin/test.sh +15 -0
- data/bin/uninstall.sh +24 -0
- data/lib/ext/string.rb +3 -3
- data/lib/osp/database.rb +195 -184
- data/lib/osp/host.rb +112 -116
- data/lib/osp/osp.rb +185 -150
- data/lib/osp/ospdev.rb +12 -10
- data/lib/osp/ospdotcom.rb +14 -14
- data/lib/osp/version.rb +5 -5
- data/osp.gemspec +25 -25
- data/osp.sublime-project +8 -8
- metadata +11 -5
- data/Makefile +0 -21
- data/Makefile.common +0 -58
data/bin/release.sh
ADDED
@@ -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
|
data/bin/test.sh
ADDED
@@ -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
|
data/bin/uninstall.sh
ADDED
@@ -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
|
data/lib/ext/string.rb
CHANGED
data/lib/osp/database.rb
CHANGED
@@ -3,188 +3,199 @@ require 'pathname'
|
|
3
3
|
require 'fileutils'
|
4
4
|
|
5
5
|
module TheFox
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|