carioca 2.1.3 → 2.1.5
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 +4 -4
- data/.rubocop.yml +2 -4
- data/Gemfile +0 -1
- data/README.md +53 -0
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/carioca.gemspec +4 -4
- data/lib/carioca/configuration.rb +1 -1
- data/lib/carioca/constants.rb +9 -12
- data/lib/carioca/dependencies.rb +0 -4
- data/lib/carioca/mixin.rb +5 -5
- data/lib/carioca/registry.rb +8 -8
- data/lib/carioca/registry_file.rb +2 -2
- data/lib/carioca/services/debug.rb +3 -3
- data/lib/carioca/services/finisher.rb +108 -115
- data/lib/carioca/services/init.rb +1 -1
- data/lib/carioca/services/output.rb +6 -6
- data/lib/carioca/services/sanitycheck.rb +90 -106
- data/lib/carioca/services/securestore.rb +74 -75
- data/lib/carioca/services/setup.rb +55 -60
- data/lib/carioca/services/toolbox.rb +93 -94
- data/samples/Gemfile +7 -0
- data/samples/test.rb +39 -43
- metadata +19 -19
- data/Gemfile.lock +0 -132
@@ -1,143 +1,127 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# base Carioca namespace
|
4
4
|
module Carioca
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module Services
|
6
|
+
# Exiter namespace
|
7
|
+
class Sanitycheck
|
8
|
+
def initialize
|
9
|
+
registry = Carioca::Registry.get
|
10
|
+
@output = registry.get_service name: :output
|
11
|
+
@i18n = registry.get_service name: :i18n
|
12
|
+
@toolbox = registry.get_service name: :toolbox
|
13
|
+
@configuration = registry.get_service name: :configuration
|
14
|
+
@finisher = registry.get_service name: :finisher
|
15
|
+
@schema = {}
|
16
|
+
if @configuration.settings.include? :sanitycheck
|
17
|
+
@schema = @configuration.settings.sanitycheck.include?(:rules) ? @configuration.settings.sanitycheck.rules : {}
|
18
|
+
end
|
19
|
+
end
|
10
20
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
def run
|
22
|
+
unless @schema.empty?
|
23
|
+
begin
|
24
|
+
@output.info @i18n.t('sanitycheck.run.start')
|
25
|
+
error_number = 0
|
26
|
+
@schema.each do |item|
|
27
|
+
testcase = item[:test]
|
28
|
+
item.delete(:test)
|
29
|
+
res = send(testcase, **item)
|
30
|
+
if res.empty?
|
31
|
+
@output.ok @i18n.t('sanitycheck.run.ok', testcase:, name: item[:name].to_s)
|
32
|
+
else
|
33
|
+
pbm = res.map(&:to_s).join(',')
|
34
|
+
@output.ko @i18n.t('sanitycheck.run.ko', testcase:, name: item[:name].to_s, pbm:)
|
35
|
+
error_number = + 1
|
36
|
+
end
|
22
37
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
testcase = item[:test] ; item.delete(:test)
|
31
|
-
res = self.send(testcase, **item)
|
32
|
-
if res.empty? then
|
33
|
-
@output.ok @i18n.t('sanitycheck.run.ok', testcase: testcase, name: item[:name].to_s)
|
34
|
-
else
|
35
|
-
pbm = res.map {|p| p.to_s}.join(',')
|
36
|
-
@output.ko @i18n.t('sanitycheck.run.ko', testcase: testcase, name: item[:name].to_s, pbm: pbm)
|
37
|
-
error_number =+ 1
|
38
|
-
end
|
39
|
-
end
|
40
|
-
if error_number>0 then
|
41
|
-
@output.error @i18n.t('sanitycheck.failure')
|
42
|
-
else
|
43
|
-
@output.success @i18n.t('sanitycheck.success')
|
44
|
-
end
|
45
|
-
rescue Exception
|
46
|
-
@finisher.secure_raise message: @i18n.t('sanitychek.error'), error_case: :status_ko
|
47
|
-
end unless @schema.empty?
|
48
|
-
|
38
|
+
if error_number.positive?
|
39
|
+
@output.error @i18n.t('sanitycheck.failure')
|
40
|
+
else
|
41
|
+
@output.success @i18n.t('sanitycheck.success')
|
42
|
+
end
|
43
|
+
rescue StandardError
|
44
|
+
@finisher.secure_raise message: @i18n.t('sanitychek.error'), error_case: :status_ko
|
49
45
|
end
|
46
|
+
end
|
47
|
+
end
|
50
48
|
|
49
|
+
# @!group Verifiers for application : FS and TCP/IP services
|
51
50
|
|
51
|
+
# check folder
|
52
|
+
# @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
|
53
|
+
# @option [String] :name folder path (relative or absolute)
|
54
|
+
# @option [String] :mode String for OCTAL rights like "644", default 755
|
55
|
+
# @option [String] :owner file owner for folder, optionnal
|
56
|
+
# @option [String] :group file group for folder, optionnal
|
57
|
+
def verify_folder(name:, mode: '755', owner: nil, group: nil)
|
58
|
+
full_name = File.expand_path(name)
|
59
|
+
res = []
|
60
|
+
return [:inexistant] unless File.directory?(full_name)
|
52
61
|
|
53
|
-
#@!group Verifiers for application : FS and TCP/IP services
|
54
|
-
|
55
|
-
# check folder
|
56
|
-
# @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
|
57
|
-
# @option [String] :name folder path (relative or absolute)
|
58
|
-
# @option [String] :mode String for OCTAL rights like "644", default 755
|
59
|
-
# @option [String] :owner file owner for folder, optionnal
|
60
|
-
# @option [String] :group file group for folder, optionnal
|
61
|
-
def verify_folder(name:, mode: "755", owner: nil, group: nil)
|
62
|
-
full_name = File.expand_path(name)
|
63
|
-
res = Array::new
|
64
|
-
return [:inexistant] unless File.directory?(full_name)
|
65
62
|
stat = File.stat(full_name)
|
66
|
-
if mode
|
67
|
-
tested_mode =
|
68
|
-
res << :mode if tested_mode[-3
|
63
|
+
if mode
|
64
|
+
tested_mode = format('%o', stat.mode)
|
65
|
+
res << :mode if tested_mode[-3..] != mode
|
69
66
|
end
|
70
|
-
if owner
|
71
|
-
|
72
|
-
|
73
|
-
if group then
|
74
|
-
res << :group if Etc.getgrgid(stat.gid).name != group
|
75
|
-
end
|
76
|
-
return res
|
67
|
+
res << :owner if owner && (Etc.getpwuid(stat.uid).name != owner)
|
68
|
+
res << :group if group && (Etc.getgrgid(stat.gid).name != group)
|
69
|
+
res
|
77
70
|
end
|
78
|
-
|
71
|
+
|
79
72
|
# check symlink
|
80
73
|
# @return [Boolean]
|
81
74
|
# @option [String] :name path of the link
|
82
|
-
def verify_link(name:
|
83
|
-
full_name = File.expand_path(name)
|
84
|
-
res =
|
75
|
+
def verify_link(name:)
|
76
|
+
full_name = File.expand_path(name)
|
77
|
+
res = []
|
85
78
|
res.push :inexistant unless File.file?(full_name)
|
86
|
-
|
79
|
+
res
|
87
80
|
end
|
88
|
-
|
81
|
+
|
89
82
|
# check file
|
90
83
|
# @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
|
91
84
|
# @option [String] :name path of file
|
92
85
|
# @option [String] :mode String for OCTAL rights like "644", optionnal
|
93
86
|
# @option [String] :owner file owner for file, optionnal
|
94
87
|
# @option [String] :group file group for file, optionnal
|
95
|
-
def verify_file(name
|
88
|
+
def verify_file(name:, mode: '644', owner: nil, group: nil)
|
96
89
|
full_name = File.expand_path(name)
|
97
|
-
res =
|
98
|
-
return
|
90
|
+
res = []
|
91
|
+
return [:inexistant] unless File.file?(full_name)
|
92
|
+
|
99
93
|
stat = File.stat(full_name)
|
100
|
-
if mode
|
101
|
-
tested_mode =
|
102
|
-
res << :mode if tested_mode[-3
|
94
|
+
if mode
|
95
|
+
tested_mode = format('%o', stat.mode)
|
96
|
+
res << :mode if tested_mode[-3..] != mode
|
103
97
|
end
|
104
|
-
if owner
|
105
|
-
|
106
|
-
|
107
|
-
if group then
|
108
|
-
res << :group if Etc.getgrgid(stat.gid).name != group
|
109
|
-
end
|
110
|
-
return res
|
98
|
+
res << :owner if owner && (Etc.getpwuid(stat.uid).name != owner)
|
99
|
+
res << :group if group && (Etc.getgrgid(stat.gid).name != group)
|
100
|
+
res
|
111
101
|
end
|
112
|
-
|
102
|
+
|
113
103
|
# TCP/IP service checker
|
114
104
|
# @return [Bool] status
|
115
|
-
# @option [String] :name display name
|
116
105
|
# @option [String] :host hostname
|
117
106
|
# @option [String] :port TCP port
|
118
107
|
# @option [String] :url full URL, priority on :host and :port
|
119
|
-
def verify_service(
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
return true
|
131
|
-
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
132
|
-
return false
|
133
|
-
end
|
134
|
-
end
|
135
|
-
rescue Timeout::Error
|
108
|
+
def verify_service(url: nil, host: nil, port: nil)
|
109
|
+
if url
|
110
|
+
uri = URI.parse(url)
|
111
|
+
host = uri.host
|
112
|
+
port = uri.port
|
113
|
+
end
|
114
|
+
Timeout.timeout(1) do
|
115
|
+
s = TCPSocket.new(host, port)
|
116
|
+
s.close
|
117
|
+
return true
|
118
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
136
119
|
return false
|
137
120
|
end
|
121
|
+
rescue Timeout::Error
|
122
|
+
false
|
138
123
|
end
|
139
|
-
|
140
|
-
|
141
|
-
end
|
124
|
+
# !@endgroup
|
142
125
|
end
|
126
|
+
end
|
143
127
|
end
|
@@ -1,81 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Carioca
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
attr_accessor :data
|
7
|
-
|
8
|
-
extend Carioca::Injector
|
9
|
-
inject service: :logger
|
10
|
-
inject service: :setup
|
11
|
-
inject service: :sanitycheck
|
4
|
+
module Services
|
5
|
+
class SecureStore
|
6
|
+
attr_accessor :data
|
12
7
|
|
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
|
-
decipher.decrypt
|
57
|
-
encoded_key = File.read(@keyfile)
|
58
|
-
key, iv = Base64.decode64(encoded_key).split('|')
|
59
|
-
decipher.key = key
|
60
|
-
decipher.iv = iv
|
61
|
-
encoded = File.read(@storefile)
|
62
|
-
encrypted = Base64.decode64(encoded)
|
63
|
-
plain = decipher.update(encrypted) + decipher.final
|
64
|
-
YAML.load(plain)
|
65
|
-
end
|
66
|
-
|
67
|
-
def encrypt(data)
|
68
|
-
encoded_key = File.read(@keyfile)
|
69
|
-
key, iv = Base64.decode64(encoded_key).split('|')
|
70
|
-
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
71
|
-
cipher.encrypt
|
72
|
-
cipher.key = key
|
73
|
-
cipher.iv = iv
|
74
|
-
encrypted = cipher.update(data.to_yaml) + cipher.final
|
75
|
-
encoded = Base64.encode64(encrypted)
|
76
|
-
File.write(@storefile, encoded)
|
77
|
-
end
|
8
|
+
extend Carioca::Injector
|
9
|
+
inject service: :logger
|
10
|
+
inject service: :setup
|
11
|
+
inject service: :sanitycheck
|
12
|
+
|
13
|
+
def initialize(storefile: Carioca::Registry.config.secure_store_file, keyfile: Carioca::Registry.config.master_key_file)
|
14
|
+
[storefile, keyfile].map { |file| File.dirname(file) }.each do |path|
|
15
|
+
setup.make_folder path: File.expand_path(path), mode: '400' unless sanitycheck.verify_folder name: path
|
16
|
+
end
|
17
|
+
@storefile = File.expand_path(storefile)
|
18
|
+
@keyfile = File.expand_path(keyfile)
|
19
|
+
init! unless initialized?
|
20
|
+
@data = decrypt
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialized?
|
24
|
+
File.exist?(@storefile) && File.exist?(@keyfile)
|
25
|
+
end
|
26
|
+
|
27
|
+
def save!
|
28
|
+
encrypt(@data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def init!
|
32
|
+
path = File.dirname(@storefile)
|
33
|
+
FileUtils.mkdir_p path
|
34
|
+
generate_key
|
35
|
+
init_data = {}
|
36
|
+
encrypt(init_data)
|
37
|
+
logger.warn(to_s) { 'Secure Store initialized' }
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def generate_key
|
43
|
+
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
44
|
+
cipher.encrypt
|
45
|
+
key = cipher.random_key
|
46
|
+
iv = cipher.random_iv
|
47
|
+
encoded_key = Base64.encode64("#{key}|#{iv}")
|
48
|
+
unless File.exist? @keyfile
|
49
|
+
File.write(@keyfile, encoded_key)
|
50
|
+
FileUtils.chmod 0o400, @keyfile
|
78
51
|
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def decrypt
|
55
|
+
decipher = OpenSSL::Cipher.new('aes-256-cbc')
|
56
|
+
decipher.decrypt
|
57
|
+
encoded_key = File.read(@keyfile)
|
58
|
+
key, iv = Base64.decode64(encoded_key).split('|')
|
59
|
+
decipher.key = key
|
60
|
+
decipher.iv = iv
|
61
|
+
encoded = File.read(@storefile)
|
62
|
+
encrypted = Base64.decode64(encoded)
|
63
|
+
plain = decipher.update(encrypted) + decipher.final
|
64
|
+
YAML.safe_load(plain)
|
65
|
+
end
|
66
|
+
|
67
|
+
def encrypt(data)
|
68
|
+
encoded_key = File.read(@keyfile)
|
69
|
+
key, iv = Base64.decode64(encoded_key).split('|')
|
70
|
+
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
71
|
+
cipher.encrypt
|
72
|
+
cipher.key = key
|
73
|
+
cipher.iv = iv
|
74
|
+
encrypted = cipher.update(data.to_yaml) + cipher.final
|
75
|
+
encoded = Base64.encode64(encrypted)
|
76
|
+
File.write(@storefile, encoded)
|
77
|
+
end
|
79
78
|
end
|
79
|
+
end
|
80
80
|
end
|
81
|
-
|
@@ -1,87 +1,82 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# base Carioca namespace
|
4
4
|
module Carioca
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@schema = (@configuration.settings.setup.include? :rules)? @configuration.settings.setup.rules : {}
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
5
|
+
module Services
|
6
|
+
# Exiter namespace
|
7
|
+
class Setup
|
8
|
+
def initialize
|
9
|
+
registry = Carioca::Registry.get
|
10
|
+
@output = registry.get_service name: :output
|
11
|
+
@i18n = registry.get_service name: :i18n
|
12
|
+
@toolbox = registry.get_service name: :toolbox
|
13
|
+
@configuration = registry.get_service name: :configuration
|
14
|
+
@finisher = registry.get_service name: :finisher
|
15
|
+
@schema = {}
|
16
|
+
if @configuration.settings.include? :setup
|
17
|
+
@schema = @configuration.settings.setup.include?(:rules) ? @configuration.settings.setup.rules : {}
|
18
|
+
end
|
19
|
+
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@finisher.secure_raise message: @i18n.t('setup.error'), error_case: :status_ko
|
34
|
-
end unless @schema.empty?
|
21
|
+
def execute!
|
22
|
+
unless @schema.empty?
|
23
|
+
begin
|
24
|
+
@output.info @i18n.t('setup.execute.start')
|
25
|
+
@schema.each do |item|
|
26
|
+
action = item[:action]
|
27
|
+
item.delete(:action)
|
28
|
+
send action, **item
|
35
29
|
end
|
30
|
+
rescue StandardError
|
31
|
+
@finisher.secure_raise message: @i18n.t('setup.error'), error_case: :status_ko
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
36
35
|
|
36
|
+
# @!group facilities for file system commands
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# @option [Bool] :gem resolve file in gem root (default true)
|
50
|
-
# @option [String] :gem_name name of the gem where to search (default "carioca")
|
51
|
-
def install_file(source:, target:, mode: "644", owner: nil, group: nil, force: true, gem: true, gem_name: "carioca" )
|
52
|
-
@output.item @i18n.t('setup.install', file: target)
|
38
|
+
# facility for file installation
|
39
|
+
# @option [String] :source file source path
|
40
|
+
# @option [String] :target file target path
|
41
|
+
# @option [String] :mode String for OCTAL rights (default "644")
|
42
|
+
# @option [String] :owner file owner for target (optional)
|
43
|
+
# @option [String] :group file group for target (optional)
|
44
|
+
# @option [Bool] :force to copy file in force (default true)
|
45
|
+
# @option [Bool] :gem resolve file in gem root (default true)
|
46
|
+
# @option [String] :gem_name name of the gem where to search (default "carioca")
|
47
|
+
def install_file(source:, target:, mode: '644', owner: nil, group: nil, force: true, gem: true, gem_name: 'carioca')
|
48
|
+
@output.item @i18n.t('setup.install', file: target)
|
53
49
|
full_target = File.expand_path(target)
|
54
|
-
source =
|
55
|
-
FileUtils
|
50
|
+
source = @toolbox.search_file_in_gem(gem: gem_name, file: source) if gem
|
51
|
+
FileUtils.copy source, full_target if force
|
56
52
|
FileUtils.chmod mode.to_i(8), full_target
|
57
|
-
FileUtils.chown owner, group, full_target if owner
|
53
|
+
FileUtils.chown owner, group, full_target if owner && group
|
58
54
|
end
|
59
|
-
|
55
|
+
|
60
56
|
# facility for folder creation
|
61
57
|
# @option [String] :path folder path (relative or absolute)
|
62
58
|
# @option [String] :mode String for OCTAL rights like "644"
|
63
59
|
# @option [String] :owner file owner for folder
|
64
60
|
# @option [String] :group file group for folder
|
65
|
-
|
61
|
+
def make_folder(path:, mode: '644', owner: nil, group: nil)
|
66
62
|
full_path = File.expand_path(path)
|
67
63
|
@output.item @i18n.t('setup.mkdir', path: full_path)
|
68
|
-
FileUtils
|
64
|
+
FileUtils.mkdir_p path unless File.exist? full_path
|
69
65
|
FileUtils.chmod mode.to_i(8), full_path
|
70
|
-
FileUtils.chown owner, group, full_path if owner
|
66
|
+
FileUtils.chown owner, group, full_path if owner && group
|
71
67
|
end
|
72
|
-
|
68
|
+
|
73
69
|
# facility for Symbolic link
|
74
70
|
# @option [String] :source path of the file
|
75
71
|
# @option [String] :link path of the symlink
|
76
72
|
def make_link(source:, link:)
|
77
|
-
|
78
|
-
|
79
|
-
@output.item @i18n.t('setup.ln', target: link, source:
|
80
|
-
FileUtils
|
81
|
-
FileUtils
|
73
|
+
File.expand_path(source)
|
74
|
+
File.expand_path(link)
|
75
|
+
@output.item @i18n.t('setup.ln', target: link, source:)
|
76
|
+
FileUtils.rm link if File.symlink?(link) && !File.exist?(link)
|
77
|
+
FileUtils.ln_s source, link unless File.exist? link
|
82
78
|
end
|
83
79
|
# @!endgroup
|
84
|
-
|
85
|
-
end
|
86
80
|
end
|
81
|
+
end
|
87
82
|
end
|