carioca 2.0.12 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.debride_withelist +49 -0
  3. data/.github/workflows/main.yml +7 -1
  4. data/Gemfile +1 -0
  5. data/Gemfile.lock +44 -4
  6. data/LICENSE.txt +21 -0
  7. data/README.md +651 -16
  8. data/Rakefile +15 -0
  9. data/VERSION +1 -1
  10. data/assets/images/carioca_output_emoji_colors.PNG +0 -0
  11. data/assets/images/carioca_output_emoji_no_colors.PNG +0 -0
  12. data/assets/images/carioca_output_no_emoji_colors.PNG +0 -0
  13. data/assets/images/carioca_output_no_emoji_no_colors.PNG +0 -0
  14. data/assets/images/description_carioca.png +0 -0
  15. data/assets/images/description_configuration_carioca.png +0 -0
  16. data/assets/images/description_container_carioca.png +0 -0
  17. data/assets/images/description_registry_carioca.png +0 -0
  18. data/assets/images/description_services_carioca.png +0 -0
  19. data/assets/images/logo_carioca_full_large.png +0 -0
  20. data/assets/images/logo_carioca_full_small.png +0 -0
  21. data/assets/images/logo_carioca_light_small.png +0 -0
  22. data/bom.xml +746 -0
  23. data/carioca.gemspec +3 -0
  24. data/config/locales/en.yml +34 -0
  25. data/config/locales/fr.yml +35 -1
  26. data/lib/carioca/configuration.rb +6 -2
  27. data/lib/carioca/constants.rb +38 -1
  28. data/lib/carioca/dependencies.rb +13 -0
  29. data/lib/carioca/services/config.rb +1 -1
  30. data/lib/carioca/services/finisher.rb +121 -0
  31. data/lib/carioca/services/sanitycheck.rb +143 -0
  32. data/lib/carioca/services/securestore.rb +81 -0
  33. data/lib/carioca/services/setup.rb +87 -0
  34. data/lib/carioca/services/toolbox.rb +104 -0
  35. data/samples/config/carioca.registry +0 -15
  36. data/samples/config/locales/en.yml +2 -1
  37. data/samples/config/settings.yml +29 -6
  38. data/samples/test.rb +111 -11
  39. 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
@@ -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}"
@@ -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, :supported_environment, :output_mode, :log_level, :output_target
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
- @supported_environment = DEFAULT_ENVIRONMENTS_LIST.dup
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|
@@ -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 serice of Carioca',
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',
@@ -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.supported_environment.each do |evt|
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