vidibus-pureftpd 0.1.0 → 1.0.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.
data/README.md CHANGED
@@ -1,43 +1,69 @@
1
1
  # Vidibus::Pureftpd
2
2
 
3
- Allows control of [Pure-FTPd](http://www.pureftpd.org/project/pure-ftpd), the free, secure, production-quality and standard-conformant FTP server.
3
+ Provides an ActiveModel-based abstraction of Pure-FTPd's [virtual users](http://download.pureftpd.org/pub/pure-ftpd/doc/README.Virtual-Users). [Pure-FTPd](http://www.pureftpd.org/project/pure-ftpd) is a free, secure, production-quality and standard-conformant FTP server.
4
4
 
5
- This gem is part of [Vidibus](http://vidibus.org), an open source toolset for building distributed (video) applications.
6
-
7
- ## Installation
8
-
9
- Add the dependency to the Gemfile of your application: `gem 'vidibus-pureftpd'`. Then call bundle install on your console.
5
+ This gem is part of [Vidibus](http://vidibus.org), an open source toolset for building distributed (video) applications. It has been tested with Ruby 1.8.7 and 1.9.3.
10
6
 
11
7
 
12
8
  ## Usage
13
9
 
14
- Add a user:
10
+ Basic CRUD operations are available for `Vidibus::Pureftpd::User`:
15
11
 
16
- ```
17
- Vidibus::Pureftpd.add_user({
18
- :login => 'someuser',
12
+ ```ruby
13
+ user = Vidibus::Pureftpd::User.new(
14
+ :login => 'my_user',
19
15
  :password => 'verysecret',
20
- :directory => '/tmp'
21
- })
22
- ```
16
+ :directory => '/home/my_user'
17
+ )
18
+ # => returns a new user instance
23
19
 
24
- Delete a user:
20
+ user = Vidibus::Pureftpd::User.create(
21
+ :login => 'my_user',
22
+ :password => 'verysecret',
23
+ :directory => '/home/my_user'
24
+ )
25
+ # => creates a new user and returns the instance
25
26
 
27
+ user = Vidibus::Pureftpd::User.find_by_login('my_user')
28
+ # => reads user from database and returns an user instance
29
+
30
+ user.save
31
+ # => saves user to database and returns true
32
+
33
+ user.destroy
34
+ # => removes user from database
26
35
  ```
27
- Vidibus::Pureftpd.delete_user(:login => 'someuser')
28
- ```
29
36
 
30
- Change a user's password:
37
+ Additionally, some methods are provided for your convenience:
38
+
39
+ ```ruby
40
+ user.valid?
41
+ # => returns true if user is valid
42
+
43
+ user.persisted?
44
+ # => returns true if user has been saved to database
31
45
 
46
+ user.reload
47
+ # => reloads user from database
32
48
  ```
33
- Vidibus::Pureftpd.change_password({
34
- :login => 'someuser',
35
- :password => 'whatever'
36
- })
49
+
50
+ By default, `Vidibus::Pureftpd` will use these settings which you may override:
51
+
52
+ ```ruby
53
+ Vidibus::Pureftpd.settings[:sysuser] # => 'pureftpd_user'
54
+ Vidibus::Pureftpd.settings[:sysgroup] # => 'pureftpd_group'
55
+ Vidibus::Pureftpd.settings[:password_file] # => '/etc/pure-ftpd/pureftpd.passwd'
37
56
  ```
38
57
 
39
58
 
40
- ## Install Pure-FTPd on Debian Lenny
59
+ ## Installation
60
+
61
+ Add the dependency to the Gemfile of your application: `gem 'vidibus-pureftpd'`. Then call bundle install on your console.
62
+
63
+ Installation of the Pure-FTPd server itself is quite simple:
64
+
65
+
66
+ ### Install Pure-FTPd on Debian Lenny
41
67
 
42
68
  Get the package:
43
69
 
@@ -112,13 +138,13 @@ Finally, (re)start Pure-FTPd:
112
138
  For more instructions, please [check this resource](http://linux.justinhartman.com/PureFTPd_Installation_and_Setup).
113
139
 
114
140
 
115
- ## Install Pure-FTPd on OSX for testing
141
+ ### Install Pure-FTPd on OSX (required for testing this gem)
116
142
 
117
143
  ```
118
144
  brew install pure-ftpd
119
145
  ```
120
146
 
121
- Create the user `pureftpd_user` with ID 483:
147
+ In order to perform the tests, a certain user is required. Create the user `pureftpd_user` with ID 483:
122
148
 
123
149
  ```
124
150
  sudo dscl . create /Users/pureftpd_user uid 483
@@ -127,7 +153,7 @@ sudo dscl . create /Users/pureftpd_user UserShell /etc/pure-ftpd
127
153
  sudo dscl . create /Users/pureftpd_user NFSHomeDirectory /dev/null
128
154
  ```
129
155
 
130
- Create the group `pureftpd_group` with ID 483:
156
+ Then create the group `pureftpd_group`, also with ID 483:
131
157
 
132
158
  ```
133
159
  sudo dscl . create /Groups/pureftpd_group gid 483
@@ -141,21 +167,14 @@ dscacheutil -q user
141
167
  dscacheutil -q group
142
168
  ```
143
169
 
144
- Ensure that the database exists and is writable for the user that executes RSpec:
145
-
146
- ```
147
- sudo mkdir /etc/pure-ftpd/
148
- sudo touch /etc/pure-ftpd/pureftpd.passwd
149
- sudo chown -R `whoami` /etc/pure-ftpd/
150
- ```
151
170
 
152
- To start the server (not needed for testing), type:
171
+ Not needed for testing, but to start the server, type:
153
172
 
154
173
  ```
155
174
  sudo /usr/local/sbin/pure-ftpd &
156
175
  ```
157
176
 
158
- You should be able to connect via ftp:
177
+ You should now be able to connect via ftp:
159
178
 
160
179
  ```
161
180
  ftp localhost
@@ -164,7 +183,27 @@ ftp localhost
164
183
  Shut it down with:
165
184
 
166
185
  ```
167
- pkill pure-ftpd
186
+ sudo pkill pure-ftpd
187
+ ```
188
+
189
+
190
+ ## TODO
191
+
192
+ Implement all user options offered by Pure-FTPd:
193
+
194
+ ```
195
+ -t <download bandwidth>
196
+ -T <upload bandwidth>
197
+ -n <max number of files>
198
+ -N <max Mbytes>
199
+ -q <upload ratio>
200
+ -Q <download ratio>
201
+ -r <allow client host>
202
+ -R <deny client host>
203
+ -i <allow local host>
204
+ -I <deny local host>
205
+ -y <max number of concurrent sessions>
206
+ -z <hhmm>-<hhmm>
168
207
  ```
169
208
 
170
209
 
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rdoc/task'
5
5
  require 'rspec'
6
6
  require 'rspec/core/rake_task'
7
7
 
8
- require 'vidibus/pureftpd'
8
+ require 'vidibus/pureftpd/version'
9
9
 
10
10
  Bundler::GemHelper.install_tasks
11
11
 
@@ -0,0 +1,8 @@
1
+ de:
2
+ errors:
3
+ messages:
4
+ taken: 'ist bereits vergeben'
5
+ not_existent: 'gibt es nicht'
6
+ not_a_directory: 'muss ein Verzeichnis sein'
7
+ not_readable: 'muss lesbar sein'
8
+ not_writable: 'muss beschreibbar sein'
@@ -0,0 +1,8 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ taken: 'has already been taken'
5
+ not_existent: 'does not exist'
6
+ not_a_directory: 'must be a directory'
7
+ not_readable: 'must be readable'
8
+ not_writable: 'must be writable'
@@ -1,4 +1,6 @@
1
- require 'open3'
1
+ require 'posix/spawn'
2
+ require 'active_model'
2
3
  require 'vidibus-core_extensions'
3
4
 
4
5
  require 'vidibus/pureftpd'
6
+ require 'vidibus/pureftpd/user'
@@ -1,7 +1,5 @@
1
1
  module Vidibus
2
2
  module Pureftpd
3
- VERSION = '0.1.0'
4
-
5
3
  class Error < StandardError; end
6
4
 
7
5
  class << self
@@ -17,60 +15,25 @@ module Vidibus
17
15
  }
18
16
  end
19
17
 
20
- # Adds a new user.
21
- # Required options:
22
- # :login, :password, :directory
23
- def add_user(options)
24
- unless options.keys?(:login, :password, :directory)
25
- raise ArgumentError.new('Required options are :login, :password, :directory')
26
- end
27
- password = options.delete(:password)
28
- cmd = 'pure-pw useradd %{login} -f %{password_file} -u %{sysuser} -g %{sysgroup} -d %{directory} -m' % settings.merge(options)
29
- perform(cmd) do |stdin, stdout, stderr|
30
- stdin.puts(password)
31
- stdin.puts(password)
32
- end
33
- end
34
-
35
- # Deletes an existing user.
36
- # Required options:
37
- # :login
38
- def delete_user(options)
39
- unless options.key?(:login)
40
- raise ArgumentError.new('Required option is :login')
41
- end
42
- cmd = 'pure-pw userdel %{login} -f %{password_file} -m' % settings.merge(options)
43
- perform(cmd)
44
- end
45
-
46
- # Changes password of existing user.
47
- # Required options:
48
- # :login, :password
49
- def change_password(options)
50
- unless options.keys?(:login, :password)
51
- raise ArgumentError.new('Required options are :login, :password')
52
- end
53
- password = options.delete(:password)
54
- cmd = 'pure-pw passwd %{login} -f %{password_file} -m' % settings.merge(options)
55
- perform(cmd) do |stdin, stdout, stderr|
56
- stdin.puts(password)
57
- stdin.puts(password)
58
- end
59
- end
60
-
61
- protected
62
-
63
- # Performs given command. Accepts a block with |stdin, stdout, stderr|.
18
+ # Performs given command prefixed with pure-pw.
19
+ # Accepts a block with |stdin, stdout, stderr|.
64
20
  def perform(cmd, &block)
21
+ cmd = "pure-pw #{cmd}"
65
22
  error = ''
66
- Open3.popen3(cmd) do |stdin, stdout, stderr|
67
- yield(stdin, stdout, stderr) if block_given?
68
- error = stderr.read
69
- end
23
+ output = nil
24
+
25
+ pid, stdin, stdout, stderr = POSIX::Spawn::popen4(cmd)
26
+ yield(stdin, stdout, stderr) if block_given?
27
+ error = stderr.read
28
+ output = stdout.read
29
+
70
30
  unless error == ''
71
- raise Error.new("Error while executing this command:\n#{cmd}\n\n#{error}")
31
+ raise Error.new("Pure-FTPd returned an error:\n#{cmd}\n\n#{error}")
72
32
  end
73
- true
33
+ output
34
+ ensure
35
+ [stdin, stdout, stderr].each { |io| io.close if !io.closed? }
36
+ Process::waitpid(pid)
74
37
  end
75
38
  end
76
39
  end
@@ -4,27 +4,56 @@ module Vidibus
4
4
  include ActiveModel::Validations
5
5
  include ActiveModel::Dirty
6
6
 
7
- attr_accessor :login, :password, :directory
7
+ class Error < Vidibus::Pureftpd::Error; end
8
+ class DocumentNotFound < Error; end
9
+
10
+ ACCESSSORS = [:login, :password, :directory]
11
+
12
+ ACCESSSORS.each do |attribute|
13
+ class_eval <<-EOS
14
+ def #{attribute}=(value)
15
+ unless @#{attribute} == value
16
+ #{attribute}_will_change!
17
+ @#{attribute} = value
18
+ end
19
+ end
20
+
21
+ def #{attribute}
22
+ @#{attribute}
23
+ end
24
+ EOS
25
+ end
8
26
 
9
- validate :login, :password, :directory, :presence => true
10
- validates :unique_login
27
+ define_attribute_methods ACCESSSORS
28
+
29
+ validates :password, :directory, :presence => true
30
+ validates :login, :format => { :with => /^[a-z_]+$/ }
31
+ validate :unique_login?, :if => :login_changed?
32
+ validate :valid_directory?, :if => :directory_changed?
11
33
 
12
34
  def initialize(values = {})
13
- attributes = values
35
+ self.attributes = values
36
+ @persisted = false
14
37
  end
15
38
 
16
39
  def save
40
+ return false unless valid? && changed?
17
41
  persisted? ? update : create
42
+ @previously_changed = changes
43
+ @changed_attributes.clear
44
+ @persisted = true
18
45
  end
19
46
 
20
47
  def destroy
21
- cmd = 'userdel %{login} -f %{password_file} -m' % settings
48
+ return false unless persisted? && login_was
49
+ cmd = "userdel #{login_was} -f %{password_file} -m" % settings
22
50
  perform(cmd)
23
- user.instance_variable_set('@is_persisted', false)
51
+ instance_variable_set('@persisted', false)
52
+ self
24
53
  end
25
54
 
26
55
  def persisted?
27
- @is_persisted || User.find(login)
56
+ @persisted
28
57
  end
29
58
 
30
59
  def attributes
@@ -35,50 +64,91 @@ module Vidibus
35
64
  hash
36
65
  end
37
66
 
38
- def attributes=(values)
39
- values.each {|a| self.send("#{a}=")}
67
+ def attributes=(hash)
68
+ hash.each do |key, value|
69
+ send("#{key}=", value)
70
+ end
40
71
  end
41
72
 
42
73
  def reload
43
- attributes = User.find(login).attributes
44
- end
45
-
46
- def settings
47
- attributes.merge(Vidibus::Pureftpd.settings)
74
+ if persisted? && login_was && (user = User.find_by_login(login_was))
75
+ self.attributes = user.attributes
76
+ self
77
+ else
78
+ raise DocumentNotFound
79
+ end
48
80
  end
49
81
 
50
82
  class << self
51
83
 
52
- def find(login)
53
- cmd = 'show %{login} -f %{password_file}' % settings
54
- data = Vidibus::Pureftpd.perform(cmd)
84
+ def find_by_login(login)
85
+ cmd = "show #{login} -f %{password_file}" %
86
+ Vidibus::Pureftpd.settings
87
+ begin
88
+ data = Vidibus::Pureftpd.perform(cmd)
89
+ rescue Vidibus::Pureftpd::Error => e
90
+ if e.message.match('Unable to fetch info about user')
91
+ return nil
92
+ else
93
+ raise e
94
+ end
95
+ end
96
+
55
97
  attributes = {}
98
+ data.scan(/(#{ACCESSSORS.join('|')})\s*:\s*(.+)/i).each do |key, value|
99
+ attributes[key.downcase] = value
100
+ end
101
+
56
102
  User.new(attributes).tap do |user|
57
- user.instance_variable_set('@is_persisted', true)
103
+ user.instance_variable_set('@persisted', true)
58
104
  end
59
105
  end
60
106
 
61
107
  def create(attributes)
62
-
108
+ new(attributes).tap do |user|
109
+ user.save
110
+ end
63
111
  end
64
112
  end
65
113
 
66
114
  private
67
115
 
68
- def unique_login
69
- puts %(login = #{login.inspect})
70
- puts %(login_was = #{login_was.inspect})
116
+ def settings
117
+ attributes.merge(Vidibus::Pureftpd.settings)
118
+ end
119
+
120
+ def unique_login?
121
+ return unless login.present?
122
+ if User.find_by_login(login)
123
+ self.errors.add(:login, :taken)
124
+ end
125
+ end
126
+
127
+ def valid_directory?
128
+ return unless directory.present?
129
+ if !File.exist?(directory)
130
+ self.errors.add(:directory, :not_existent)
131
+ elsif !File.directory?(directory)
132
+ self.errors.add(:directory, :not_a_directory)
133
+ elsif !File.readable?(directory)
134
+ self.errors.add(:directory, :not_readable)
135
+ elsif !File.writable?(directory)
136
+ self.errors.add(:directory, :not_writable)
137
+ end
71
138
  end
72
139
 
73
140
  def update
74
141
  if login_changed?
75
142
  a = attributes
143
+ destroy
144
+ User.create(a)
76
145
  end
77
146
  if password_changed?
78
147
  update_password
79
148
  end
80
-
81
- # usermod
149
+ if changes.except(:login, :password).any?
150
+ update_attributes
151
+ end
82
152
  end
83
153
 
84
154
  def create
@@ -87,14 +157,22 @@ module Vidibus
87
157
  end
88
158
 
89
159
  def update_password
90
- cmd = 'pure-pw passwd %{login} -f %{password_file} -m' % settings
160
+ cmd = 'passwd %{login} -f %{password_file} -m' % settings
91
161
  perform_with_password_input(cmd)
92
162
  end
93
163
 
164
+ def update_attributes
165
+ cmd = 'usermod %{login} -d %{directory} -f %{password_file} -m' % settings
166
+ perform(cmd)
167
+ end
168
+
169
+ # TODO: Dry this up. But args << &Proc.new isn't possible.
94
170
  def perform(cmd)
95
- Vidibus::Pureftpd.perform(cmd)
96
- rescue Vidibus::Pureftpd::Error => e
97
- raise fucked
171
+ if block_given?
172
+ Vidibus::Pureftpd.perform(cmd, &Proc.new)
173
+ else
174
+ Vidibus::Pureftpd.perform(cmd)
175
+ end
98
176
  end
99
177
 
100
178
  def perform_with_password_input(cmd)
@@ -103,7 +181,6 @@ module Vidibus
103
181
  stdin.puts(password)
104
182
  end
105
183
  end
106
-
107
184
  end
108
185
  end
109
186
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vidibus-pureftpd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,6 +11,22 @@ bindir: bin
11
11
  cert_chain: []
12
12
  date: 2012-09-19 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: posix-spawn
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: vidibus-core_extensions
16
32
  requirement: !ruby/object:Gem::Requirement
@@ -27,6 +43,22 @@ dependencies:
27
43
  - - ! '>='
28
44
  - !ruby/object:Gem::Version
29
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activemodel
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
30
62
  - !ruby/object:Gem::Dependency
31
63
  name: bundler
32
64
  requirement: !ruby/object:Gem::Requirement
@@ -133,6 +165,8 @@ files:
133
165
  - lib/vidibus/pureftpd/version.rb
134
166
  - lib/vidibus/pureftpd.rb
135
167
  - lib/vidibus-pureftpd.rb
168
+ - config/locales/de.yml
169
+ - config/locales/en.yml
136
170
  - LICENSE
137
171
  - README.md
138
172
  - Rakefile
@@ -150,7 +184,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
150
184
  version: '0'
151
185
  segments:
152
186
  - 0
153
- hash: -1082857674235287288
187
+ hash: 848917452145310950
154
188
  required_rubygems_version: !ruby/object:Gem::Requirement
155
189
  none: false
156
190
  requirements: