postfix_admin 0.3.0 → 0.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a15e49758d586d12c05d2e0f15f04b123345942d9de889a3617d9cb13ca25bb
4
- data.tar.gz: 9b0a5e9d4741ad9dc8562347c4ca63616bddf97b5e20e6669ae7088bbb04bb29
3
+ metadata.gz: a9877c43c00661c60c1fee5e200211c97cd4cdc8d53593985c3e49f8296d27b9
4
+ data.tar.gz: 9d9744f09e0a9e3ccdc72af138a4879824f3cd00bc3a1a111d0320a30007ee84
5
5
  SHA512:
6
- metadata.gz: eeee12bb7ba7e2d3530878a431f82bfd0c239f64b96225577d46a20e90d60f870a6df73fe96a3dbc02cc69363b380f63976074d8d0e4debd465ae0c014d337a4
7
- data.tar.gz: 257ad43dfbb601927501abab85a6ecf8c0c1bc2d353f2bf7f6be93db81a50c6f27eaf685cc9c6386c5c734a0989e3909346e8aa66247dbabb77c1fb66562b40e
6
+ metadata.gz: 86dfd30f6793fd6c13d09e532d7499f2e040dbe654f1bf86299d79b518a96906b6e208fd87c9bbda631d577ccfe39fb08f910e9cc41617be00425c84860d03f2
7
+ data.tar.gz: f553b648aefc912877d3bcc149258a1d529caa457827f24b04bc4c5d4b45d7fc1397c4189c56ba0e800360320b6295b92abbde0ff344dc56d764995d8d88c448
@@ -38,9 +38,9 @@ jobs:
38
38
  - name: Install dovecotpw
39
39
  run: sudo apt-get install -y dovecot-core
40
40
  - name: Copy configure file
41
- run: cp ./test/misc/postfix_admin.conf ~/.postfix_admin.conf
41
+ run: cp ./spec/misc/postfix_admin.conf ~/.postfix_admin.conf
42
42
  - name: Copy my.cnf (for `rake setup_test_db`)
43
- run: cp ./test/misc/ci.my.cnf ~/.my.cnf
43
+ run: cp ./spec/misc/ci.my.cnf ~/.my.cnf
44
44
  - name: docker-compose up
45
45
  run: docker-compose up -d db
46
46
  - name: Sleep (work around)
@@ -48,7 +48,5 @@ jobs:
48
48
  run: sleep 10
49
49
  - name: Set up test database
50
50
  run: bundle exec rake setup_test_db
51
- - name: Run tests
52
- run: bundle exec rake test
53
51
  - name: Run specs
54
52
  run: bundle exec rake spec
data/CHANGELOG.md CHANGED
@@ -1,29 +1,36 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.3.1
4
+ + Added `admins`, `domains`, `accounts`, `aliases` and `forwards` subcommands
5
+ * Added `teardown` subcommand for the opposite operation of `setup`
6
+ * `delete_domain` subcommand removes logs associated with the domain
7
+ * Added `-s` (scheme) and `-r` (rounds) options for subcommands that require password arguments
8
+ + Encryption rounds are supported only for `BLF-CRYPT`, `SHA256-CRYPT`, and `SHA512-CRYPT` schemes
9
+
3
10
  ## 0.3.0
4
- * Added support for table display format
5
- * No longer supports `dovecotpw` for password hash generation
6
- + Only `doveadm pw` is supported
11
+ * Added support for table display format
12
+ * No longer supports `dovecotpw` for password hash generation
13
+ + Only `doveadm pw` is supported
7
14
 
8
15
  ## 0.2.1
9
- * Added support for the `superadmin` column in the `admin` table
10
- * Added `passwordhash_prefix` keyword in the configuration format for backward compatibility
16
+ * Added support for the `superadmin` column in the `admin` table
17
+ * Added `passwordhash_prefix` keyword in the configuration format for backward compatibility
11
18
 
12
19
  ## 0.2.0
13
- * Switched the object-relational mapper from DataMapper to ActiveRecord
14
- * Stored password hashes now include scheme prefixes, such as `{CRAM-MD5}` and `{PLAIN}`
20
+ * Switched the object-relational mapper from DataMapper to ActiveRecord
21
+ * Stored password hashes now include scheme prefixes, such as `{CRAM-MD5}` and `{PLAIN}`
15
22
 
