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.
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