httpd_configmap_generator 0.1.0

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 (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +14 -0
  5. data/Dockerfile +16 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +201 -0
  8. data/README-active-directory.md +38 -0
  9. data/README-ipa.md +41 -0
  10. data/README-saml.md +70 -0
  11. data/README.md +386 -0
  12. data/Rakefile +8 -0
  13. data/bin/httpd_configmap_generator +101 -0
  14. data/httpd_configmap_generator.gemspec +34 -0
  15. data/lib/httpd_configmap_generator.rb +29 -0
  16. data/lib/httpd_configmap_generator/active_directory.rb +114 -0
  17. data/lib/httpd_configmap_generator/base.rb +83 -0
  18. data/lib/httpd_configmap_generator/base/command.rb +29 -0
  19. data/lib/httpd_configmap_generator/base/config.rb +13 -0
  20. data/lib/httpd_configmap_generator/base/config_map.rb +183 -0
  21. data/lib/httpd_configmap_generator/base/file.rb +66 -0
  22. data/lib/httpd_configmap_generator/base/kerberos.rb +13 -0
  23. data/lib/httpd_configmap_generator/base/network.rb +37 -0
  24. data/lib/httpd_configmap_generator/base/pam.rb +9 -0
  25. data/lib/httpd_configmap_generator/base/principal.rb +33 -0
  26. data/lib/httpd_configmap_generator/base/sssd.rb +51 -0
  27. data/lib/httpd_configmap_generator/export.rb +31 -0
  28. data/lib/httpd_configmap_generator/ipa.rb +122 -0
  29. data/lib/httpd_configmap_generator/options.rb +13 -0
  30. data/lib/httpd_configmap_generator/saml.rb +104 -0
  31. data/lib/httpd_configmap_generator/update.rb +39 -0
  32. data/lib/httpd_configmap_generator/version.rb +3 -0
  33. data/templates/etc/pam.d/httpd-auth +2 -0
  34. data/templates/httpd-configmap-generator-template.yaml +113 -0
  35. metadata +203 -0
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :test => :spec
7
+ task :default => :spec
8
+
@@ -0,0 +1,101 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Script for configuring container with external authentication
4
+ #
5
+ # Stdout: standard processing messages ...
6
+ # Stderr: any errors during configuration ...
7
+ # -o filename: for the generated auth-config map.
8
+ #
9
+
10
+ require "bundler/setup"
11
+ require "trollop"
12
+ require "httpd_configmap_generator"
13
+
14
+ CMD = File.basename($PROGRAM_NAME)
15
+
16
+ def error_msg(msg)
17
+ $stderr.puts msg
18
+ exit 1
19
+ end
20
+
21
+ module HttpdConfigmapGenerator
22
+ class Cli
23
+ def parse_arguments(auth_config)
24
+ opts = Trollop.options do
25
+ version("#{CMD} #{HttpdConfigmapGenerator::VERSION} - External Authentication Configuration script")
26
+ banner <<-EOS
27
+ #{version}
28
+
29
+ Usage: #{CMD} auth_type | update | export [--help | options]
30
+
31
+ #{CMD} options are:
32
+ EOS
33
+ opt :version, "Version of the #{CMD} command",
34
+ :default => false, :short => "-V"
35
+ auth_config.required_options.each do |key, key_options|
36
+ opt key, key_options[:description], HttpdConfigmapGenerator::Cli.options_for(key_options, true)
37
+ end
38
+ auth_config.optional_options.each do |key, key_options|
39
+ opt key, key_options[:description], HttpdConfigmapGenerator::Cli.options_for(key_options)
40
+ end
41
+ end
42
+ opts
43
+ end
44
+
45
+ def run_configure(auth_type)
46
+ begin
47
+ auth_config = HttpdConfigmapGenerator.new_config(auth_type)
48
+ rescue => err
49
+ error_msg(err.to_s)
50
+ end
51
+
52
+ auth_config.run_configure(parse_arguments(auth_config))
53
+ end
54
+
55
+ def run_update
56
+ begin
57
+ auth_config = HttpdConfigmapGenerator::Update.new
58
+ rescue => err
59
+ error_msg(err.to_s)
60
+ end
61
+
62
+ auth_config.update(parse_arguments(auth_config))
63
+ end
64
+
65
+ def run_export
66
+ begin
67
+ auth_config = HttpdConfigmapGenerator::Export.new
68
+ rescue => err
69
+ error_msg(err.to_s)
70
+ end
71
+
72
+ auth_config.export(parse_arguments(auth_config))
73
+ end
74
+
75
+ def self.options_for(key_options, required = false)
76
+ options = {}
77
+ options[:default] = key_options.key?(:default) ? key_options[:default] : ""
78
+ options[:required] = true if required
79
+ options[:short] = key_options[:short] if key_options[:short]
80
+ options[:multi] = key_options[:multi] if key_options[:multi]
81
+ options
82
+ end
83
+ end
84
+ end
85
+
86
+ if ARGV.empty?
87
+ error_msg("
88
+ Usage: #{CMD} auth_type | update | export [--help | options]
89
+ Supported auth_type: #{HttpdConfigmapGenerator.supported_auth_types.join(', ')}
90
+ ")
91
+ else
92
+ auth_type = ARGV.shift
93
+ case auth_type
94
+ when "update"
95
+ HttpdConfigmapGenerator::Cli.new.run_update
96
+ when "export"
97
+ HttpdConfigmapGenerator::Cli.new.run_export
98
+ else
99
+ HttpdConfigmapGenerator::Cli.new.run_configure(auth_type)
100
+ end
101
+ end
@@ -0,0 +1,34 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ # Declare Gem's Version
5
+ require "httpd_configmap_generator/version"
6
+
7
+ # Declare Dependencies
8
+ Gem::Specification.new do |s|
9
+ s.name = "httpd_configmap_generator"
10
+ s.version = HttpdConfigmapGenerator::VERSION
11
+ s.authors = ["Httpd Auth Config Developers"]
12
+ s.homepage = "https://github.com/ManageIQ/httpd_configmap_generator"
13
+ s.summary = "The Httpd Configmap Generator"
14
+ s.description = "The Httpd Configmap Generator"
15
+ s.licenses = ["Apache-2.0"]
16
+
17
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ s.bindir = "bin"
21
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_development_dependency "codeclimate-test-reporter", "~> 1.0.0"
25
+ s.add_development_dependency "rspec", "~> 3.0"
26
+ s.add_development_dependency "rake"
27
+ s.add_development_dependency "simplecov"
28
+
29
+ s.add_dependency "activesupport", ">=5.0"
30
+ s.add_dependency "awesome_spawn", "~> 1.4"
31
+ s.add_dependency "iniparse", "~> 1.4"
32
+ s.add_dependency "more_core_extensions", "~> 3.4"
33
+ s.add_dependency "trollop", "~> 2.1"
34
+ end
@@ -0,0 +1,29 @@
1
+ require "httpd_configmap_generator/version"
2
+ require "httpd_configmap_generator/base"
3
+ require "httpd_configmap_generator/active_directory"
4
+ require "httpd_configmap_generator/ipa"
5
+ require "httpd_configmap_generator/saml"
6
+ require "httpd_configmap_generator/update"
7
+ require "httpd_configmap_generator/export"
8
+ require "more_core_extensions/core_ext/hash"
9
+
10
+ module HttpdConfigmapGenerator
11
+ def self.new_config(auth_type)
12
+ auth_class(auth_type).new
13
+ end
14
+
15
+ def self.supported_auth_types
16
+ constants.collect do |c|
17
+ k = const_get(c)
18
+ k::AUTH[:subtype] if k.kind_of?(Class) && k.constants.include?(:AUTH)
19
+ end.compact
20
+ end
21
+
22
+ def self.auth_class(auth_type)
23
+ require "active_support/core_ext/string" # for camelize
24
+
25
+ auth_type = auth_type.tr('-', '_').camelize
26
+ raise "Invalid Authentication Type #{auth_type} specified" unless const_defined?(auth_type, false)
27
+ const_get(auth_type, false)
28
+ end
29
+ end
@@ -0,0 +1,114 @@
1
+ module HttpdConfigmapGenerator
2
+ class ActiveDirectory < Base
3
+ REALM_COMMAND = "/usr/sbin/realm".freeze
4
+ KERBEROS_KEYTAB_FILE = "/etc/krb5.keytab".freeze
5
+ AUTH = {
6
+ :type => "active-directory",
7
+ :subtype => "active-directory"
8
+ }.freeze
9
+
10
+ def required_options
11
+ super.merge(
12
+ :ad_domain => { :description => "Active Directory Domain" },
13
+ :ad_user => { :description => "Active Directory User" },
14
+ :ad_password => { :description => "Active Directory Password" }
15
+ )
16
+ end
17
+
18
+ def optional_options
19
+ super.merge(
20
+ :ad_realm => { :description => "Active Directory Realm" },
21
+ :ad_server => { :description => "Active Directory Server" }
22
+ )
23
+ end
24
+
25
+ def persistent_files
26
+ %w(
27
+ /etc/krb5.keytab
28
+ /etc/krb5.conf
29
+ /etc/nsswitch.conf
30
+ /etc/openldap/ldap.conf
31
+ /etc/pam.d/fingerprint-auth-ac
32
+ /etc/pam.d/httpd-auth
33
+ /etc/pam.d/password-auth-ac
34
+ /etc/pam.d/postlogin-ac
35
+ /etc/pam.d/smartcard-auth-ac
36
+ /etc/pam.d/system-auth-ac
37
+ /etc/resolv.conf
38
+ /etc/sssd/sssd.conf
39
+ /etc/sysconfig/authconfig
40
+ /etc/sysconfig/network
41
+ )
42
+ end
43
+
44
+ def configure(opts)
45
+ update_hostname(opts[:host])
46
+ join_ad_realm
47
+ realm_permit_all
48
+ configure_pam
49
+ configure_sssd
50
+ update_kerberos_keytab_permissions
51
+ enable_kerberos_dns_lookups
52
+ config_map = ConfigMap.new(opts)
53
+ config_map.generate(AUTH[:type], realm, persistent_files)
54
+ config_map.save(opts[:output])
55
+ rescue => err
56
+ log_command_error(err)
57
+ raise err
58
+ end
59
+
60
+ def configured?
61
+ File.exist?(SSSD_CONFIG)
62
+ end
63
+
64
+ def unconfigure
65
+ return unless configured?
66
+ end
67
+
68
+ def realm
69
+ @realm ||= opts[:ad_realm] if opts[:ad_realm].present?
70
+ @realm ||= domain
71
+ @realm ||= super
72
+ @realm = @realm.upcase
73
+ end
74
+
75
+ def domain
76
+ @domain ||= opts[:ad_domain] if opts[:ad_domain].present?
77
+ @domain ||= super
78
+ @domain
79
+ end
80
+
81
+ private
82
+
83
+ def configure_sssd
84
+ info_msg("Configuring SSSD Service")
85
+ sssd = Sssd.new(opts)
86
+ sssd.load(SSSD_CONFIG)
87
+ sssd.configure_domain(domain)
88
+ sssd.section("domain/#{domain}")["ad_server"] = opts[:ad_server] if opts[:ad_server].present?
89
+ sssd.section("sssd")["domains"] = domain
90
+ sssd.section("sssd")["default_domain_suffix"] = domain
91
+ sssd.add_service("pam")
92
+ sssd.configure_ifp
93
+ debug_msg("- Creating #{SSSD_CONFIG}")
94
+ sssd.save(SSSD_CONFIG)
95
+ end
96
+
97
+ def join_ad_realm
98
+ info_msg("Joining the AD Realm ...")
99
+ debug_msg(" - realm join #{realm} ...")
100
+ command_run!(REALM_COMMAND, :params => ["join", domain, "-U", opts[:ad_user]], :stdin_data => opts[:ad_password])
101
+ end
102
+
103
+ def realm_permit_all
104
+ info_msg("Allowing AD Users to Login ...")
105
+ command_run!(REALM_COMMAND, :params => ["permit", "--all"])
106
+ end
107
+
108
+ def update_kerberos_keytab_permissions
109
+ info_msg("Updating Kerberos keytab permissions ...")
110
+ FileUtils.chown("apache", "root", KERBEROS_KEYTAB_FILE)
111
+ FileUtils.chmod(0o640, KERBEROS_KEYTAB_FILE)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,83 @@
1
+ require "pathname"
2
+ require "httpd_configmap_generator/base/command"
3
+ require "httpd_configmap_generator/base/config"
4
+ require "httpd_configmap_generator/base/config_map"
5
+ require "httpd_configmap_generator/base/file"
6
+ require "httpd_configmap_generator/base/kerberos"
7
+ require "httpd_configmap_generator/base/network"
8
+ require "httpd_configmap_generator/base/pam"
9
+ require "httpd_configmap_generator/base/principal"
10
+ require "httpd_configmap_generator/base/sssd"
11
+
12
+ module HttpdConfigmapGenerator
13
+ class Base
14
+ APACHE_USER = "apache".freeze
15
+ HTTP_KEYTAB = "/etc/http.keytab".freeze
16
+ IPA_COMMAND = "/usr/bin/ipa".freeze
17
+ KERBEROS_CONFIG_FILE = "/etc/krb5.conf".freeze
18
+ LDAP_ATTRS = {
19
+ "mail" => "REMOTE_USER_EMAIL",
20
+ "givenname" => "REMOTE_USER_FIRSTNAME",
21
+ "sn" => "REMOTE_USER_LASTNAME",
22
+ "displayname" => "REMOTE_USER_FULLNAME",
23
+ "domainname" => "REMOTE_USER_DOMAIN"
24
+ }.freeze
25
+ PAM_CONFIG = "/etc/pam.d/httpd-auth".freeze
26
+ SSSD_CONFIG = "/etc/sssd/sssd.conf".freeze
27
+ TIMESTAMP_FORMAT = "%Y%m%d_%H%M%S".freeze
28
+
29
+ attr_accessor :opts
30
+
31
+ def initialize(opts = {})
32
+ @opts = opts
33
+ @realm = @domain = nil
34
+ end
35
+
36
+ def err_msg(msg)
37
+ STDERR.puts(msg)
38
+ end
39
+
40
+ def info_msg(msg)
41
+ STDOUT.puts(msg)
42
+ end
43
+
44
+ def debug_msg(msg)
45
+ STDOUT.puts(msg) if opts[:debug]
46
+ end
47
+
48
+ def required_options
49
+ {
50
+ :host => { :description => "Application Domain",
51
+ :short => "-h" },
52
+ :output => { :description => "Configuration map file to create",
53
+ :short => "-o" }
54
+ }
55
+ end
56
+
57
+ def optional_options
58
+ {
59
+ :force => { :description => "Force configuration if configured already",
60
+ :short => "-f",
61
+ :default => false },
62
+ :debug => { :description => "Enable debugging",
63
+ :short => "-d",
64
+ :default => false }
65
+ }
66
+ end
67
+
68
+ def run_configure(opts)
69
+ validate_options(opts)
70
+ @opts = opts
71
+ unconfigure if configured? && opts[:force]
72
+ raise "#{self.class.name} Already Configured" if configured?
73
+ unless ENV["HTTPD_AUTH_TYPE"]
74
+ raise "Not running in httpd_configmap_generator container - Skipping #{self.class.name} configuration"
75
+ end
76
+ configure(opts)
77
+ end
78
+
79
+ def validate_options(_options)
80
+ nil
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ require "awesome_spawn"
2
+
3
+ module HttpdConfigmapGenerator
4
+ class Base
5
+ def command_run(executable, options = {})
6
+ if opts && opts[:debug]
7
+ debug_msg("Running Command: #{AwesomeSpawn.build_command_line(executable, options)}")
8
+ end
9
+ AwesomeSpawn.run(executable, options)
10
+ end
11
+
12
+ def command_run!(executable, options = {})
13
+ if opts && opts[:debug]
14
+ debug_msg("Running Command: #{AwesomeSpawn.build_command_line(executable, options)}")
15
+ end
16
+ AwesomeSpawn.run!(executable, options)
17
+ end
18
+
19
+ def log_command_error(err)
20
+ err_msg("Command Error: #{err}")
21
+ if err.kind_of?(AwesomeSpawn::CommandResultError)
22
+ err_msg("stdout: #{err.result.output}")
23
+ err_msg("stderr: #{err.result.error}")
24
+ else
25
+ err_msg(err.backtrace)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ require "active_support"
2
+ require "active_support/core_ext" # for Time.current
3
+
4
+ module HttpdConfigmapGenerator
5
+ class Base
6
+ def config_file_backup(path)
7
+ if File.exist?(path)
8
+ timestamp = Time.current.strftime(TIMESTAMP_FORMAT)
9
+ FileUtils.copy(path, "#{path}.#{timestamp}")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,183 @@
1
+ require "base64"
2
+ require "yaml"
3
+ require "uri"
4
+ require "etc"
5
+
6
+ module HttpdConfigmapGenerator
7
+ class ConfigMap < Base
8
+ DATA_SECTION = "data".freeze
9
+ AUTH_CONFIGURATION = "auth-configuration.conf".freeze
10
+
11
+ attr_accessor :config_map
12
+ attr_accessor :opts
13
+
14
+ def initialize(opts = {})
15
+ @opts = opts
16
+ @config_map = template
17
+ end
18
+
19
+ def generate(auth_type, realm, file_list)
20
+ info_msg("Generating Auth Config-Map for #{auth_type}")
21
+ @config_map = template(auth_type, realm)
22
+ file_specs = gen_filespecs(file_list)
23
+ define_configuration(file_specs)
24
+ include_files(file_specs)
25
+ end
26
+
27
+ def load(file_path)
28
+ @config_map = File.exist?(file_path) ? YAML.load_file(file_path) : {}
29
+ end
30
+
31
+ def save(file_path)
32
+ delete_target_file(file_path)
33
+ info_msg("Saving Auth Config-Map to #{file_path}")
34
+ File.open(file_path, "w") { |f| f.write(config_map.to_yaml) }
35
+ end
36
+
37
+ def add_files(file_list)
38
+ return unless file_list
39
+ file_specs = gen_filespecs_for_files_to_add(file_list)
40
+ update_configuration(file_specs)
41
+ include_files(file_specs)
42
+ end
43
+
44
+ def export_file(file_entry, output_file)
45
+ basename, _target_file, _mode = search_file_entry(file_entry)
46
+ raise "File #{file_entry} does not exist in the configuration map" unless basename
47
+ delete_target_file(output_file)
48
+ create_target_directory(output_file)
49
+ debug_msg("Exporting #{file_entry} to #{output_file} ...")
50
+ content = config_map.fetch_path(DATA_SECTION, basename)
51
+ content = Base64.decode64(content) if basename =~ /^.*\.base64$/
52
+ File.write(output_file, content)
53
+ end
54
+
55
+ private
56
+
57
+ def template(auth_type = "internal", kerberos_realms = "undefined")
58
+ {
59
+ DATA_SECTION => {
60
+ "auth-type" => auth_type,
61
+ "auth-kerberos-realms" => kerberos_realms
62
+ },
63
+ "kind" => "ConfigMap",
64
+ "metadata" => {
65
+ "name" => "httpd-auth-configs"
66
+ }
67
+ }
68
+ end
69
+
70
+ def gen_filespecs(file_list)
71
+ file_specs = []
72
+ file_list.each do |file|
73
+ file_specs << file_entry_spec(file.strip)
74
+ end
75
+ file_specs.sort_by { |file_spec| file_spec[:basename] }
76
+ end
77
+
78
+ # Supporting the following signatures:
79
+ # /path/of/real/file
80
+ # /path/of/source/file,/path/of/real/file
81
+ # /path/of/source/file,/path/of/real/file,mode
82
+ # http://url_source,/path/of/real/file,mode
83
+ def gen_filespecs_for_files_to_add(file_list)
84
+ file_specs = []
85
+ file_list.each do |file_to_add|
86
+ file_spec = file_to_add.split(",").map(&:strip)
87
+ case file_spec.length
88
+ when 1
89
+ file = file_spec.first
90
+ file_entry = file_entry_spec(file)
91
+ when 2
92
+ source_file, target_file = file_spec
93
+ raise "Must specify a mode for URL file sources" if source_file =~ URI.regexp(%w(http https))
94
+ file_entry = file_entry_spec(source_file, target_file)
95
+ when 3
96
+ source_file, target_file, mode = file_spec
97
+ if source_file =~ URI.regexp(%w(http https))
98
+ fetch_network_file(source_file, target_file)
99
+ file_entry = file_entry_spec(target_file, target_file, mode)
100
+ else
101
+ file_entry = file_entry_spec(source_file, target_file, mode)
102
+ end
103
+ else
104
+ raise "Invalid file specification #{file_to_add}"
105
+ end
106
+ file_specs << file_entry
107
+ end
108
+ file_specs.sort_by { |file_spec| file_spec[:basename] }
109
+ end
110
+
111
+ def file_entry_spec(source_file, target_file = nil, mode = nil)
112
+ target_file = source_file.dup unless target_file
113
+ unless mode
114
+ stat = File.stat(source_file)
115
+ file_owner = Etc.getpwuid(stat.uid).name
116
+ file_group = Etc.getgrgid(stat.gid).name
117
+ end
118
+ {
119
+ :basename => File.basename(target_file).dup,
120
+ :binary => file_binary?(source_file),
121
+ :target => target_file,
122
+ :mode => mode ? mode : "%4o:%s:%s" % [stat.mode & 0o7777, file_owner, file_group]
123
+ }
124
+ end
125
+
126
+ def update_configuration(file_specs)
127
+ auth_configuration = fetch_auth_configuration
128
+ return define_configuration(file_specs) unless auth_configuration
129
+ # first, remove any file_specs references in the file list, we don't want duplication here.
130
+ auth_configuration = auth_configuration.split("\n")
131
+ file_specs.each do |file_spec|
132
+ entry = auth_configuration.select { |line| line =~ file_entry_regex(file_spec[:target]) }
133
+ auth_configuration -= entry if entry
134
+ end
135
+ auth_configuration = auth_configuration.join("\n") + "\n"
136
+ # now, append any of the new file_specs at the end of the list.
137
+ append_configuration(auth_configuration, file_specs)
138
+ end
139
+
140
+ def search_file_entry(target_file)
141
+ auth_configuration = fetch_auth_configuration
142
+ return nil unless auth_configuration
143
+ auth_configuration = auth_configuration.split("\n")
144
+ entry = auth_configuration.select { |line| line =~ file_entry_regex(target_file) }
145
+ entry ? entry.first.split('=')[1].strip.split(' ') : nil
146
+ end
147
+
148
+ def define_configuration(file_specs)
149
+ auth_configuration = "# External Authentication Configuration File\n#\n"
150
+ append_configuration(auth_configuration, file_specs)
151
+ end
152
+
153
+ def include_files(file_specs)
154
+ file_specs.each do |file_spec|
155
+ content = File.read(file_spec[:target])
156
+ content = Base64.encode64(content) if file_spec[:binary]
157
+ # encode(:universal_newline => true) will convert \r\n to \n, necessary for to_yaml to render properly.
158
+ config_map[DATA_SECTION].merge!(file_basename(file_spec) => content.encode(:universal_newline => true))
159
+ end
160
+ end
161
+
162
+ def file_basename(file_spec)
163
+ file_spec[:binary] ? "#{file_spec[:basename]}.base64" : file_spec[:basename]
164
+ end
165
+
166
+ def append_configuration(auth_configuration, file_specs)
167
+ file_specs.each do |file_spec|
168
+ debug_msg("Adding file #{file_spec[:target]} ...")
169
+ auth_configuration += "file = #{file_basename(file_spec)} #{file_spec[:target]} #{file_spec[:mode]}\n"
170
+ end
171
+ config_map[DATA_SECTION] ||= {}
172
+ config_map[DATA_SECTION].merge!(AUTH_CONFIGURATION => auth_configuration)
173
+ end
174
+
175
+ def fetch_auth_configuration
176
+ config_map.fetch_path(DATA_SECTION, AUTH_CONFIGURATION)
177
+ end
178
+
179
+ def file_entry_regex(target_file)
180
+ /^file = .* #{target_file} .*$/
181
+ end
182
+ end
183
+ end