16
23
  ## 0.1.4
17
- * Added "log" subcommand
24
+ * Added `log` subcommand
18
25
 
19
26
  ## 0.1.3
20
- * Added support for activation and deactivation of domains, admins, and accounts
21
- * Added "edit_admin" subcommand
27
+ * Added support for activation and deactivation of domains, admins, and accounts
28
+ * Added `edit_admin` subcommand
22
29
 
23
30
  ## 0.1.2
24
- * Added support for password hashing by doveadm (external subcommand)
25
- * Display active status
26
- * Hide passwords in list format
31
+ * Added support for password hashing by doveadm (external subcommand)
32
+ * Display active status
33
+ * Hide passwords in list format
27
34
 
28
35
  ## 0.1.1, released 2013-05-10
29
- * Fixed string length issue for passwords
36
+ * Fixed string length issue for passwords
data/Rakefile CHANGED
@@ -2,6 +2,11 @@ require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
  require "rspec/core/rake_task"
4
4
 
5
+ require "bundler/setup"
6
+ Bundler.require(:default, :development)
7
+ require "postfix_admin"
8
+ require "postfix_admin/cli"
9
+
5
10
  Rake::TestTask.new(:test) do |t|
6
11
  t.libs << "test"
7
12
  t.libs << "lib"
@@ -22,3 +27,27 @@ task :setup_test_db do
22
27
  puts import_db_cmd
23
28
  puts `#{import_db_cmd}`
24
29
  end
30
+
31
+ namespace :db do
32
+ desc "Loads the seed data from db/seeds.rb"
33
+ task :seed do
34
+ establish_db_connection
35
+ require_relative "db/seeds"
36
+ end
37
+
38
+ namespace :seed do
39
+ desc "Truncates tables of each database for current environment and loads the seeds"
40
+ task :replant do
41
+ establish_db_connection
42
+
43
+ require_relative "db/reset"
44
+ require_relative "db/seeds"
45
+ end
46
+ end
47
+
48
+ def establish_db_connection
49
+ FactoryBot.find_definitions
50
+
51
+ PostfixAdmin::CLI.new.db_setup
52
+ end
53
+ end
data/db/reset.rb ADDED
@@ -0,0 +1,7 @@
1
+ DomainAdmin.delete_all
2
+ Mailbox.delete_all
3
+ Alias.delete_all
4
+ Domain.without_all.delete_all
5
+ Admin.delete_all
6
+ Quota2.delete_all
7
+ Log.delete_all
data/db/seeds.rb ADDED
@@ -0,0 +1,26 @@
1
+ include FactoryBot::Syntax::Methods
2
+
3
+ create(:domain, domain: "example.com", description: "example.com Description")
4
+ create(:domain, domain: "example.org", description: "example.org Description")
5
+
6
+ all_admin = create(:admin, username: "all@example.com")
7
+ all_admin.rel_domains << Domain.find('ALL')
8
+ all_admin.superadmin = true if all_admin.has_superadmin_column?
9
+ all_admin.save!
10
+
11
+ admin = create(:admin, username: "admin@example.com")
12
+ domain = Domain.find('example.com')
13
+ domain.admins << admin
14
+ domain.rel_aliases << build(:alias, address: "alias@example.com")
15
+ domain.rel_mailboxes << build(:mailbox, local_part: "user")
16
+ domain.rel_mailboxes << build(:mailbox, local_part: "user2")
17
+ domain.save!
18
+
19
+ # forward
20
+ user = Alias.find("user2@example.com").update(goto: "user2@example.com,forward@example.com")
21
+
22
+ create(:quota2, username: "user@example.com", bytes: 75 * PostfixAdmin::KB_TO_MB)
23
+
24
+ create(:log)
25
+ create(:log, action: "delete_domain", data: "user@example.com")
26
+ create(:log, domain: "example.org", data: "example.org")
@@ -16,9 +16,11 @@ $CONF['password_validation'] = array(
16
16
 
17
17
  $CONF['encrypt'] = 'dovecot:CRAM-MD5';
18
18
 
19
+ $CONF['default_aliases'] = array();
19
20
  $CONF['domain_quota'] = 'NO';
20
21
  $CONF['quota'] = 'YES';
22
+ $CONF['emailcheck_resolve_domain']='NO';
21
23
 
22
24
  // setup_password: 'password'
23
25
  $CONF['setup_password'] = '87745eb0269b2f42813b23601be3231a:6e41880f73d97321f2f0b25a5ee30f57f5ab3be8';
24
- ?>
26
+ ?>
data/docker-compose.yml CHANGED
@@ -1,10 +1,12 @@
1
1
  # docker compose up -d
2
2
  #
3
3
  # Run tests on service app
4
- # You may need to run tests on Docker because of its requirement of doveadm
4
+ # You may need to run tests on Docker because of its requirement of `doveadm` command.
5
5
  # docker compose exec app bundle exec rake setup_test_db
6
6
  # docker compose exec app bundle exec rake test
7
7
  # docker compose exec app bundle exec rake spec
8
+ #
9
+ # rspec spec/runner_spec.rb
8
10
 
9
11
  services:
10
12
  app:
@@ -26,12 +28,6 @@ services:
26
28
  - ./docker-admin/config.local.php:/var/www/html/config.local.php
27
29
  depends_on:
28
30
  - db
29
- # environment:
30
- # - POSTFIXADMIN_DB_TYPE=mysqli
31
- # - POSTFIXADMIN_DB_HOST=db
32
- # - POSTFIXADMIN_DB_USER=postfix
33
- # - POSTFIXADMIN_DB_PASSWORD=password
34
- # - POSTFIXADMIN_DB_NAME=postfix
35
31
  db:
36
32
  image: mariadb:10
37
33
  ports:
@@ -100,7 +100,7 @@ module PostfixAdmin
100
100
  domain: domain_name,
101
101
  password: password,
102
102
  name: name,
103
- quota_mb: @config[:maxquota]
103
+ quota: @config[:maxquota] * KB_TO_MB
104
104
  }
