postdb 0.1.5

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