carioca 2.0.12 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.debride_withelist +49 -0
- data/.github/workflows/main.yml +7 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +44 -4
- data/LICENSE.txt +21 -0
- data/README.md +651 -16
- data/Rakefile +15 -0
- data/VERSION +1 -1
- data/assets/images/carioca_output_emoji_colors.PNG +0 -0
- data/assets/images/carioca_output_emoji_no_colors.PNG +0 -0
- data/assets/images/carioca_output_no_emoji_colors.PNG +0 -0
- data/assets/images/carioca_output_no_emoji_no_colors.PNG +0 -0
- data/assets/images/description_carioca.png +0 -0
- data/assets/images/description_configuration_carioca.png +0 -0
- data/assets/images/description_container_carioca.png +0 -0
- data/assets/images/description_registry_carioca.png +0 -0
- data/assets/images/description_services_carioca.png +0 -0
- data/assets/images/logo_carioca_full_large.png +0 -0
- data/assets/images/logo_carioca_full_small.png +0 -0
- data/assets/images/logo_carioca_light_small.png +0 -0
- data/bom.xml +746 -0
- data/carioca.gemspec +3 -0
- data/config/locales/en.yml +34 -0
- data/config/locales/fr.yml +35 -1
- data/lib/carioca/configuration.rb +6 -2
- data/lib/carioca/constants.rb +38 -1
- data/lib/carioca/dependencies.rb +13 -0
- data/lib/carioca/services/config.rb +1 -1
- data/lib/carioca/services/finisher.rb +121 -0
- data/lib/carioca/services/sanitycheck.rb +143 -0
- data/lib/carioca/services/securestore.rb +81 -0
- data/lib/carioca/services/setup.rb +87 -0
- data/lib/carioca/services/toolbox.rb +104 -0
- data/samples/config/carioca.registry +0 -15
- data/samples/config/locales/en.yml +2 -1
- data/samples/config/settings.yml +29 -6
- data/samples/test.rb +111 -11
- metadata +64 -2
data/carioca.gemspec
CHANGED
@@ -41,6 +41,9 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_development_dependency 'rubocop', '~> 1.32'
|
42
42
|
spec.add_development_dependency 'yard', '~> 0.9.27'
|
43
43
|
spec.add_development_dependency 'yard-rspec', '~> 0.1'
|
44
|
+
spec.add_development_dependency "bundle-audit", "~> 0.1.0"
|
44
45
|
spec.metadata['rubygems_mfa_required'] = 'false'
|
45
46
|
spec.add_dependency 'version', '~> 1.1'
|
47
|
+
spec.add_runtime_dependency 'ps-ruby','~> 0.0.4'
|
48
|
+
spec.add_development_dependency "cyclonedx-ruby", "~> 1.1"
|
46
49
|
end
|
data/config/locales/en.yml
CHANGED
@@ -21,3 +21,37 @@ en:
|
|
21
21
|
output:
|
22
22
|
load:
|
23
23
|
context: "Output service initialized in mode : %{confset}"
|
24
|
+
finisher:
|
25
|
+
messages:
|
26
|
+
not_root: "This operation need to be run as root (use sudo or rvmsudo)"
|
27
|
+
options_incompatibility: "Options incompatibility"
|
28
|
+
service_dependence_missing: "Appifier Service dependence missing"
|
29
|
+
config_required: "Specific configuration required"
|
30
|
+
setup_error: "Setup terminated unsuccessfully"
|
31
|
+
setup_success: "Setup terminated successfully"
|
32
|
+
sanitycheck_error: "Sanitycheck terminated unsuccessfully"
|
33
|
+
sanitycheck_success: "Sanitycheck terminated successfully"
|
34
|
+
configuration_error: "Configuration Error"
|
35
|
+
success_exit: "Operation sucessfull"
|
36
|
+
error_exit: "Operation failure"
|
37
|
+
interrupt: "User operation interrupted"
|
38
|
+
not_found: "Object not found"
|
39
|
+
already_exist: "Object already exist"
|
40
|
+
status_ok: "Status OK"
|
41
|
+
status_ko: "Status KO"
|
42
|
+
bad_usage: "Case must be a return or an exit"
|
43
|
+
setup:
|
44
|
+
error: "Setup operation failed"
|
45
|
+
execute:
|
46
|
+
start: "Begining setup :"
|
47
|
+
install: "Installation of file %{file}"
|
48
|
+
mkdir: "Creation of folder %{path}"
|
49
|
+
ln: "Creation of symlink %{target} -> %{source}"
|
50
|
+
sanitycheck:
|
51
|
+
error: "Sanitycheck stop on Configuration error"
|
52
|
+
success: "Sanitycheck finish without errors"
|
53
|
+
failure: "Sanitycheck finish with errors"
|
54
|
+
run:
|
55
|
+
start: "Begining sanitycheck :"
|
56
|
+
ok: "Testcase %{testcase} on %{name} is ok"
|
57
|
+
ko: "Testcase %{testcase} on %{name} is ok, problem(s) : %{pbm}"
|
data/config/locales/fr.yml
CHANGED
@@ -20,4 +20,38 @@ fr:
|
|
20
20
|
success: "Service de configuration initialisé avec succès depuis : %{from}"
|
21
21
|
output:
|
22
22
|
load:
|
23
|
-
context: "Service output initialisé en mode : %{confset}"
|
23
|
+
context: "Service output initialisé en mode : %{confset}"
|
24
|
+
finisher:
|
25
|
+
messages:
|
26
|
+
not_root: "L'opération doit être lancée en root (utiliser sudo ou rvmsudo)"
|
27
|
+
options_incompatibility: "Options incompatibles"
|
28
|
+
service_dependence_missing: "Service Carioca, dépendence manquante"
|
29
|
+
config_required: "Configuration spécifique requise"
|
30
|
+
setup_error: "Setup terminé avec succès"
|
31
|
+
setup_success: "Setup terminé en échec"
|
32
|
+
sanitycheck_error: "Sanitycheck terminé en echec"
|
33
|
+
sanitycheck_success: "Sanitycheck terminé avec succès"
|
34
|
+
configuration_error: "Erreur de configuration"
|
35
|
+
success_exit: "Opération terminée avec succès"
|
36
|
+
error_exit: "Opération terminée en échec"
|
37
|
+
interrupt: "Opération interrompue par l'utilisateur"
|
38
|
+
not_found: "Objet non trouvé"
|
39
|
+
already_exist: "L'objet existé déjà"
|
40
|
+
status_ok: "Status OK"
|
41
|
+
status_ko: "Status KO"
|
42
|
+
bad_usage: "return_case et exit_case sont incompatibles"
|
43
|
+
setup:
|
44
|
+
error: "Opération en echec durant le setup"
|
45
|
+
execute:
|
46
|
+
start: "Début du setup :"
|
47
|
+
install: "Installation du fichier %{file}"
|
48
|
+
mkdir: "Creation du répertoire %{path}"
|
49
|
+
ln: "Creation du lien symbolique %{target} -> %{source}"
|
50
|
+
sanitycheck:
|
51
|
+
error: "Sanitycheck stoppé sur une erreur de configuration"
|
52
|
+
success: "Sanitycheck terminé sans erreurs"
|
53
|
+
failure: "Sanitycheck termoiné avec des erreurs"
|
54
|
+
run:
|
55
|
+
start: "Démarrage du sanitycheck :"
|
56
|
+
ok: "Testcase %{testcase} sur %{name} ok"
|
57
|
+
ko: "Testcase %{testcase} sur %{name} ko, problème(s) : %{pbm}"
|
@@ -5,7 +5,8 @@ module Carioca
|
|
5
5
|
include Carioca::Constants
|
6
6
|
include Carioca::Helpers
|
7
7
|
attr_accessor :filename, :name, :builtins, :log_target, :default_locale, :locales_load_path, :debugger_tracer,
|
8
|
-
:config_file, :config_root, :environment, :
|
8
|
+
:config_file, :config_root, :environment, :supported_environments, :output_mode, :log_level, :output_target, :user_config_path,
|
9
|
+
:master_key_file, :secure_store_file
|
9
10
|
attr_writer :init_from_file, :output_colors, :output_emoji
|
10
11
|
attr_reader :log_file, :locales_availables, :debug
|
11
12
|
|
@@ -21,13 +22,16 @@ module Carioca
|
|
21
22
|
@environment = DEFAULT_ENVIRONMENT.dup
|
22
23
|
@config_root = DEFAULT_CONFIG_ROOT.dup
|
23
24
|
@log_target = '::Logger::new(STDOUT)'
|
24
|
-
@
|
25
|
+
@supported_environments = DEFAULT_ENVIRONMENTS_LIST.dup
|
25
26
|
@default_locale = DEFAULT_LOCALE
|
26
27
|
@locales_availables = []
|
27
28
|
@output_mode = DEFAULT_OUTPUT_MODE.dup
|
28
29
|
@output_colors = DEFAULT_COLORS_STATUS.dup
|
29
30
|
@output_emoji = DEFAULT_EMOJI_STATUS.dup
|
30
31
|
@output_target = DEFAULT_OUTPUT_TARGET.dup
|
32
|
+
@user_config_path = DEFAULT_USER_CONFIG_PATH.dup
|
33
|
+
@master_key_file = DEFAULT_MASTER_KEY_FILE.dup
|
34
|
+
@secure_store_file = DEFAULT_SECURE_STORE_FILE.dup
|
31
35
|
path = search_file_in_gem('carioca', 'config/locales')
|
32
36
|
@locales_load_path = Dir["#{File.expand_path(path)}/*.yml"]
|
33
37
|
Dir["#{path}/*.yml"].sort.each do |file|
|
data/lib/carioca/constants.rb
CHANGED
@@ -14,6 +14,11 @@ module Carioca
|
|
14
14
|
DEFAULT_COLORS_STATUS = true
|
15
15
|
DEFAULT_LOG_LEVEL = :info
|
16
16
|
|
17
|
+
DEFAULT_USER_CONFIG_PATH = "~/.carioca"
|
18
|
+
|
19
|
+
DEFAULT_MASTER_KEY_FILE = "#{DEFAULT_USER_CONFIG_PATH}/master.key"
|
20
|
+
DEFAULT_SECURE_STORE_FILE = "#{DEFAULT_USER_CONFIG_PATH}/secure.Store"
|
21
|
+
|
17
22
|
DEFAULT_DEBUGGER_TRACER = :output
|
18
23
|
|
19
24
|
# service definitions specs
|
@@ -24,6 +29,9 @@ module Carioca
|
|
24
29
|
|
25
30
|
DEFAULT_ENVIRONMENTS_LIST = %i[production staging test development].freeze
|
26
31
|
|
32
|
+
|
33
|
+
|
34
|
+
|
27
35
|
BUILTINS = {
|
28
36
|
configuration: {
|
29
37
|
type: :internal,
|
@@ -50,7 +58,7 @@ module Carioca
|
|
50
58
|
},
|
51
59
|
output: {
|
52
60
|
type: :internal,
|
53
|
-
description: 'The Output
|
61
|
+
description: 'The Output service of Carioca',
|
54
62
|
service: "Carioca::Services::Output::Provider::new(
|
55
63
|
mode: Carioca::Registry.config.output_mode,
|
56
64
|
emoji: Carioca::Registry.config.output_emoji?,
|
@@ -59,6 +67,35 @@ module Carioca
|
|
59
67
|
target: Carioca::Registry.config.output_target
|
60
68
|
)"
|
61
69
|
},
|
70
|
+
finisher: {
|
71
|
+
type: :internal,
|
72
|
+
service: 'Carioca::Services::Finisher::new',
|
73
|
+
description: 'The Finisher service of Carioca',
|
74
|
+
depends: [:i18n,:logger, :configuration]
|
75
|
+
},
|
76
|
+
toolbox: {
|
77
|
+
type: :internal,
|
78
|
+
service: 'Carioca::Services::Toolbox',
|
79
|
+
description: 'The Misceleanous Toolbox service of Carioca',
|
80
|
+
},
|
81
|
+
setup: {
|
82
|
+
type: :internal,
|
83
|
+
service: 'Carioca::Services::Setup::new',
|
84
|
+
description: 'The Setup service of Carioca',
|
85
|
+
depends: [:i18n,:logger, :configuration ]
|
86
|
+
},
|
87
|
+
sanitycheck: {
|
88
|
+
type: :internal,
|
89
|
+
service: 'Carioca::Services::Sanitycheck::new',
|
90
|
+
description: 'The Sanitycheck service of Carioca',
|
91
|
+
depends: [:i18n,:logger, :configuration ]
|
92
|
+
},
|
93
|
+
securestore: {
|
94
|
+
type: :internal,
|
95
|
+
service: 'Carioca::Services::SecureStore::new',
|
96
|
+
description: 'The SecureStore service of Carioca',
|
97
|
+
depends: [:i18n,:logger, :configuration ]
|
98
|
+
},
|
62
99
|
debugger: {
|
63
100
|
type: :internal,
|
64
101
|
description: 'The Debugger Service of Carioca',
|
data/lib/carioca/dependencies.rb
CHANGED
@@ -4,11 +4,24 @@ require 'yaml'
|
|
4
4
|
require 'forwardable'
|
5
5
|
require 'singleton'
|
6
6
|
|
7
|
+
require 'socket'
|
8
|
+
require 'yaml'
|
9
|
+
require 'thread'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'etc'
|
12
|
+
require 'json'
|
13
|
+
require 'uri'
|
14
|
+
require 'openssl'
|
15
|
+
require 'base64'
|
16
|
+
|
17
|
+
|
18
|
+
|
7
19
|
require 'rubygems'
|
8
20
|
require 'i18n'
|
9
21
|
require 'locale'
|
10
22
|
require 'deep_merge'
|
11
23
|
require 'pastel'
|
24
|
+
require 'ps-ruby'
|
12
25
|
|
13
26
|
require_relative 'helpers'
|
14
27
|
require_relative 'constants'
|
@@ -67,7 +67,7 @@ module Carioca
|
|
67
67
|
@data = {} unless @data.instance_of?(Hash)
|
68
68
|
@data.delete_if { |key, _value| config.config_root != key }
|
69
69
|
@data[config.config_root] = {} unless @data.include? config.config_root
|
70
|
-
config.
|
70
|
+
config.supported_environments.each do |evt|
|
71
71
|
@data[config.config_root][evt] = {} unless @data[config.config_root].include? evt
|
72
72
|
end
|
73
73
|
@data[config.config_root][:default] = {} unless @data[config.config_root].include? :default
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# base Carioca namespace
|
4
|
+
module Carioca
|
5
|
+
|
6
|
+
module Services
|
7
|
+
|
8
|
+
class SpecificError < Exception
|
9
|
+
attr_reader :error_case
|
10
|
+
def initialize(*arg, error_case: :status_ko)
|
11
|
+
super(*arg)
|
12
|
+
@error_case = error_case
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Exiter namespace
|
17
|
+
class Finisher
|
18
|
+
|
19
|
+
DEFAULT_FINISHERS_SPECS = {
|
20
|
+
# global
|
21
|
+
not_root: { code: 40, key: 'finisher.messages.not_root' },
|
22
|
+
options_incompatibility: { code: 410, key: 'finisher.messages.options_incompatibility'},
|
23
|
+
service_dependence_missing: { code: 430, key: 'finisher.messages.service_dependence_missing'},
|
24
|
+
config_required: { code: 420, key: 'finisher.messages.config_required'},
|
25
|
+
setup_error: { code: 520, key: 'finisher.messages.setup_error'},
|
26
|
+
setup_success: { code: 0, key: 'finisher.messages.setup_success'},
|
27
|
+
sanitycheck_error: { code: 510, key: 'finisher.messages.sanitycheck_error'},
|
28
|
+
sanitycheck_success: { code: 0, key: 'finisher.messages.sanitycheck_success'},
|
29
|
+
configuration_error: { code: 501, key: 'finisher.messages.configuration_error'},
|
30
|
+
success_exit: { code: 0, key: 'finisher.messages.success_exit' },
|
31
|
+
quiet_exit: { code: 0 },
|
32
|
+
error_exit: { code: 50, key: 'finisher.messages.error_exit' },
|
33
|
+
# events
|
34
|
+
interrupt: { code: 330, key: 'finisher.messages.interrupt' },
|
35
|
+
# request
|
36
|
+
not_found: { code: 404, key: 'finisher.messages.not_found' },
|
37
|
+
already_exist: { code: 408, key: 'finisher.messages.already_exist' },
|
38
|
+
# daemon
|
39
|
+
status_ok: { code: 200, key: 'finisher.messages.status_ok' },
|
40
|
+
status_ko: { code: 500, key: 'finisher.messages.status_ko' }
|
41
|
+
}
|
42
|
+
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
registry = Carioca::Registry.get
|
46
|
+
@output = registry.get_service name: :output
|
47
|
+
@i18n = registry.get_service name: :i18n
|
48
|
+
@configuration = registry.get_service name: :configuration
|
49
|
+
@exit_map = DEFAULT_FINISHERS_SPECS
|
50
|
+
@exit_map.merge! @configuration.settings.exit_cases if @configuration.settings.exit_cases
|
51
|
+
end
|
52
|
+
|
53
|
+
def terminate(return_case: nil, exit_case: nil, more: nil )
|
54
|
+
raise "Case must be a return or an exit" if return_case and exit_case
|
55
|
+
do_exit!( exit_case: exit_case, more: more) if exit_case
|
56
|
+
do_return(return_case: return_case, more: more) if return_case
|
57
|
+
end
|
58
|
+
|
59
|
+
# exiter
|
60
|
+
# @option [Symbol] :case an exit case
|
61
|
+
# @option [String] :more a complementary string to display
|
62
|
+
def do_exit!(exit_case: :quiet_exit, more: nil )
|
63
|
+
mess = ""
|
64
|
+
mess = @i18n.t(@exit_map[exit_case][:key]) if @exit_map[exit_case].include? :key
|
65
|
+
mess << " : " unless mess.empty? or not more
|
66
|
+
mess << "#{more}" if more
|
67
|
+
if @exit_map[exit_case][:code] == 0 then
|
68
|
+
@output.success mess unless mess.empty?
|
69
|
+
exit 0
|
70
|
+
else
|
71
|
+
@output.fatal mess unless mess.empty?
|
72
|
+
exit @exit_map[exit_case][:code]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def do_return(return_case: :status_ok, more: nil )
|
77
|
+
data = @exit_map[return_case].clone
|
78
|
+
if data.include? :key then
|
79
|
+
data[:message] = @i18n.t(data[:key])
|
80
|
+
data.delete :key
|
81
|
+
end
|
82
|
+
data[:more] = more if more
|
83
|
+
return data
|
84
|
+
end
|
85
|
+
|
86
|
+
def secure_raise(message: "unknown error", error_case: :status_ko)
|
87
|
+
raise SpecificError::new message, error_case: error_case
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def secure_api_return(data: nil, return_case: nil, structured: false, json: true)
|
92
|
+
result = {}
|
93
|
+
begin
|
94
|
+
data = yield if block_given?
|
95
|
+
result = (structured)? do_return(return_case: return_case).merge({data: data }) : data
|
96
|
+
rescue Exception => e
|
97
|
+
key = (e.respond_to? :error_case)? e.error_case : :status_ko
|
98
|
+
more = (e.respond_to? :error_case)? e.message : "#{e.class.to_s} : #{e.message}"
|
99
|
+
result = do_return return_case: key, more: more
|
100
|
+
end
|
101
|
+
result = JSON.pretty_generate(JSON.parse(result.to_json)) if json
|
102
|
+
return result
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def secure_execute!( exit_case: :success_exit )
|
107
|
+
result = {}
|
108
|
+
begin
|
109
|
+
more = yield
|
110
|
+
|
111
|
+
rescue Exception => e
|
112
|
+
key = (e.respond_to? :error_case)? e.error_case : :error_exit
|
113
|
+
more = (e.respond_to? :error_case)? e.message : "#{e.class.to_s} : #{e.message}"
|
114
|
+
exit_case = key
|
115
|
+
end
|
116
|
+
do_exit! exit_case: exit_case, more: more
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# base Carioca namespace
|
4
|
+
module Carioca
|
5
|
+
|
6
|
+
module Services
|
7
|
+
|
8
|
+
# Exiter namespace
|
9
|
+
class Sanitycheck
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
registry = Carioca::Registry.get
|
13
|
+
@output = registry.get_service name: :output
|
14
|
+
@i18n = registry.get_service name: :i18n
|
15
|
+
@toolbox = registry.get_service name: :toolbox
|
16
|
+
@configuration = registry.get_service name: :configuration
|
17
|
+
@finisher = registry.get_service name: :finisher
|
18
|
+
@schema = {}
|
19
|
+
if @configuration.settings.include? :sanitycheck then
|
20
|
+
@schema = (@configuration.settings.sanitycheck.include? :rules)? @configuration.settings.sanitycheck.rules : {}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
begin
|
26
|
+
result = []
|
27
|
+
@output.info @i18n.t('sanitycheck.run.start')
|
28
|
+
error_number = 0
|
29
|
+
@schema.each do |item|
|
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
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
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
|
+
stat = File.stat(full_name)
|
66
|
+
if mode then
|
67
|
+
tested_mode = "%o" % stat.mode
|
68
|
+
res << :mode if tested_mode[-3..-1] != mode
|
69
|
+
end
|
70
|
+
if owner then
|
71
|
+
res << :owner if Etc.getpwuid(stat.uid).name != owner
|
72
|
+
end
|
73
|
+
if group then
|
74
|
+
res << :group if Etc.getgrgid(stat.gid).name != group
|
75
|
+
end
|
76
|
+
return res
|
77
|
+
end
|
78
|
+
|
79
|
+
# check symlink
|
80
|
+
# @return [Boolean]
|
81
|
+
# @option [String] :name path of the link
|
82
|
+
def verify_link(name: )
|
83
|
+
full_name = File.expand_path(name)
|
84
|
+
res = Array::new
|
85
|
+
res.push :inexistant unless File.file?(full_name)
|
86
|
+
return res
|
87
|
+
end
|
88
|
+
|
89
|
+
# check file
|
90
|
+
# @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
|
91
|
+
# @option [String] :name path of file
|
92
|
+
# @option [String] :mode String for OCTAL rights like "644", optionnal
|
93
|
+
# @option [String] :owner file owner for file, optionnal
|
94
|
+
# @option [String] :group file group for file, optionnal
|
95
|
+
def verify_file(name: , mode: '644', owner: nil, group: nil)
|
96
|
+
full_name = File.expand_path(name)
|
97
|
+
res = Array::new
|
98
|
+
return [:inexistant] unless File.file?(full_name)
|
99
|
+
stat = File.stat(full_name)
|
100
|
+
if mode then
|
101
|
+
tested_mode = "%o" % stat.mode
|
102
|
+
res << :mode if tested_mode[-3..-1] != mode
|
103
|
+
end
|
104
|
+
if owner then
|
105
|
+
res << :owner if Etc.getpwuid(stat.uid).name != owner
|
106
|
+
end
|
107
|
+
if group then
|
108
|
+
res << :group if Etc.getgrgid(stat.gid).name != group
|
109
|
+
end
|
110
|
+
return res
|
111
|
+
end
|
112
|
+
|
113
|
+
# TCP/IP service checker
|
114
|
+
# @return [Bool] status
|
115
|
+
# @option [String] :name display name
|
116
|
+
# @option [String] :host hostname
|
117
|
+
# @option [String] :port TCP port
|
118
|
+
# @option [String] :url full URL, priority on :host and :port
|
119
|
+
def verify_service(name: nil, url: nil, host: nil, port: nil)
|
120
|
+
begin
|
121
|
+
if url then
|
122
|
+
uri = URI.parse(url)
|
123
|
+
host = uri.host
|
124
|
+
port = uri.port
|
125
|
+
end
|
126
|
+
Timeout::timeout(1) do
|
127
|
+
begin
|
128
|
+
s = TCPSocket.new(host, port)
|
129
|
+
s.close
|
130
|
+
return true
|
131
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
132
|
+
return false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
rescue Timeout::Error
|
136
|
+
return false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
#!@endgroup
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
|
2
|
+
module Carioca
|
3
|
+
module Services
|
4
|
+
|
5
|
+
class SecureStore
|
6
|
+
attr_accessor :data
|
7
|
+
|
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
|
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.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
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# base Carioca namespace
|
4
|
+
module Carioca
|
5
|
+
|
6
|
+
module Services
|
7
|
+
|
8
|
+
# Exiter namespace
|
9
|
+
class Setup
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
registry = Carioca::Registry.get
|
13
|
+
@output = registry.get_service name: :output
|
14
|
+
@i18n = registry.get_service name: :i18n
|
15
|
+
@toolbox = registry.get_service name: :toolbox
|
16
|
+
@configuration = registry.get_service name: :configuration
|
17
|
+
@finisher = registry.get_service name: :finisher
|
18
|
+
@schema = {}
|
19
|
+
if @configuration.settings.include? :setup then
|
20
|
+
@schema = (@configuration.settings.setup.include? :rules)? @configuration.settings.setup.rules : {}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def execute!
|
26
|
+
begin
|
27
|
+
@output.info @i18n.t('setup.execute.start')
|
28
|
+
@schema.each do |item|
|
29
|
+
action = item[:action] ; item.delete(:action)
|
30
|
+
self.send action, **item
|
31
|
+
end
|
32
|
+
rescue Exception
|
33
|
+
@finisher.secure_raise message: @i18n.t('setup.error'), error_case: :status_ko
|
34
|
+
end unless @schema.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
# @!group facilities for file system commands
|
41
|
+
|
42
|
+
# facility for file installation
|
43
|
+
# @option [String] :source file source path
|
44
|
+
# @option [String] :target file target path
|
45
|
+
# @option [String] :mode String for OCTAL rights (default "644")
|
46
|
+
# @option [String] :owner file owner for target (optional)
|
47
|
+
# @option [String] :group file group for target (optional)
|
48
|
+
# @option [Bool] :force to copy file in force (default true)
|
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)
|
53
|
+
full_target = File.expand_path(target)
|
54
|
+
source = (gem)? @toolbox.search_file_in_gem(gem_name,source) : source
|
55
|
+
FileUtils::copy source, full_target if force
|
56
|
+
FileUtils.chmod mode.to_i(8), full_target
|
57
|
+
FileUtils.chown owner, group, full_target if owner and group
|
58
|
+
end
|
59
|
+
|
60
|
+
# facility for folder creation
|
61
|
+
# @option [String] :path folder path (relative or absolute)
|
62
|
+
# @option [String] :mode String for OCTAL rights like "644"
|
63
|
+
# @option [String] :owner file owner for folder
|
64
|
+
# @option [String] :group file group for folder
|
65
|
+
def make_folder(path:, mode: "644", owner: nil, group: nil )
|
66
|
+
full_path = File.expand_path(path)
|
67
|
+
@output.item @i18n.t('setup.mkdir', path: full_path)
|
68
|
+
FileUtils::mkdir_p path unless File::exist? full_path
|
69
|
+
FileUtils.chmod mode.to_i(8), full_path
|
70
|
+
FileUtils.chown owner, group, full_path if owner and group
|
71
|
+
end
|
72
|
+
|
73
|
+
# facility for Symbolic link
|
74
|
+
# @option [String] :source path of the file
|
75
|
+
# @option [String] :link path of the symlink
|
76
|
+
def make_link(source:, link:)
|
77
|
+
full_source = File.expand_path(source)
|
78
|
+
full_link = File.expand_path(link)
|
79
|
+
@output.item @i18n.t('setup.ln', target: link, source: source)
|
80
|
+
FileUtils::rm link if (File::symlink? link and not File::exist? link)
|
81
|
+
FileUtils::ln_s source, link unless File::exist? link
|
82
|
+
end
|
83
|
+
# @!endgroup
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|