105
105
 
106
106
  # An Alias also will be added when a Mailbox is saved.
@@ -121,7 +121,7 @@ module PostfixAdmin
121
121
  domain = find_domain(domain_name)
122
122
 
123
123
  attributes = {
124
- local_part: local_part,
124
+ address: address,
125
125
  goto: goto
126
126
  }
127
127
 
@@ -174,18 +174,6 @@ module PostfixAdmin
174
174
  admin_names = domain.admins.map(&:username)
175
175
 
176
176
  domain.destroy!
177
-
178
- # Remove admins who had the deleted domain only
179
- admin_names.each do |name|
180
- next unless Admin.exists?(name)
181
-
182
- admin = Admin.find(name)
183
-
184
- # check if the admin is needed or not
185
- if admin.rel_domains.empty?
186
- admin.destroy!
187
- end
188
- end
189
177
  end
190
178
 
191
179
  def delete_admin(user_name)
@@ -194,7 +182,6 @@ module PostfixAdmin
194
182
  end
195
183
 
196
184
  admin = Admin.find(user_name)
197
- admin.rel_domains.delete_all
198
185
  admin.destroy!
199
186
  end
200
187
 
@@ -203,8 +190,8 @@ module PostfixAdmin
203
190
  raise_error "Could not find account: #{address}"
204
191
  end
205
192
 
206
- Mailbox.where(username: address).delete_all
207
- Alias.where(address: address).delete_all
193
+ mailbox = Mailbox.find(address)
194
+ mailbox.destroy!
208
195
  end
209
196
 
210
197
  private
@@ -57,9 +57,9 @@ module PostfixAdmin
57
57
  show_domain_details(name)
58
58
  else
59
59
  # no argument: show all domains and admins
60
- show_domain
60
+ show_domains
61
61
  puts
62
- show_admin
62
+ show_admins
63
63
  end
64
64
  end
65
65
 
@@ -73,13 +73,21 @@ module PostfixAdmin
73
73
 
74
74
  # Set up a domain
75
75
  # Add a domain, add an admin, and grant the admin access to the domain
76
- def setup_domain(domain_name, password)
76
+ def setup_domain(domain_name, password, scheme: nil, rounds: nil)
77
77
  admin = "admin@#{domain_name}"
78
78
  add_domain(domain_name)
