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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +15 -0
- data/bin/console +8 -0
- data/bin/postdb +5 -0
- data/bin/setup +6 -0
- data/db/migrate/20160508215354_create_domains.rb +10 -0
- data/db/migrate/20160508215355_create_users.rb +11 -0
- data/db/migrate/20160508215356_create_aliases.rb +12 -0
- data/lib/postdb.rb +55 -0
- data/lib/postdb/alias.rb +24 -0
- data/lib/postdb/cli.rb +54 -0
- data/lib/postdb/cli/aliases.rb +123 -0
- data/lib/postdb/cli/database.rb +39 -0
- data/lib/postdb/cli/domains.rb +89 -0
- data/lib/postdb/cli/domains/dkim.rb +69 -0
- data/lib/postdb/cli/helper.rb +101 -0
- data/lib/postdb/cli/main.rb +44 -0
- data/lib/postdb/cli/users.rb +146 -0
- data/lib/postdb/configuration.rb +35 -0
- data/lib/postdb/constants.rb +3 -0
- data/lib/postdb/database.rb +23 -0
- data/lib/postdb/dkim.rb +197 -0
- data/lib/postdb/domain.rb +62 -0
- data/lib/postdb/errors.rb +3 -0
- data/lib/postdb/errors/dkim.rb +45 -0
- data/lib/postdb/errors/mail_location.rb +46 -0
- data/lib/postdb/errors/setup.rb +62 -0
- data/lib/postdb/helpers.rb +3 -0
- data/lib/postdb/helpers/array.rb +15 -0
- data/lib/postdb/helpers/inflections.rb +3 -0
- data/lib/postdb/helpers/openssl/pkey/rsa.rb +55 -0
- data/lib/postdb/helpers/string.rb +22 -0
- data/lib/postdb/mail.rb +31 -0
- data/lib/postdb/mail_location.rb +33 -0
- data/lib/postdb/user.rb +42 -0
- data/postdb.gemspec +34 -0
- metadata +222 -0
@@ -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,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,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,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
|
data/lib/postdb/mail.rb
ADDED
@@ -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
|
data/lib/postdb/user.rb
ADDED
@@ -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
|
data/postdb.gemspec
ADDED
@@ -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
|