rsync_config 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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