79
- add_admin(admin, password)
79
+ add_admin(admin, password, scheme: scheme, rounds: rounds)
80
80
  add_admin_domain(admin, domain_name)
81
81
  end
82
82
 
83
+ # Tear down a domain
84
+ # Delete a domain and delete an admin user for it
85
+ def teardown_domain(domain_name)
86
+ admin = "admin@#{domain_name}"
87
+ delete_domain(domain_name)
88
+ delete_admin(admin)
89
+ end
90
+
83
91
  def show_account_details(user_name, display_password: false)
84
92
  account_check(user_name)
85
93
  mailbox = Mailbox.find(user_name)
@@ -90,7 +98,7 @@ module PostfixAdmin
90
98
  rows << ["Address", mailbox.username]
91
99
  rows << ["Name", mailbox.name]
92
100
  rows << ["Password", mailbox.password] if display_password
93
- rows << ["Quota (MB)", mailbox.quota_mb_str]
101
+ rows << ["Quota (MB)", mailbox.quota_display_str(format: "%.1f")]
94
102
  rows << ["Go to", mail_alias.goto]
95
103
  rows << ["Active", mailbox.active_str]
96
104
 
@@ -125,7 +133,7 @@ module PostfixAdmin
125
133
  puts_table(rows: rows)
126
134
  end
127
135
 
128
- def show_domain
136
+ def show_domains
129
137
  rows = []
130
138
  headings = ["No.", "Domain", "Aliases", "Mailboxes","Max Quota (MB)",
131
139
  "Active", "Description"]
@@ -152,12 +160,12 @@ module PostfixAdmin
152
160
  puts_registered(domain_name, "a domain")
153
161
  end
154
162
 
155
- def change_admin_password(user_name, password)
156
- change_password(Admin, user_name, password)
163
+ def change_admin_password(user_name, password, scheme: nil, rounds: nil)
164
+ change_password(Admin, user_name, password, scheme: scheme, rounds: rounds)
157
165
  end
158
166
 
159
- def change_account_password(user_name, password)
160
- change_password(Mailbox, user_name, password)
167
+ def change_account_password(user_name, password, scheme: nil, rounds: nil)
168
+ change_password(Mailbox, user_name, password, scheme: scheme, rounds: rounds)
161
169
  end
162
170
 
163
171
  def edit_admin(admin_name, options)
@@ -171,7 +179,7 @@ module PostfixAdmin
171
179
  admin.active = options[:active] unless options[:active].nil?
172
180
  admin.save!
173
181
 
174
- puts "Successfully updated #{admin_name}"
182
+ puts "successfully updated #{admin_name}"
175
183
  show_admin_details(admin_name)
176
184
  end
177
185
 
@@ -185,7 +193,7 @@ module PostfixAdmin
185
193
  domain.description = options[:description] if options[:description]
186
194
  domain.save!
187
195
 
188
- puts "Successfully updated #{domain_name}"
196
+ puts "successfully updated #{domain_name}"
189
197
  show_summary(domain_name)
190
198
  end
191
199
 
@@ -194,9 +202,9 @@ module PostfixAdmin
194
202
  puts_deleted(domain_name)
195
203
  end
196
204
 
197
- def show_admin(domain_name = nil)
205
+ def show_admins(domain_name = nil)
198
206
  admins = domain_name ? Admin.select { |a| a.rel_domains.exists?(domain_name) } : Admin.all
199
- headings = %w[No. Admin Domains Active]
207
+ headings = ["No.", "Admin", "Domains", "Active", "Scheme Prefix"]
200
208
 
201
209
  puts_title("Admins")
202
210
  if admins.empty?
@@ -208,45 +216,60 @@ module PostfixAdmin
208
216
  admins.each_with_index do |a, i|
209
217
  no = i + 1
210
218
  domains = a.super_admin? ? 'Super Admin' : a.rel_domains.count
211
- rows << [no.to_s, a.username, domains.to_s, a.active_str]
219
+ rows << [no.to_s, a.username, domains.to_s, a.active_str, a.scheme_prefix]
212
220
  end
213
221
 
214
222
  puts_table(headings: headings, rows: rows)
215
223
  end
216
224
 
