rsync_config 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2eec2099c07ff331d789ae499f3487d9cbbf2371
4
+ data.tar.gz: d0af2a1154084f0fff7aab650f68cbdf0a0cff9c
5
+ SHA512:
6
+ metadata.gz: 44661fc4d9f091e2760e11a16e85219cb779b21c769728006e3b6fd7e74a7ab6c005db042aa8a3e04f61753c6b7617890173762b0b2423ed1e6e137c23556314
7
+ data.tar.gz: 8bb05a02d50ed6e99acfdefb4b873677cdc57a268f584e5d8df551fafb212b806ed7933cc8f78171f0adccaf25f7a40bcd91cc5ad03d58ecd5916b3f58e8a1ce
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rsync_config.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Guillaume Bodi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # RsyncConfig
2
+
3
+ This is a little utility gem to help manage the rsyncd config files on GNU/Linux systems.
4
+ It is still in infancy but is usable as is.
5
+
6
+ [![Code Climate](https://codeclimate.com/github/kuma-giyomu/rsync_config.png)](https://codeclimate.com/github/kuma-giyomu/rsync_config)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'rsync_config'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install rsync_config
21
+
22
+ ## Usage
23
+
24
+ You can load a config file using `RsyncConfig::load_config <file_path>` or create a config from scratch with `RsyncConfig::Config.new`.
25
+ Alternatively, you can use `RsyncConfig::parse <content>` if you prefer to provide the input from a random source.
26
+
27
+ ### Properties
28
+
29
+ The `RsyncConfig::Config` instance provides access to properties via the `#[]` and `#[]=` methods. So for instance
30
+
31
+ ```ruby
32
+ config = RsyncConfig::Config.new
33
+ config['uid'] = 'alice' # setter
34
+ puts config['uid'] # getter
35
+ ```
36
+
37
+ Note that the class accepts symbols as alternative `config[:uid]` is valid too.
38
+ But I felt it was best to keep as strings for the config options with spaces such as `secrets file`.
39
+
40
+ ### Modules
41
+
42
+ The `RsyncConfig::Config` instance provides access to modules via the `#module` method
43
+
44
+ ```ruby
45
+ config = RsyncConfig::Config.new
46
+ foo_module = config.module :foo
47
+ bar_module = config.module 'bar'
48
+ ```
49
+
50
+ Here again, symbols are converted to strings (rsync allows modules with spaces and whatnot)
51
+
52
+ Properties can be assigned to modules in the same way they are added to the `RsyncConfig::Config` object.
53
+
54
+ ```ruby
55
+ foo_module['uid'] = 'alice'
56
+ ```
57
+
58
+ `RsyncConfig::Module` instances do not have modules themselves.
59
+
60
+ ### Users
61
+
62
+ Both `RsyncConfig::Config` and `RsyncConfig::Module` instances can define a list of users that have access to the modules.
63
+ They are defined using the `#users` hash accessor method.
64
+ Additionally a test method `#user?` can be used to probe for a give user's existence.
65
+
66
+ ```ruby
67
+ config = RsyncConfig::Config.new
68
+ config.users = {'alice' => 'wonder'}
69
+ puts config.user?('alice') # true
70
+ config.users['bob'] = 'march'
71
+ ```
72
+
73
+ ### Writing to disk
74
+
75
+ Use `RsyncConfig::Config#write_to <main_output_file>`.
76
+ The gem assumes that you have write permissions on the file and a `RuntimeError` will be raised otherwise.
77
+ This method will automatically try to write the secrets files when defined.
78
+
79
+ For a raw String output, just call `RsyncConfig::Config#to_config_file` instead.
80
+ Note that this method will not return the content of the secrets file (not sure if this will be useful).
81
+
82
+ ### Limitations
83
+
84
+ - currently the gem does not process the directives `&include` and `&merge`.
85
+ - the values for the properties are not checked/validated in any way.
86
+ - the way secrets files are handled is simplistic since the files will be written/read multiple times if specified more than once
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork it
91
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 4. Push to the branch (`git push origin my-new-feature`)
94
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'lib'
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/test*.rb']
8
+ end
@@ -0,0 +1,112 @@
1
+ module RsyncConfig
2
+
3
+ class Config
4
+
5
+ include Propertiable
6
+ include UserManagement
7
+
8
+ # global properties
9
+ allow_properties :motd_file, :pid_file, :port, :address, :socket_options, :listen_backlog
10
+
11
+ # module properties
12
+ allow_properties :comment, :path, :use_chroot, :numeric_ids, :munge_symlinks, :charset
13
+ allow_properties :max_connections, :log_file, :syslog_facility, :max_verbosity, :lock_file
14
+ allow_properties :read_only, :write_only, :list, :uid, :gid, :fake_super, :filter, :exclude, :include
15
+ allow_properties :exclude_from, :include_from, :incoming_chmod, :outgoing_chmod, :auth_users
16
+ allow_properties :secrets_file, :strict_modes, :hosts_allow, :hosts_deny, :reverse_lookup
17
+ allow_properties :forward_lookup, :ignore_errors, :ignore_nonreadable, :transfer_logging
18
+ allow_properties :log_format, :timeout, :refuse_options, :dont_compress, 'pre-xfer exec', 'post-xfer exec'
19
+
20
+ def self.load_file(config_file)
21
+ raise 'File does not exist' unless File.exists? config_file
22
+ raise 'File is not readable' unless File.readable? config_file
23
+
24
+ content = nil
25
+
26
+ File.open(config_file, 'r') do |file|
27
+ content = file.read
28
+ end
29
+
30
+ Config.parse content
31
+ end
32
+
33
+ def self.parse(content)
34
+ raise 'Cannot process nil' if content.nil?
35
+
36
+ Treetop.load 'lib/rsync_config/parser/config_file'
37
+ parser = RsyncConfigFileParser.new
38
+ p = parser.parse content
39
+
40
+ unless p.nil?
41
+ return p.to_config.after_parse
42
+ else
43
+ raise RuntimeError, parser.failure_reason
44
+ end
45
+ end
46
+
47
+ def module(key)
48
+ raise ArgumentError if key.nil?
49
+ key = key.to_s if key.is_a? Symbol
50
+ key = key.strip
51
+ raise ArgumentError if key.length == 0
52
+
53
+ modules[key] ||= Module.new(self, key)
54
+
55
+ modules[key]
56
+ end
57
+
58
+ def to_s
59
+ to_config_file
60
+ end
61
+
62
+ def to_config_file
63
+ out = []
64
+ properties_to_s = properties_to_a.join("\n")
65
+ out << properties_to_s if properties_to_s.length > 0
66
+
67
+ modules.each do |key, mod|
68
+ out << mod.to_config_file
69
+ end
70
+
71
+ out.join "\n"
72
+ end
73
+
74
+ def write_to(file_path)
75
+ File.open(file_path, 'w') do |file|
76
+ file.write to_config_file
77
+ end
78
+
79
+ write_secrets_files
80
+ end
81
+
82
+ def module_names
83
+ modules.keys
84
+ end
85
+
86
+ def after_parse
87
+ batch_process_secrets_files :load_secrets_file
88
+ end
89
+
90
+ private
91
+
92
+ def modules
93
+ @modules ||= {}
94
+ end
95
+
96
+ def write_secrets_files
97
+ batch_process_secrets_files :write_secrets_file
98
+ end
99
+
100
+ def batch_process_secrets_files(method_name)
101
+ send method_name, self.[]('secrets file', true)
102
+
103
+ modules.each_value do |rmodule|
104
+ rmodule.send method_name, rmodule.[]('secrets file', true)
105
+ end
106
+
107
+ self
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,32 @@
1
+ module RsyncConfig
2
+
3
+ class Module
4
+
5
+ include Propertiable
6
+ include UserManagement
7
+
8
+ # module properties
9
+ allow_properties :comment, :path, :use_chroot, :numeric_ids, :munge_symlinks, :charset
10
+ allow_properties :max_connections, :log_file, :syslog_facility, :max_verbosity, :lock_file
11
+ allow_properties :read_only, :write_only, :list, :uid, :gid, :fake_super, :filter, :exclude, :include
12
+ allow_properties :exclude_from, :include_from, :incoming_chmod, :outgoing_chmod, :auth_users
13
+ allow_properties :secrets_file, :strict_modes, :hosts_allow, :hosts_deny, :reverse_lookup
14
+ allow_properties :forward_lookup, :ignore_errors, :ignore_nonreadable, :transfer_logging
15
+ allow_properties :log_format, :timeout, :refuse_options, :dont_compress, 'pre-xfer exec', 'post-xfer exec'
16
+
17
+ def initialize(parent_config, name)
18
+ @parent_config = parent_config
19
+ @name = name
20
+ end
21
+
22
+ def to_s
23
+ to_config_file
24
+ end
25
+
26
+ def to_config_file
27
+ (["[#{@name}]"] + properties_to_a.map {|p| " #{p}"}).join "\n"
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,98 @@
1
+ require 'rsync_config/config'
2
+
3
+ module RsyncConfig
4
+
5
+ grammar RsyncConfigFile
6
+
7
+ rule start
8
+ global_section? module_section? EOF
9
+ {
10
+ attr_accessor :config, :active_module
11
+
12
+ def to_config
13
+ @config = ::RsyncConfig::Config.new
14
+ @active_module = nil
15
+
16
+ crawl self
17
+
18
+ @config
19
+ end
20
+ }
21
+ end
22
+
23
+ rule global_section
24
+ ((comment / space? option?) EOL)+
25
+ end
26
+
27
+ rule module_section
28
+ (module_header module_body*)+
29
+ end
30
+
31
+ rule module_body
32
+ (comment / space? option?) EOL
33
+ end
34
+
35
+ rule option
36
+ property space? '=' space? value space?
37
+ {
38
+ def action(top_node)
39
+ receiver = top_node.active_module.nil? ? top_node.config : top_node.active_module
40
+ receiver[property.text_value] = value.text_value
41
+
42
+ # interrupt subtree crawling
43
+ false
44
+ end
45
+ }
46
+ end
47
+
48
+ rule property
49
+ [^=\n]+
50
+ end
51
+
52
+ rule value
53
+ [^\n]* ('\\' space? EOL value)?
54
+ {
55
+ def value
56
+ text_value.gsub(/\\\s*\n/, ' ')
57
+ end
58
+ }
59
+ end
60
+
61
+ rule module_header
62
+ '[' module_label:([^/\]]+) ']' space?
63
+ {
64
+ def value
65
+ module_label.text_value.gsub /\s+/, ' '
66
+ end
67
+
68
+ def action(top_node)
69
+ top_node.active_module = top_node.config.module(module_label.text_value)
70
+
71
+ # interrupt subtree crawling
72
+ false
73
+ end
74
+ }
75
+ end
76
+
77
+ rule comment
78
+ space? '#' [^"\n"]*
79
+ end
80
+
81
+ rule space
82
+ [ \t]+
83
+ end
84
+
85
+ rule EOL
86
+ "\n"
87
+ end
88
+
89
+ rule EOF
90
+ !.
91
+ end
92
+
93
+ rule EOS
94
+ EOL / EOF
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,28 @@
1
+ module RsyncConfig
2
+
3
+ module Node
4
+
5
+ module Crawlable
6
+
7
+ def crawl *args
8
+ continue = true
9
+ continue = action(*args) if respond_to? :action
10
+
11
+ return if !continue || elements.nil?
12
+
13
+ elements.each do |elt|
14
+ elt.crawl(*args)
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ class Treetop::Runtime::SyntaxNode
21
+
22
+ include ::RsyncConfig::Node::Crawlable
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,59 @@
1
+
2
+ module RsyncConfig
3
+
4
+ grammar RsyncSecretsFile
5
+
6
+ rule start
7
+ lines
8
+ {
9
+ def to_hash
10
+ users = {}
11
+ crawl users
12
+
13
+ users
14
+ end
15
+ }
16
+ end
17
+
18
+ rule lines
19
+ line? (EOL+ lines)?
20
+ end
21
+
22
+ rule line
23
+ comment
24
+ / user_record
25
+ / whitespace+
26
+ end
27
+
28
+ rule comment
29
+ '#' [^\n]*
30
+ end
31
+
32
+ rule user_record
33
+ user:([^:]+) ':' password:([^\t\r\n\ ]+)
34
+ {
35
+ def action users
36
+ users[user_value] = password_value
37
+ end
38
+
39
+ def user_value
40
+ user.text_value
41
+ end
42
+
43
+ def password_value
44
+ password.text_value
45
+ end
46
+ }
47
+ end
48
+
49
+ rule whitespace
50
+ [ \t\r\n]
51
+ end
52
+
53
+ rule EOL
54
+ "\r\n" / "\n"
55
+ end
56
+
57
+ end
58
+ end
59
+
@@ -0,0 +1 @@
1
+ require 'rsync_config/parser/node_extension'
@@ -0,0 +1,68 @@
1
+ module RsyncConfig
2
+
3
+ module Propertiable
4
+
5
+ def self.included base
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def allow_properties *properties
12
+ properties.each do |property|
13
+ property = property.to_s if property.is_a? Symbol
14
+ property = property.downcase.strip.gsub(/_/, ' ')
15
+ allowed_properties.push property
16
+ end
17
+ end
18
+
19
+ def allowed_properties
20
+ @allowed_properties ||= []
21
+ end
22
+
23
+ def allowed_property? property
24
+ allowed_properties.include? property
25
+ end
26
+
27
+ end
28
+
29
+ def [](key, local_only = false)
30
+ key = sanitize_key(key)
31
+ return nil if key.nil?
32
+
33
+ value = properties[key]
34
+ return @parent_config[key] if !local_only && value.nil? && @parent_config.respond_to?(:[])
35
+
36
+ value
37
+ end
38
+
39
+ def sanitize_key(key)
40
+ key = key.to_s unless key.is_a? String
41
+ key = key.strip.downcase
42
+ return nil if key.length == 0
43
+ return nil unless self.class.allowed_property? key
44
+ key
45
+ end
46
+
47
+ def []=(key, value)
48
+ key = sanitize_key(key)
49
+ if value.nil?
50
+ properties.delete key
51
+ else
52
+ properties[key] = value.to_s unless value.nil?
53
+ end
54
+ end
55
+
56
+ def properties_to_a
57
+ properties.map { |key, value| "#{key} = #{value}" }
58
+ end
59
+
60
+ private
61
+
62
+ def properties
63
+ @properties ||= {}
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,86 @@
1
+ module RsyncConfig
2
+
3
+ module UserManagement
4
+
5
+ def self.included base
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def parse_secrets_file(content)
12
+ raise 'Cannot process nil' if content.nil?
13
+
14
+ Treetop.load 'lib/rsync_config/parser/secrets_file'
15
+ parser = RsyncSecretsFileParser.new
16
+ p = parser.parse content
17
+
18
+ unless p.nil?
19
+ return p.to_hash
20
+ else
21
+ raise RuntimeError, parser.failure_reason
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ def users
28
+ return @parent_config.users if @parent_config && @users.nil?
29
+ local_users
30
+ end
31
+
32
+ def local_users
33
+ @users ||= {}
34
+ end
35
+
36
+ def user? identifier
37
+ users.include? identifier
38
+ end
39
+
40
+ def users= (users_list)
41
+ @users = users_list if users_list.is_a?(Hash) || users_list.nil?
42
+ end
43
+
44
+ def local_user_list
45
+ # we need to output only what is related to this specific node
46
+ local_users.keys.map do |user|
47
+ "#{user}:#{local_users[user]}"
48
+ end
49
+ end
50
+
51
+ def write_secrets_file(file)
52
+ return if file.nil?
53
+ raise "Cannot write secrets file #{file}" if File.exists?(file) && ! File.writable?(file)
54
+ raise "Cannot create secrets file #{file}" if !File.exist?(file) && ! ( Dir.exists?(File.dirname(file)) && File.writable?(File.dirname(file)))
55
+
56
+ File.open(file, 'w') do |file|
57
+ file.write local_user_list.join "\n"
58
+ end
59
+
60
+ correct_file_permissions file
61
+ end
62
+
63
+ def load_secrets_file(file)
64
+ # fails silently if no parameter is found OR the file does not exist on disk
65
+ return if file.nil? || !(File.exist? file)
66
+
67
+ # fails is the file exists but cannot be read, since it's suspicious
68
+ raise "Cannot load secrets file #{file}" unless File.readable?(file)
69
+
70
+ File.open(file, 'r') do |file|
71
+ self.users = self.class.parse_secrets_file(file.read)
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def correct_file_permissions(file)
78
+ if File.world_readable?(file) || File.world_writable?(file)
79
+ # reuse existing permissions but remove everything from world
80
+ File.chmod (File.stat(file).mode & 0770), file
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,3 @@
1
+ module RsyncConfig
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,20 @@
1
+ require 'polyglot'
2
+ require 'treetop'
3
+ require 'rsync_config/version'
4
+ require 'rsync_config/parser'
5
+ require 'rsync_config/propertiable'
6
+ require 'rsync_config/user_management'
7
+ require 'rsync_config/module'
8
+ require 'rsync_config/config'
9
+
10
+ module RsyncConfig
11
+
12
+ def self.load_file(config_file)
13
+ Config.load_file config_file
14
+ end
15
+
16
+ def self.parse(content)
17
+ Config.parse content
18
+ end
19
+
20
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rsync_config/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rsync_config"
8
+ spec.version = RsyncConfig::VERSION
9
+ spec.authors = ["Guillaume Bodi"]
10
+ spec.email = ["bodi.giyomu@gmail.com"]
11
+ spec.description = %q{Utility gem to manage rsyncd config files}
12
+ spec.summary = %q{Utility gem to manage rsyncd config files}
13
+ spec.homepage = ""
14
+ spec.license = "GPLv3"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "test-unit"
24
+
25
+ spec.add_dependency 'treetop', '~>1.4.14'
26
+ end
File without changes
@@ -0,0 +1,10 @@
1
+ uid = nobody
2
+ gid = nobody
3
+ use chroot = no
4
+ max connections = 4
5
+ syslog facility = local5
6
+ pid file = /run/rsyncd.pid
7
+
8
+ [ftp]
9
+ path = /srv/ftp
10
+ comment = ftp area
@@ -0,0 +1,12 @@
1
+ uid = nobody
2
+ gid = nobody
3
+ use chroot = no
4
+ max connections = 4
5
+ syslog facility = local5
6
+ pid file = /run/rsyncd.pid
7
+ secrets file = test/etc/secrets_top.conf
8
+
9
+ [ftp]
10
+ path = /srv/ftp
11
+ comment = ftp area
12
+ secrets file = test/etc/secrets_ftp.conf
@@ -0,0 +1,4 @@
1
+ john:lennon
2
+ paul:mccartney
3
+ georges:something
4
+ ringo:starr
@@ -0,0 +1,2 @@
1
+ john:lennon
2
+ bob:bidwell
@@ -0,0 +1,233 @@
1
+ require 'test-unit'
2
+ require 'rsync_config'
3
+
4
+ class RsyncConfigTest < Test::Unit::TestCase
5
+
6
+ TEST_INPUT_FILE = File.join(__dir__, 'etc', 'rsyncd.conf')
7
+
8
+ TEST_INPUT_FILE_WITH_SECRETS = File.join(__dir__, 'etc', 'rsyncd_with_secrets.conf')
9
+
10
+ TEST_OUTPUT_FILE = File.join(__dir__, 'etc', 'out', 'rsyncd.conf')
11
+
12
+ TEST_SECRETS_FILE = File.join(__dir__, 'etc', 'out', 'secrets.conf')
13
+
14
+ def teardown
15
+ File.delete TEST_OUTPUT_FILE if File.exists? TEST_OUTPUT_FILE
16
+ File.delete TEST_SECRETS_FILE if File.exists? TEST_SECRETS_FILE
17
+ end
18
+
19
+ def make_new_config
20
+ RsyncConfig::Config.new
21
+ end
22
+
23
+ def make_simple_config
24
+ RsyncConfig.load_file TEST_INPUT_FILE
25
+ end
26
+
27
+ def make_secrets_config
28
+ RsyncConfig.load_file TEST_INPUT_FILE_WITH_SECRETS
29
+ end
30
+
31
+ def test_load_missing_file
32
+ assert_raise RuntimeError, 'Did not throw an error for missing file' do
33
+ RsyncConfig.load_file 'idontexist.conf'
34
+ end
35
+ end
36
+
37
+ def test_load_file_does_not_crash
38
+ config = nil
39
+ assert_nothing_raised RuntimeError do
40
+ config = make_simple_config
41
+ end
42
+
43
+ assert_equal config.class, RsyncConfig::Config, 'Did not return a config object' end
44
+
45
+ def test_accessing_missing_properties_returns_nil
46
+ config = make_new_config
47
+
48
+ assert_nil config[:i_dont_exist]
49
+ end
50
+
51
+ def test_accessing_a_property_returns_a_string
52
+ config = make_new_config
53
+ config[:uid] = 'nobody'
54
+
55
+ assert_equal 'nobody', config[:uid]
56
+ end
57
+
58
+ def test_one_module_output
59
+ config = make_new_config
60
+ ftp_module = config.module :ftp
61
+ ftp_module[:path] = 'local'
62
+ expected = <<EOS
63
+ [ftp]
64
+ path = local
65
+ EOS
66
+
67
+ assert_equal expected.strip, config.to_config_file
68
+ end
69
+
70
+ def test_module_listing
71
+ config = make_new_config
72
+ config.module :ftp
73
+ config.module :smb
74
+ config.module :something
75
+
76
+ assert_equal ['ftp', 'smb', 'something'], config.module_names
77
+ end
78
+
79
+ def test_easy_accessor_complete
80
+ config = make_new_config
81
+ config[:uid] = 'nut'
82
+ config[:gid] = 'kiwi'
83
+ config[:uid] = config[:gid]
84
+
85
+ assert_equal 'kiwi', config[:uid]
86
+ assert_equal config[:gid], config[:uid]
87
+ end
88
+
89
+ def test_remove_property
90
+ config = make_new_config
91
+ config[:uid] = 'true'
92
+ config[:comment] = 'false'
93
+
94
+ # remove the comment config
95
+ config[:comment] = nil
96
+ expected = <<EOS
97
+ uid = true
98
+ EOS
99
+ assert_equal expected.strip, config.to_s
100
+ end
101
+
102
+ def test_module_inherits_config
103
+ config = make_new_config
104
+ config[:uid] = 'true'
105
+ ftp_module = config.module :ftp
106
+
107
+ assert_equal 'true', ftp_module[:uid]
108
+ end
109
+
110
+ def test_global_users_accessor_exists
111
+ config = make_new_config
112
+
113
+ assert_not_nil config.users
114
+ end
115
+
116
+ def test_global_users_assignment
117
+ config = make_new_config
118
+ config.users = {'john' => 'password'}
119
+
120
+ assert_true config.user?('john')
121
+ end
122
+
123
+ def test_global_user_existence
124
+ config = make_new_config
125
+ config.users = {'john' => 'password'}
126
+
127
+ assert_true config.user?('john')
128
+ end
129
+
130
+ def test_global_user_inexistence
131
+ config = make_new_config
132
+
133
+ assert_false config.user?('georges')
134
+ end
135
+
136
+ def test_module_user_existence
137
+ config = make_new_config
138
+ ftp_module = config.module :ftp
139
+ ftp_module.users = {'john' => 'password'}
140
+
141
+ assert_true ftp_module.user? 'john'
142
+ assert_false ftp_module.user? 'george'
143
+ end
144
+
145
+ def test_module_user_inexistence
146
+ config = make_new_config
147
+ ftp_module = config.module :ftp
148
+
149
+ assert_false ftp_module.user? 'george'
150
+ end
151
+
152
+ def test_module_user_inheritance
153
+ config = make_new_config
154
+ config.users = {'john' => 'password'}
155
+ ftp_module = config.module :ftp
156
+ ftp_module.users = {'george' => 'password'}
157
+
158
+ assert_false ftp_module.user? 'john'
159
+ assert_true ftp_module.user? 'george'
160
+ end
161
+
162
+ def test_write_to_file_succeeds
163
+ config = make_new_config
164
+ config.write_to TEST_OUTPUT_FILE
165
+
166
+ assert_true File.exists? TEST_OUTPUT_FILE
167
+ end
168
+
169
+ def test_write_to_file_has_correct_content
170
+ config = make_new_config
171
+ config[:uid] = 'test'
172
+
173
+ config.write_to TEST_OUTPUT_FILE
174
+ expected = <<EOL
175
+ uid = test
176
+ EOL
177
+
178
+ assert_equal expected.strip, File.read(TEST_OUTPUT_FILE).strip
179
+ end
180
+
181
+ def test_write_to_secrets_file_succeeds
182
+ config = make_simple_config
183
+ config['secrets file'] = TEST_SECRETS_FILE
184
+ config.users['john'] = 'doe'
185
+
186
+ config.write_to TEST_OUTPUT_FILE
187
+
188
+ assert_true File.exists? TEST_SECRETS_FILE
189
+ end
190
+
191
+ def test_write_to_secrets_file_has_correct_permissions
192
+ config = make_new_config
193
+ config['secrets file'] = TEST_SECRETS_FILE
194
+ config.users['john'] = 'doe'
195
+
196
+ config.write_to TEST_OUTPUT_FILE
197
+
198
+ assert_nil File.world_readable? TEST_SECRETS_FILE
199
+ assert_nil File.world_writable? TEST_SECRETS_FILE
200
+ end
201
+
202
+ def test_write_correct_content_to_secrets_file
203
+ config = make_new_config
204
+ config['secrets file'] = TEST_SECRETS_FILE
205
+ config.users['john'] = 'doe'
206
+
207
+ config.write_to TEST_OUTPUT_FILE
208
+
209
+ secrets_expected = <<EOL
210
+ john:doe
211
+ EOL
212
+ assert_equal secrets_expected.strip, File.read(TEST_SECRETS_FILE).strip
213
+ end
214
+
215
+ def test_module_secrets_user_exists
216
+ config = make_secrets_config
217
+
218
+ assert_true config.module(:ftp).user? 'john'
219
+ end
220
+
221
+ def test_global_secrets_user_exists
222
+ config = make_secrets_config
223
+
224
+ assert_true config.user? 'bob'
225
+ end
226
+
227
+ def test_module_overriden_secrets_does_not_include_global_users
228
+ config = make_secrets_config
229
+
230
+ assert_false config.module(:ftp).user? 'bob'
231
+ end
232
+
233
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rsync_config
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Guillaume Bodi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: treetop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.14
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.4.14
69
+ description: Utility gem to manage rsyncd config files
70
+ email:
71
+ - bodi.giyomu@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - lib/rsync_config.rb
82
+ - lib/rsync_config/config.rb
83
+ - lib/rsync_config/module.rb
84
+ - lib/rsync_config/parser.rb
85
+ - lib/rsync_config/parser/config_file.treetop
86
+ - lib/rsync_config/parser/node_extension.rb
87
+ - lib/rsync_config/parser/secrets_file.treetop
88
+ - lib/rsync_config/propertiable.rb
89
+ - lib/rsync_config/user_management.rb
90
+ - lib/rsync_config/version.rb
91
+ - rsync_config.gemspec
92
+ - test/etc/out/.keep
93
+ - test/etc/rsyncd.conf
94
+ - test/etc/rsyncd_with_secrets.conf
95
+ - test/etc/secrets_ftp.conf
96
+ - test/etc/secrets_top.conf
97
+ - test/test_rsync_config.rb
98
+ homepage: ''
99
+ licenses:
100
+ - GPLv3
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.0.5
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Utility gem to manage rsyncd config files
122
+ test_files:
123
+ - test/etc/out/.keep
124
+ - test/etc/rsyncd.conf
125
+ - test/etc/rsyncd_with_secrets.conf
126
+ - test/etc/secrets_ftp.conf
127
+ - test/etc/secrets_top.conf
128
+ - test/test_rsync_config.rb