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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7eb8c10674d608a4ebe84dc2c0a3c324235d4550
|
4
|
+
data.tar.gz: a932e7b6771517dfb39e57960ad49ec74fbfa93b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 075eb7656fc26bef5b300e9beea11b1964fa91412c900959ab12224a82592de199e272bdf9f2e685e44ad7f1b468e2bcd7e7c2b8de02fae0cb210b43e838faa1
|
7
|
+
data.tar.gz: 97aae2ed0cef5e610e489ee38fb1bd754ec202f4686f54155958d54d14cc483195849bcab0c3141219d37a72307750792625885a748716f158c524dcd85c0838
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/bin/console
ADDED
data/bin/postdb
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateAliases < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :virtual_aliases do |t|
|
4
|
+
t.integer :domain_id, null: false
|
5
|
+
t.integer :user_id, null: true
|
6
|
+
t.string :source, null: false
|
7
|
+
t.string :destination, null: false
|
8
|
+
|
9
|
+
t.timestamps(null: false)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/postdb.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# The PostDB main module
|
2
|
+
#
|
3
|
+
module PostDB
|
4
|
+
class << self
|
5
|
+
# Require dependencies
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# >> PostDB.require!
|
9
|
+
# => nil
|
10
|
+
#
|
11
|
+
def require!
|
12
|
+
require 'cgi'
|
13
|
+
require 'openssl'
|
14
|
+
require 'fileutils'
|
15
|
+
require 'active_support'
|
16
|
+
require 'active_record'
|
17
|
+
require 'attr_password'
|
18
|
+
|
19
|
+
require 'postdb/constants'
|
20
|
+
require 'postdb/errors'
|
21
|
+
require 'postdb/helpers'
|
22
|
+
require 'postdb/configuration'
|
23
|
+
require 'postdb/database'
|
24
|
+
require 'postdb/mail_location'
|
25
|
+
require 'postdb/mail'
|
26
|
+
require 'postdb/dkim'
|
27
|
+
require 'postdb/domain'
|
28
|
+
require 'postdb/user'
|
29
|
+
require 'postdb/alias'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Setup the gem
|
33
|
+
#
|
34
|
+
# Arguments:
|
35
|
+
# path: (String) The path to the configuration file
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
# >> PostDB.setup(path)
|
39
|
+
# => true
|
40
|
+
#
|
41
|
+
def setup(path)
|
42
|
+
PostDB::Configuration.load_file(path)
|
43
|
+
|
44
|
+
PostDB::Database.setup_with_configuration!
|
45
|
+
PostDB::Mail.setup_with_configuration!
|
46
|
+
PostDB::DKIM.setup_with_configuration!
|
47
|
+
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Require dependencies
|
54
|
+
#
|
55
|
+
PostDB.require!
|
data/lib/postdb/alias.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module PostDB
|
2
|
+
class Alias < ActiveRecord::Base
|
3
|
+
self.table_name = 'virtual_aliases'
|
4
|
+
|
5
|
+
belongs_to :domain
|
6
|
+
belongs_to :user
|
7
|
+
|
8
|
+
validates :source, presence: true
|
9
|
+
validates :destination, presence: true
|
10
|
+
|
11
|
+
validate do |aliaz|
|
12
|
+
alias_count = self.class.where(
|
13
|
+
source: aliaz.source,
|
14
|
+
destination: aliaz.destination
|
15
|
+
).where.not(
|
16
|
+
id: aliaz.id
|
17
|
+
).count
|
18
|
+
|
19
|
+
next unless alias_count > 0
|
20
|
+
|
21
|
+
errors.add(:source_and_destination, "must be unique")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/postdb/cli.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# The PostDB main module
|
2
|
+
#
|
3
|
+
module PostDB
|
4
|
+
# The CLI module
|
5
|
+
#
|
6
|
+
module CLI
|
7
|
+
class << self
|
8
|
+
# Require dependencies
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
# >> PostDB.require!
|
12
|
+
# => nil
|
13
|
+
#
|
14
|
+
def require!
|
15
|
+
require 'yaml'
|
16
|
+
require 'thor'
|
17
|
+
require 'tty-prompt'
|
18
|
+
require 'tty-table'
|
19
|
+
require 'postdb'
|
20
|
+
|
21
|
+
require 'postdb/cli/helper'
|
22
|
+
require 'postdb/cli/database'
|
23
|
+
require 'postdb/cli/domains/dkim'
|
24
|
+
require 'postdb/cli/domains'
|
25
|
+
require 'postdb/cli/users'
|
26
|
+
require 'postdb/cli/aliases'
|
27
|
+
require 'postdb/cli/main'
|
28
|
+
end
|
29
|
+
|
30
|
+
# Ensure superuser
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# >> PostDB::CLI.ensure_superuser!
|
34
|
+
# => nil
|
35
|
+
#
|
36
|
+
def ensure_superuser!
|
37
|
+
return nil if 0 == Process.uid
|
38
|
+
|
39
|
+
prompt = TTY::Prompt.new
|
40
|
+
prompt.error("PostDB requires superuser privileges to run!")
|
41
|
+
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Require dependencies
|
49
|
+
#
|
50
|
+
PostDB::CLI.require!
|
51
|
+
|
52
|
+
# Ensure superuser privileges
|
53
|
+
#
|
54
|
+
PostDB::CLI.ensure_superuser!
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module PostDB
|
2
|
+
module CLI
|
3
|
+
class Aliases < Thor
|
4
|
+
SOURCE_REGEX = /^([A-Za-z0-9\-\_\.\+]+)?@[A-Za-z0-9\-]+\.[A-Za-z0-9\-\.]+$/
|
5
|
+
|
6
|
+
no_tasks do
|
7
|
+
include PostDB::CLI::Helper
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "list DOMAIN", "List all aliases"
|
11
|
+
def list(domain = nil)
|
12
|
+
domains = PostDB::Domain.where(**(domain ? { name: domain } : {}))
|
13
|
+
|
14
|
+
if domains.empty?
|
15
|
+
if domain
|
16
|
+
exit_with_warning("The domain '#{domain}' could not be found.")
|
17
|
+
else
|
18
|
+
exit_with_warning("There don't appear to be any domains on this system.")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
domains = domains.to_a
|
23
|
+
domains.sort! { |a, b| a.name <=> b.name }
|
24
|
+
|
25
|
+
domains.each_with_index do |domain, index|
|
26
|
+
aliases = domain.forwarding_aliases.sort { |a, b| a.source <=> b.source }
|
27
|
+
|
28
|
+
if aliases.empty?
|
29
|
+
puts TTY::Table.new(
|
30
|
+
header: [domain.name].pad(' '),
|
31
|
+
rows: [['No Aliases'].pad(' ')]
|
32
|
+
).render(:ascii)
|
33
|
+
else
|
34
|
+
puts TTY::Table.new(
|
35
|
+
header: [" " + domain.name + " \n Source: ", "\n Destination: "],
|
36
|
+
rows: aliases.map { |a| [a.source, a.destination].pad(' ') }
|
37
|
+
).render(:ascii, multiline: true)
|
38
|
+
end
|
39
|
+
|
40
|
+
new_line unless (index + 1) == domains.count
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "add SOURCE DESTINATION", "Add an alias"
|
45
|
+
def add(source = nil, destination = nil)
|
46
|
+
unless source
|
47
|
+
source = prompt.ask("Source:") do |q|
|
48
|
+
q.required(true)
|
49
|
+
q.validate(SOURCE_REGEX)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
unless destination
|
54
|
+
destination = prompt.ask("Destination:") do |q|
|
55
|
+
q.required(true)
|
56
|
+
q.validate(:email)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
source = source.downcase
|
61
|
+
destination = destination.downcase
|
62
|
+
|
63
|
+
if source == destination
|
64
|
+
exit_with_error("The alias '#{source} -> #{destination}' is managed automatically by PostDB.")
|
65
|
+
end
|
66
|
+
|
67
|
+
if PostDB::Alias.where(source: source, destination: destination).count > 0
|
68
|
+
exit_with_warning("The alias '#{source} -> #{destination}' has already been added.")
|
69
|
+
end
|
70
|
+
|
71
|
+
domain_name = source.split('@')[1]
|
72
|
+
|
73
|
+
unless domain = PostDB::Domain.where(name: domain_name).first
|
74
|
+
exit_with_error("The domain '#{domain_name}' is not available.")
|
75
|
+
end
|
76
|
+
|
77
|
+
PostDB::Alias.create(domain: domain, source: source, destination: destination)
|
78
|
+
|
79
|
+
prompt.ok("The alias '#{source} -> #{destination}' has been added.")
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "remove SOURCE DESTINATION", "Remove an alias"
|
83
|
+
option :force, type: :boolean, default: false
|
84
|
+
def remove(source = nil, destination = nil)
|
85
|
+
unless source && destination
|
86
|
+
aliases = PostDB::Alias.all.select { |a| a.source != a.destination }
|
87
|
+
|
88
|
+
if aliases.empty?
|
89
|
+
exit_with_warning("There don't appear to be any aliases on this system.")
|
90
|
+
end
|
91
|
+
|
92
|
+
a = prompt.select("Alias:", aliases.map { |a| ["#{a.source} -> #{a.destination}", a] }.to_h)
|
93
|
+
|
94
|
+
source = a.source
|
95
|
+
destination = a.destination
|
96
|
+
end
|
97
|
+
|
98
|
+
source = source.downcase
|
99
|
+
destination = destination.downcase
|
100
|
+
|
101
|
+
if source == destination
|
102
|
+
exit_with_error("The alias '#{source} -> #{destination}' is managed automatically by PostDB.")
|
103
|
+
end
|
104
|
+
|
105
|
+
aliases = PostDB::Alias.where(source: source, destination: destination)
|
106
|
+
|
107
|
+
if aliases.empty?
|
108
|
+
exit_with_warning("The alias '#{source} -> #{destination}' could not be found.")
|
109
|
+
end
|
110
|
+
|
111
|
+
unless options[:force]
|
112
|
+
confirm_action!("Remove the alias '#{source} -> #{destination}'?", "'#{source} -> #{destination}' left untouched.")
|
113
|
+
end
|
114
|
+
|
115
|
+
aliases.each(&:destroy)
|
116
|
+
|
117
|
+
prompt.ok("The alias '#{source} -> #{destination}' has been removed.")
|
118
|
+
end
|
119
|
+
|
120
|
+
default_task :list
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module PostDB
|
2
|
+
module CLI
|
3
|
+
class Database < Thor
|
4
|
+
no_tasks do
|
5
|
+
include PostDB::CLI::Helper
|
6
|
+
|
7
|
+
# Get the ActiveRecord::Base connection
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# >> connection
|
11
|
+
# => ?
|
12
|
+
#
|
13
|
+
def connection
|
14
|
+
ActiveRecord::Base.connection
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get the database configuration
|
18
|
+
#
|
19
|
+
# Example:
|
20
|
+
# >> configuration
|
21
|
+
# => { adapter: "mysql", host: "127.0.0.1", username: "mail", password: "...", database: "mail" }
|
22
|
+
#
|
23
|
+
def configuration
|
24
|
+
PostDB::Configuration[:database]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "migrate VERSION", "Migrate the database"
|
29
|
+
def migrate(version = nil)
|
30
|
+
# Get the path to the migrations directory
|
31
|
+
migrations = File.join('..', '..', '..', '..', 'db', 'migrate')
|
32
|
+
migrations = File.expand_path(migrations, __FILE__)
|
33
|
+
|
34
|
+
# Run the migrations
|
35
|
+
ActiveRecord::Migrator.migrate(migrations, version)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module PostDB
|
2
|
+
module CLI
|
3
|
+
class Domains < Thor
|
4
|
+
no_tasks do
|
5
|
+
include PostDB::CLI::Helper
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "list", "List all domains"
|
9
|
+
def list
|
10
|
+
domains = PostDB::Domain.all
|
11
|
+
|
12
|
+
if domains.empty?
|
13
|
+
exit_with_warning("There don't appear to be any domains on this system.")
|
14
|
+
end
|
15
|
+
|
16
|
+
domains = domains.to_a
|
17
|
+
domains.sort! { |a, b| a.name <=> b.name }
|
18
|
+
|
19
|
+
puts TTY::Table.new(
|
20
|
+
header: ["Domain Name", "Total Users", "Total Aliases"].pad(' '),
|
21
|
+
rows: domains.map { |d| [d.name, d.users.count, d.forwarding_aliases.count].pad(' ') }
|
22
|
+
).render(:ascii, multiline: true)
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "add DOMAIN", "Add a domain"
|
26
|
+
def add(domain_name = nil)
|
27
|
+
unless domain_name
|
28
|
+
domain_name = prompt.ask("Domain:") do |q|
|
29
|
+
q.required(true)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
domain_name = domain_name.downcase
|
34
|
+
|
35
|
+
if PostDB::Domain.where(name: domain_name).count > 0
|
36
|
+
exit_with_warning("The domain '#{domain_name}' has already been added.")
|
37
|
+
end
|
38
|
+
|
39
|
+
domain = PostDB::Domain.new
|
40
|
+
domain.name = domain_name
|
41
|
+
|
42
|
+
if domain.save
|
43
|
+
prompt.ok("The domain '#{domain_name}' has been added.")
|
44
|
+
else
|
45
|
+
errors = domain.errors.full_messages.map { |m| " #{m}" }
|
46
|
+
exit_with_error("The domain '#{domain_name}' couldn't be added:", *errors)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "remove DOMAIN", "Remove a domain"
|
51
|
+
option :force, type: :boolean, default: false
|
52
|
+
def remove(domain_name = nil)
|
53
|
+
unless domain_name
|
54
|
+
domains = PostDB::Domain.all
|
55
|
+
|
56
|
+
if domains.empty?
|
57
|
+
exit_with_warning("There don't appear to be any domains on this system.")
|
58
|
+
end
|
59
|
+
|
60
|
+
domains = domains.to_a
|
61
|
+
domains.sort! { |a, b| a.name <=> b.name }
|
62
|
+
|
63
|
+
domain_name = prompt.select("Domain:", domains.map(&:name))
|
64
|
+
end
|
65
|
+
|
66
|
+
domain_name = domain_name.downcase
|
67
|
+
|
68
|
+
domains = PostDB::Domain.where(name: domain_name)
|
69
|
+
|
70
|
+
if domains.empty?
|
71
|
+
exit_with_warning("The domain '#{domain_name}' could not be found.")
|
72
|
+
end
|
73
|
+
|
74
|
+
unless options[:force]
|
75
|
+
confirm_action!("Remove the domain '#{domain_name}'?", "'#{domain_name}' left untouched.")
|
76
|
+
end
|
77
|
+
|
78
|
+
domains.each(&:destroy)
|
79
|
+
|
80
|
+
prompt.ok("The domain '#{domain_name}' has been removed.")
|
81
|
+
end
|
82
|
+
|
83
|
+
desc "dkim SUBCOMMAND ...ARGS", "Manage DKIM keys"
|
84
|
+
subcommand "dkim", PostDB::CLI::Domains::DKIM
|
85
|
+
|
86
|
+
default_task :list
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|