217
- def show_address(domain_name)
218
- domain_check(domain_name)
225
+ def show_accounts(domain_name=nil)
226
+ domain_check(domain_name) if domain_name
219
227
 
220
228
  rows = []
221
- mailboxes = Domain.find(domain_name).rel_mailboxes
222
- headings = ["No.", "Email", "Name", "Quota (MB)", "Active", "Maildir"]
223
-
224
- puts_title("Addresses")
229
+ mailboxes = if domain_name
230
+ Domain.find(domain_name).rel_mailboxes
231
+ else
232
+ Mailbox.all
233
+ end
234
+ headings = ["No.", "Email", "Name", "Quota (MB)", "Active",
235
+ "Scheme Prefix", "Maildir"]
236
+
237
+ puts_title("Accounts")
225
238
  if mailboxes.empty?
226
- puts "No addresses"
239
+ puts "No accounts"
227
240
  return
228
241
  end
229
242
 
230
243
  mailboxes.each_with_index do |m, i|
231
244
  no = i + 1
232
- rows << [no.to_s, m.username, m.name, m.quota_mb_str,
233
- m.active_str, m.maildir]
245
+ rows << [no.to_s, m.username, m.name, m.quota_display_str,
246
+ m.active_str, m.scheme_prefix, m.maildir]
234
247
  end
235
248
 
236
249
  puts_table(headings: headings, rows: rows)
237
250
  end
238
251
 
239
- def show_alias(domain_name)
240
- domain_check(domain_name)
241
-
242
- forwards, aliases = Domain.find(domain_name).rel_aliases.partition { |a| a.mailbox? }
252
+ def show_forwards(domain_name=nil)
253
+ domain_check(domain_name) if domain_name
243
254
 
244
- forwards.delete_if do |f|
245
- f.address == f.goto
246
- end
255
+ forwards = if domain_name
256
+ Domain.find(domain_name).rel_aliases.forward
257
+ else
258
+ Alias.forward
259
+ end
247
260
 
248
261
  show_alias_base("Forwards", forwards)
249
- puts
262
+ end
263
+
264
+ def show_aliases(domain_name=nil)
265
+ domain_check(domain_name) if domain_name
266
+
267
+ aliases = if domain_name
268
+ Domain.find(domain_name).rel_aliases.pure
269
+ else
270
+ db_aliases = Alias.pure
271
+ end
272
+
250
273
  show_alias_base("Aliases", aliases)
251
274
  end
252
275
 
@@ -266,10 +289,14 @@ module PostfixAdmin
266
289
  puts_table(rows: rows, headings: %w[No. Domain])
267
290
  end
268
291
 
269
- def add_admin(user_name, password, super_admin = false, scheme = nil)
292
+ def add_admin(user_name, password, super_admin: false,
293
+ scheme: nil, rounds: nil)
270
294
  validate_password(password)
271
295
 
272
- @base.add_admin(user_name, hashed_password(password, scheme))
296
+ h_password = hashed_password(password, user_name: user_name,
297
+ scheme: scheme, rounds: rounds)
298
+ @base.add_admin(user_name, h_password)
299
+
273
300
  if super_admin
274
301
  Admin.find(user_name).super_admin = true
275
302
  puts_registered(user_name, "a super admin")
@@ -288,10 +315,12 @@ module PostfixAdmin
288
315
  puts "#{domain_name} was successfully deleted from #{user_name}"
289
316
  end
290
317
 
291
- def add_account(address, password, scheme = nil, name = nil)
318
+ def add_account(address, password, name: nil, scheme: nil, rounds: nil)
292
319
  validate_password(password)
293
320
 
294
- @base.add_account(address, hashed_password(password, scheme), name: name)
321
+ h_password = hashed_password(password, user_name: address,
322
+ scheme: scheme, rounds: rounds)
323
+ @base.add_account(address, h_password, name: name)
295
324
  puts_registered(address, "an account")
296
325
  end
297
326
 
@@ -301,10 +330,13 @@ module PostfixAdmin
301
330
  end
302
331
 
303
332
  def edit_account(address, options)
333
+ quota = options[:quota]
334
+ raise "Invalid Quota value: #{quota}" if quota && quota <= 0
335
+
304
336
  mailbox_check(address)
305
337
  mailbox = Mailbox.find(address)
