carioca 2.0.12 → 2.1.1
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/.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
|