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