306
338
  mailbox.name = options[:name] if options[:name]
307
- mailbox.quota = options[:quota] * KB_TO_MB if options[:quota]
339
+ mailbox.quota_mb = quota if quota
308
340
  mailbox.active = options[:active] unless options[:active].nil?
309
341
  mailbox.save!
310
342
 
@@ -314,7 +346,7 @@ module PostfixAdmin
314
346
  mail_alias.save!
315
347
  end
316
348
 
317
- puts "Successfully updated #{address}"
349
+ puts "successfully updated #{address}"
318
350
  show_account_details(address)
319
351
  end
320
352
 
@@ -325,7 +357,7 @@ module PostfixAdmin
325
357
  mail_alias.active = options[:active] unless options[:active].nil?
326
358
  mail_alias.save or raise "Could not save Alias"
327
359
 
328
- puts "Successfully updated #{address}"
360
+ puts "successfully updated #{address}"
329
361
  show_alias_details(address)
330
362
  end
331
363
 
@@ -428,11 +460,13 @@ module PostfixAdmin
428
460
  end
429
461
 
430
462
  def show_domain_details(domain_name)
431
- show_admin(domain_name)
463
+ show_admins(domain_name)
432
464
  puts
433
- show_address(domain_name)
465
+ show_accounts(domain_name)
434
466
  puts
435
- show_alias(domain_name)
467
+ show_forwards(domain_name)
468
+ puts
469
+ show_aliases(domain_name)
436
470
  end
437
471
 
438
472
  def show_alias_base(title, addresses)
@@ -524,24 +558,44 @@ module PostfixAdmin
524
558
  end
525
559
  end
526
560
 
527
- def change_password(klass, user_name, password)
561
+ def change_password(klass, user_name, password, scheme: nil, rounds: nil)
528
562
  raise Error, "Could not find #{user_name}" unless klass.exists?(user_name)
529
563
 
530
564
  validate_password(password)
531
565
 
532
566
  obj = klass.find(user_name)
567
+ h_password = hashed_password(password, scheme: scheme, rounds: rounds,
568
+ user_name: user_name)
533
569
 
534
- if obj.update(password: hashed_password(password))
535
- puts "the password of #{user_name} was successfully changed."
570
+ if obj.update(password: h_password)
571
+ puts "the password of #{user_name} was successfully updated."
536
572
  else
537
573
  raise "Could not change password of #{klass.name}"
538
574
  end
539
575
  end
540
576
 
541
- def hashed_password(password, in_scheme = nil)
577
+ # The default number of rounds for BLF-CRYPT in `doveadm pw` is 5.
578
+ # However, this method uses 10 rounds by default, similar to
579
+ # the password_hash() function in PHP.
580
+ #
581
+ # https://www.php.net/manual/en/function.password-hash.php
582
+ # <?php
583
+ # echo password_hash("password", PASSWORD_BCRYPT);
584
+ #
585
+ # $2y$10$qzRgjWZWfH4VsNQGvp/DNObFSaMiZxXJSzgXqOOS/qtF68qIhhwFe
586
+ DEFAULT_BLF_CRYPT_ROUNDS = 10
587
+
588
+ # Generate a hashed password
589
+ def hashed_password(password, scheme: nil, rounds: nil, user_name: nil)
542
590
  prefix = @base.config[:passwordhash_prefix]
543
- scheme = in_scheme || @base.config[:scheme]
544
- PostfixAdmin::Doveadm.password(password, scheme, prefix)
591
+ new_scheme = scheme || @base.config[:scheme]
592
+ new_rounds = if rounds
593
+ rounds
594
+ elsif new_scheme == "BLF-CRYPT"
595
+ DEFAULT_BLF_CRYPT_ROUNDS
596
+ end
597
+ PostfixAdmin::Doveadm.password(password, new_scheme, rounds: new_rounds,
598
+ user_name: user_name, prefix: prefix)
545
599
  end
546
600
  end
547
601
  end
@@ -1,33 +1,49 @@
1
-
2
1
  require 'open3'
3
2
  require 'shellwords'
4
3
 
5
4
  module PostfixAdmin
6
5
  class Doveadm
