postdb 0.1.5

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.
@@ -0,0 +1,62 @@
1
+ module PostDB
2
+ class Domain < ActiveRecord::Base
3
+ self.table_name = 'virtual_domains'
4
+
5
+ serialize :dkim, OpenSSL::PKey::RSA
6
+
7
+ has_many :users, dependent: :destroy
8
+ has_many :aliases, dependent: :destroy
9
+
10
+ validates :name, presence: true, uniqueness: true
11
+ validates :dkim, presence: true, uniqueness: true
12
+
13
+ before_validation(on: :create) do |domain|
14
+ domain.regenerate_dkim
15
+ end
16
+
17
+ after_save do
18
+ PostDB::DKIM.generate_configuration
19
+ end
20
+
21
+ after_destroy do
22
+ PostDB::DKIM.generate_configuration
23
+ end
24
+
25
+ # Get all forwarding aliases
26
+ # This excludes aliases where source == destination
27
+ #
28
+ # Example:
29
+ # >> domain.forwarding_aliases
30
+ # => []
31
+ #
32
+ def forwarding_aliases
33
+ # self.aliases.select { |a| a.source != a.destination }
34
+ self.aliases.where('source != destination')
35
+ end
36
+
37
+ # Get the path to the DKIM key file
38
+ #
39
+ # Example:
40
+ # >> domain.dkim_path
41
+ # => "..."
42
+ #
43
+ def dkim_path
44
+ keys_directory = PostDB::DKIM.keys_directory
45
+
46
+ File.join(keys_directory, "#{self.name}.private")
47
+ end
48
+
49
+ # Generate a new DKIM key
50
+ #
51
+ # Arguments:
52
+ # size: (Integer) (Default: 2048)
53
+ #
54
+ # Example:
55
+ # >> domain.regenerate_dkim
56
+ # => #<OpenSSL::PKey::RSA:0x00000000000000>
57
+ #
58
+ def regenerate_dkim(size = 2048)
59
+ self.dkim = OpenSSL::PKey::RSA.new(size)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.expand_path('../errors/**/*.rb', __FILE__)).each do |matcher|
2
+ require(matcher)
3
+ end
@@ -0,0 +1,45 @@
1
+ module PostDB
2
+ # The DKIMError error is used by the DKIM class
3
+ #
4
+ class DKIMError < StandardError
5
+ # A suberror to describe the error in more detail
6
+ #
7
+ attr_reader :attribute
8
+
9
+ # An array containing additional arguments
10
+ #
11
+ attr_reader :arguments
12
+
13
+ # Create a new instance of this exception
14
+ #
15
+ # Arguments:
16
+ # suberror: (Symbol|String)
17
+ # args: (Array)
18
+ #
19
+ # Example:
20
+ # >> raise PostDB::DKIMError.new
21
+ # => #<PostDB::DKIMError:0x00000000000000>
22
+ #
23
+ def initialize(suberror = nil, *args)
24
+ # Store the suberror property (if provided)
25
+ @suberror = suberror if suberror
26
+
27
+ # Store the arguments
28
+ @arguments = args
29
+ end
30
+
31
+ # Convert the error to a string
32
+ #
33
+ # Example:
34
+ # >> error.to_s
35
+ # => "Error Description"
36
+ #
37
+ def to_s
38
+ case @suberror
39
+ when nil
40
+ else
41
+ super.to_s
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ module PostDB
2
+ # The MailLocationError error is used by the MailLocation class
3
+ #
4
+ class MailLocationError < StandardError
5
+ # A suberror to describe the error in more detail
6
+ #
7
+ attr_reader :attribute
8
+
9
+ # An array containing additional arguments
10
+ #
11
+ attr_reader :arguments
12
+
13
+ # Create a new instance of this exception
14
+ #
15
+ # Arguments:
16
+ # suberror: (Symbol|String)
17
+ # args: (Array)
18
+ #
19
+ # Example:
20
+ # >> raise PostDB::MailLocationError.new
21
+ # => #<PostDB::MailLocationError:0x00000000000000>
22
+ #
23
+ def initialize(suberror = nil, *args)
24
+ # Store the suberror property (if provided)
25
+ @suberror = suberror if suberror
26
+
27
+ # Store the arguments
28
+ @arguments = args
29
+ end
30
+
31
+ # Convert the error to a string
32
+ #
33
+ # Example:
34
+ # >> error.to_s
35
+ # => "Error Description"
36
+ #
37
+ def to_s
38
+ case @suberror
39
+ when :malformed_location
40
+ "'#{@arguments[0]}' is not a valid location."
41
+ else
42
+ super.to_s
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ module PostDB
2
+ # The SetupError error is used when a setup error occurs
3
+ #
4
+ class SetupError < StandardError
5
+ # A suberror to describe the error in more detail
6
+ #
7
+ attr_reader :attribute
8
+
9
+ # An array containing additional arguments
10
+ #
11
+ attr_reader :arguments
12
+
13
+ # Create a new instance of this exception
14
+ #
15
+ # Arguments:
16
+ # suberror: (Symbol|String)
17
+ # args: (Array)
18
+ #
19
+ # Example:
20
+ # >> raise PostDB::SetupError.new
21
+ # => #<PostDB::SetupError:0x00000000000000>
22
+ #
23
+ def initialize(suberror = nil, *args)
24
+ # Store the suberror property (if provided)
25
+ @suberror = suberror if suberror
26
+
27
+ # Store the arguments
28
+ @arguments = args
29
+ end
30
+
31
+ # Convert the error to a string
32
+ #
33
+ # Example:
34
+ # >> error.to_s
35
+ # => "Error Description"
36
+ #
37
+ def to_s
38
+ case @suberror
39
+ when :invalid_adapter
40
+ "The adapter '#{@arguments[0]}' is not valid."
41
+ when :missing_database_args
42
+ "You must provide the database configuration."
43
+ when :missing_mail_args
44
+ "You must provide the mail configuration."
45
+ when :missing_dkim_args
46
+ "You must provide the DKIM configuration."
47
+ when :missing_mail_location
48
+ "You must provide the mail location string."
49
+ when :missing_dkim_directory
50
+ "You must provide the path to the DKIM keys directory."
51
+ when :missing_trusted_hosts_path
52
+ "You must provide the path to the DKIM TrustedHosts file."
53
+ when :missing_key_table_path
54
+ "You must provide the path to the DKIM KeyTable file."
55
+ when :missing_signing_table_path
56
+ "You must provide the path to the DKIM SigningTable file."
57
+ else
58
+ super.to_s
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.expand_path('../helpers/**/*.rb', __FILE__)).each do |matcher|
2
+ require(matcher)
3
+ end
@@ -0,0 +1,15 @@
1
+ class Array
2
+ # Add padding to each element in the array
3
+ #
4
+ # Arguments:
5
+ # padding: (String) The padding to apply before and after the element
6
+ #
7
+ # Example:
8
+ # >> array = [ "Name" ]
9
+ # >> array.pad(" ")
10
+ # => [ " Name " ]
11
+ #
12
+ def pad(padding)
13
+ map { |element| "#{padding}#{element}#{padding}" }
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
2
+ inflect.acronym 'DKIM'
3
+ end
@@ -0,0 +1,55 @@
1
+ module OpenSSL
2
+ module PKey
3
+ class RSA
4
+ class << self
5
+ # Dump the object
6
+ #
7
+ # Arguments:
8
+ # object: (OpenSSL::PKey::RSA)
9
+ #
10
+ # Example:
11
+ # >> OpenSSL::PKey::RSA.dump(rsa)
12
+ # => "..."
13
+ #
14
+ def dump(object)
15
+ unless object.is_a?(self)
16
+ raise ActiveRecord::SerializationTypeMismatch, "Expected '#{self}' got '#{object.class}'."
17
+ end
18
+
19
+ object.to_der
20
+ end
21
+
22
+ # Load the object
23
+ #
24
+ # Arguments:
25
+ # object: (String)
26
+ #
27
+ # Example:
28
+ # >> OpenSSL::PKey::RSA.load(object)
29
+ # => #<OpenSSL::PKey::RSA:0x00000000000000>
30
+ #
31
+ def load(object)
32
+ return nil unless object
33
+
34
+ new(object)
35
+ end
36
+ end
37
+
38
+ # Check if the RSA key is valid
39
+ #
40
+ # Example:
41
+ # >> key.valid?
42
+ # => true
43
+ #
44
+ def valid?
45
+ begin
46
+ self.class.new(self.to_der)
47
+ rescue OpenSSL::PKey::RSAError => e
48
+ return false
49
+ end
50
+
51
+ true
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,22 @@
1
+ class String
2
+ # Check if the string is a crypt
3
+ #
4
+ # Arguments:
5
+ # crypt: (Symbol)
6
+ #
7
+ # Example:
8
+ # >> crypt = "$6$AzeWWlznoLKPRJ1m$MeWkZqWKJp1XD.Jnt66D.Riubq7HT9vrRV0AwFXov2rxyzONR50ULiUFj7Kl6ykmh4EXsmpox8QUr6EUIo3NB0"
9
+ # >> crypt.is_crypt?(:sha512)
10
+ # => true
11
+ #
12
+ def is_crypt?(crypt)
13
+ case crypt
14
+ when :sha512
15
+ return false unless self =~ /^(\$6\$[a-zA-Z0-9]{16}\$[a-zA-Z0-9\.\/\\]{86})$/
16
+
17
+ true
18
+ else
19
+ false
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ module PostDB
2
+ class Mail
3
+ class << self
4
+ # The template path to the mail location
5
+ #
6
+ attr_reader :mail_location
7
+
8
+ # Setup the mail configuration
9
+ #
10
+ # Example:
11
+ # >> PostDB::Mail.setup_with_configuration!
12
+ # => nil
13
+ #
14
+ def setup_with_configuration!
15
+ configuration = PostDB::Configuration[:mail]
16
+
17
+ unless configuration.is_a?(Hash)
18
+ raise PostDB::SetupError.new(:missing_mail_args)
19
+ end
20
+
21
+ unless configuration[:location]
22
+ raise PostDB::SetupError.new(:missing_mail_location)
23
+ end
24
+
25
+ @mail_location = PostDB::MailLocation.new(configuration[:location])
26
+
27
+ nil
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ module PostDB
2
+ class MailLocation
3
+ attr_reader :type
4
+ attr_reader :path
5
+ attr_reader :components
6
+
7
+ def initialize(location)
8
+ unless location =~ /^[A-Za-z0-9]+:(.*)$/
9
+ raise PostDB::MailLocationError.new(:malformed_location)
10
+ end
11
+
12
+ components = location.split(':')
13
+
14
+ @type = components[0].to_sym
15
+ @path = components[1]
16
+
17
+ begin
18
+ @components = Hash[*components[2..-1].map { |c| c.split('=') }.flatten]
19
+ rescue => e
20
+ raise PostDB::MailLocationError.new(:malformed_location)
21
+ end
22
+ end
23
+
24
+ def get_path(username, domain, home_dir = nil)
25
+ path = @path.dup
26
+ path.gsub!('%u', username)
27
+ path.gsub!('%n', username.split('@')[0])
28
+ path.gsub!('%d', domain)
29
+ path.gsub!('%h', home_dir) if home_dir
30
+ path
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ module PostDB
2
+ class User < ActiveRecord::Base
3
+ self.table_name = 'virtual_users'
4
+
5
+ has_password :sha512
6
+
7
+ has_one :alias, dependent: :destroy
8
+
9
+ belongs_to :domain
10
+
11
+ validates :email, presence: true, uniqueness: true
12
+ validates :crypted_password, presence: true
13
+
14
+ before_save do |user|
15
+ user.alias ||= PostDB::Alias.create(
16
+ domain: user.domain,
17
+ source: user.email,
18
+ destination: user.email
19
+ )
20
+ end
21
+
22
+ after_destroy do |user|
23
+ user.delete_account_data
24
+ end
25
+
26
+ def delete_account_data
27
+ mail_location = PostDB::Mail.mail_location
28
+
29
+ path = mail_location.get_path(self.email, self.domain.name)
30
+
31
+ return false unless File.exist?(path)
32
+
33
+ begin
34
+ FileUtils.rm_rf(path)
35
+ rescue => e
36
+ return false
37
+ end
38
+
39
+ true
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'postdb/constants'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'postdb'
8
+ spec.version = PostDB::VERSION
9
+ spec.authors = ['Rediweb Hosting']
10
+ spec.email = ['support@rediwebhosting.uk']
11
+
12
+ spec.summary = %q{Postfix Database Management}
13
+ spec.description = %q{An easy to use library for managing your postfix database.}
14
+ spec.homepage = 'https://rubygems.org/gems/postdb'
15
+ spec.license = 'GPL-3.0'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.executables = spec.files.grep(%r{^bin/(?!((console|setup)$))}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'activerecord', '~> 4.2'
23
+ spec.add_dependency 'attr_password', '~> 0.1'
24
+ spec.add_dependency 'thor', '~> 0.19.1'
25
+ spec.add_dependency 'tty-prompt', '~> 0.3'
26
+ spec.add_dependency 'tty-table', '~> 0.4'
27
+
28
+ spec.add_dependency 'mysql', '~> 2.9'
29
+ spec.add_dependency 'mysql2', '~> 0.4'
30
+ spec.add_dependency 'postgresql', '~> 1.0'
31
+ spec.add_dependency 'sqlite3', '~> 1.3'
32
+
33
+ spec.add_development_dependency 'bundler', '~> 1.11'
34
+ end