6
+ # doveadm-pw: https://doc.dovecot.org/3.0/man/doveadm-pw.1/
7
+ CMD_DOVEADM_PW = "doveadm pw"
8
+
9
+ # List all supported password schemes
7
10
  def self.schemes
8
- result = `#{self.command_name} -l`
11
+ result = `#{CMD_DOVEADM_PW} -l`
9
12
  result.split
10
13
  end
11
14
 
12
- def self.password(in_password, in_scheme, prefix)
13
- password = Shellwords.escape(in_password)
14
- scheme = Shellwords.escape(in_scheme)
15
- _stdin, stdout, stderr = Open3.popen3("#{self.command_name} -s #{scheme} -p #{password}")
15
+ # Generate a password hash using `doveadm pw` command
16
+ def self.password(password, scheme, rounds: nil, user_name: nil,
17
+ prefix: true)
18
+ escaped_password = Shellwords.escape(password)
19
+ escaped_scheme = Shellwords.escape(scheme)
16
20
 
17
- if stderr.readlines.to_s =~ /Fatal:/
18
- raise Error, stderr.readlines
19
- else
20
- res = stdout.readlines.first.chomp
21
+ cmd = "#{CMD_DOVEADM_PW} -s #{escaped_scheme} -p #{escaped_password}"
22
+
23
+ # DIGEST-MD5 requires -u option (user name)
24
+ if scheme == "DIGEST-MD5"
25
+ escaped_user_name = Shellwords.escape(user_name)
26
+ cmd << " -u #{escaped_user_name}"
27
+ end
28
+
29
+ if rounds
30
+ escaped_rounds = Shellwords.escape(rounds.to_s)
31
+ cmd << " -r #{rounds}"
32
+ end
33
+
34
+ output, error, status = Open3.capture3(cmd)
35
+
36
+ if status.success?
37
+ res = output.chomp
21
38
  if prefix
22
39
  res
23
40
  else
24
- res.gsub("{#{scheme}}", "")
41
+ # Remove the prefix
42
+ res.gsub("{#{escaped_scheme}}", "")
25
43
  end
44
+ else
45
+ raise Error, "#{CMD_DOVEADM_PW}: #{error}"
26
46
  end
27
47
  end
28
-
29
- def self.command_name
30
- "doveadm pw"
31
- end
32
48
  end
33
49
  end
@@ -1,16 +1,34 @@
1
- require 'postfix_admin/concerns/dovecot_cram_md5_password'
1
+ require 'postfix_admin/models/concerns/has_password'
2
2
 
3
3
  module PostfixAdmin
4
4
  class Admin < ApplicationRecord
5
+ # version: 1841
6
+ # > describe admin;
7
+ # +----------------+--------------+------+-----+---------------------+-------+
8
+ # | Field | Type | Null | Key | Default | Extra |
9
+ # +----------------+--------------+------+-----+---------------------+-------+
10
+ # | username | varchar(255) | NO | PRI | NULL | |
11
+ # | password | varchar(255) | NO | | NULL | |
12
+ # | created | datetime | NO | | 2000-01-01 00:00:00 | |
13
+ # | modified | datetime | NO | | 2000-01-01 00:00:00 | |
14
+ # | active | tinyint(1) | NO | | 1 | |
15
+ # | superadmin | tinyint(1) | NO | | 0 | |
16
+ # | phone | varchar(30) | NO | | | |
17
+ # | email_other | varchar(255) | NO | | | |
18
+ # | token | varchar(255) | NO | | | |
19
+ # | token_validity | datetime | NO | | 2000-01-01 00:00:00 | |
20
+ # +----------------+--------------+------+-----+---------------------+-------+
21
+
5
22
  self.table_name = :admin
6
23
  self.primary_key = :username
7
24
 
8
- include DovecotCramMD5Password
25
+ include HasPassword
9
26
 
10
27
  validates :username, presence: true, uniqueness: { case_sensitive: false },
11
28
  format: { with: RE_EMAIL_LIKE_WITH_ANCHORS,
12
29
  message: "must be a valid email address" }
13
30
 
31
+ # Admin <-> DomainAdmin <-> Domain
14
32
  has_many :domain_admins, foreign_key: :username, dependent: :delete_all
15
33
  has_many :rel_domains, through: :domain_admins
16
34