postfix_admin 0.2.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +52 -0
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +20 -0
  5. data/CHANGELOG.md +26 -12
  6. data/Gemfile +1 -1
  7. data/LICENSE +1 -1
  8. data/README.md +26 -20
  9. data/Rakefile +48 -1
  10. data/bin/console +6 -2
  11. data/db/reset.rb +7 -0
  12. data/db/seeds.rb +26 -0
  13. data/docker-admin/config.local.php +6 -1
  14. data/docker-app/Dockerfile +2 -6
  15. data/docker-app/my.cnf +2 -2
  16. data/docker-compose.yml +20 -20
  17. data/lib/postfix_admin/base.rb +119 -112
  18. data/lib/postfix_admin/cli.rb +267 -158
  19. data/lib/postfix_admin/doveadm.rb +32 -20
  20. data/lib/postfix_admin/{admin.rb → models/admin.rb} +25 -2
  21. data/lib/postfix_admin/{alias.rb → models/alias.rb} +16 -26
  22. data/lib/postfix_admin/{application_record.rb → models/application_record.rb} +1 -1
  23. data/lib/postfix_admin/{concerns → models/concerns}/existing_timestamp.rb +1 -2
  24. data/lib/postfix_admin/models/concerns/has_password.rb +16 -0
  25. data/lib/postfix_admin/models/domain.rb +112 -0
  26. data/lib/postfix_admin/models/domain_admin.rb +19 -0
  27. data/lib/postfix_admin/models/log.rb +20 -0
  28. data/lib/postfix_admin/models/mailbox.rb +126 -0
  29. data/lib/postfix_admin/models/quota2.rb +18 -0
  30. data/lib/postfix_admin/models.rb +8 -9
  31. data/lib/postfix_admin/runner.rb +91 -28
  32. data/lib/postfix_admin/version.rb +1 -1
  33. data/postfix_admin.gemspec +12 -10
  34. metadata +61 -34
  35. data/.github/workflows/ruby.yml +0 -37
  36. data/docker-app/docker-entrypoint.sh +0 -5
  37. data/docker-app-2.5/Dockerfile +0 -15
  38. data/lib/postfix_admin/concerns/dovecot_cram_md5_password.rb +0 -30
  39. data/lib/postfix_admin/domain.rb +0 -98
  40. data/lib/postfix_admin/domain_admin.rb +0 -8
  41. data/lib/postfix_admin/log.rb +0 -5
  42. data/lib/postfix_admin/mail_domain.rb +0 -9
  43. data/lib/postfix_admin/mailbox.rb +0 -89
  44. data/lib/postfix_admin/quota.rb +0 -6
  45. /data/lib/postfix_admin/{concerns → models/concerns}/.keep +0 -0
@@ -1,6 +1,7 @@
1
1
  require 'yaml'
2
2
  require 'postfix_admin'
3
3
  require 'postfix_admin/doveadm'
4
+ require 'terminal-table'
4
5
 
5
6
  module PostfixAdmin
6
7
  class CLI
@@ -29,127 +30,142 @@ module PostfixAdmin
29
30
  name = name.downcase if name
30
31
 
31
32
  if name =~ /@/
33
+ # address like argument
32
34
  if Admin.exists?(name)
33
- show_admin_details(name)
34
- end
35
-
36
- if Mailbox.exists?(name)
37
- show_account_details(name)
35
+ # admin
36
+ show_admin_details(name, display_password: true)
37
+ puts
38
+ show_admin_domain(name)
39
+ elsif Mailbox.exists?(name)
40
+ # mailbox
41
+ show_account_details(name, display_password: true)
38
42
  elsif Alias.exists?(name)
43
+ # alias
39
44
  show_alias_details(name)
45
+ else
46
+ raise Error, "Could not find admin/mailbox/alias #{name}"
40
47
  end
41
48
 
42
49
  return
43
50
  end
44
51
 
45
52
  show_summary(name)
53
+ puts
46
54
 
47
55
  if name
48
- show_admin(name)
49
- show_address(name)
50
- show_alias(name)
56
+ # domain name
57
+ show_domain_details(name)
51
58
  else
52
- show_domain
53
- show_admin
59
+ # no argument: show all domains and admins
60
+ show_domains
61
+ puts
62
+ show_admins
54
63
  end
55
64
  end
56
65
 
57
66
  def show_summary(domain_name = nil)
58
- title = "Summary"
59
67
  if domain_name
60
- domain_name = domain_name.downcase
61
- domain_check(domain_name)
62
- title = "Summary of #{domain_name}"
63
- end
64
-
65
- report(title) do
66
- if domain_name
67
- domain = Domain.find(domain_name)
68
- puts "Mailboxes : %4d / %4s" % [domain.rel_mailboxes.count, max_str(domain.mailboxes)]
69
- puts "Aliases : %4d / %4s" % [domain.pure_aliases.count, max_str(domain.aliases)]
70
- puts "Max Quota : %4d MB" % domain.maxquota
71
- puts "Active : %3s" % domain.active_str
72
- else
73
- puts "Domains : %4d" % Domain.without_all.count
74
- puts "Admins : %4d" % Admin.count
75
- puts "Mailboxes : %4d" % Mailbox.count
76
- puts "Aliases : %4d" % Alias.pure.count
77
- end
68
+ show_domain_summary(domain_name)
69
+ else
70
+ show_general_summary
78
71
  end
79
72
  end
80
73
 
81
- def setup_domain(domain_name, password)
74
+ # Set up a domain
75
+ # Add a domain, add an admin, and grant the admin access to the domain
76
+ def setup_domain(domain_name, password, scheme: nil, rounds: nil)
82
77
  admin = "admin@#{domain_name}"
83
78
  add_domain(domain_name)
84
- add_admin(admin, password)
79
+ add_admin(admin, password, scheme: scheme, rounds: rounds)
85
80
  add_admin_domain(admin, domain_name)
86
81
  end
87
82
 
88
- def show_account_details(user_name)
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
+
91
+ def show_account_details(user_name, display_password: false)
89
92
  account_check(user_name)
90
93
  mailbox = Mailbox.find(user_name)
91
94
  mail_alias = Alias.find(user_name)
92
95
 
93
- report("Mailbox") do
94
- puts "Address : %s" % mailbox.username
95
- puts "Name : %s" % mailbox.name
96
- puts "Password : %s" % mailbox.password
97
- puts "Quota : %d MB" % max_str(mailbox.quota / KB_TO_MB)
98
- puts "Go to : %s" % mail_alias.goto
99
- puts "Active : %s" % mailbox.active_str
100
- end
96
+ rows = []
97
+ puts_title("Mailbox")
98
+ rows << ["Address", mailbox.username]
99
+ rows << ["Name", mailbox.name]
100
+ rows << ["Password", mailbox.password] if display_password
101
+ rows << ["Quota (MB)", mailbox.quota_display_str(format: "%.1f")]
102
+ rows << ["Go to", mail_alias.goto]
103
+ rows << ["Active", mailbox.active_str]
104
+
105
+ puts_table(rows: rows)
101
106
  end
102
107
 
103
- def show_admin_details(name)
108
+ def show_admin_details(name, display_password: false)
104
109
  admin_check(name)
105
110
  admin = Admin.find(name)
106
111
 
107
- report("Admin") do
108
- puts "Name : %s" % admin.username
109
- puts "Password : %s" % admin.password
110
- puts "Domains : %s" % (admin.super_admin? ? "ALL" : admin.rel_domains.count)
111
- puts "Role : %s" % (admin.super_admin? ? "Super admin" : "Admin")
112
- puts "Active : %s" % admin.active_str
113
- end
112
+ rows = []
113
+ puts_title("Admin")
114
+ rows << ["Name", admin.username]
115
+ rows << ["Password", admin.password] if display_password
116
+ rows << ["Domains", admin.super_admin? ? "ALL" : admin.rel_domains.count.to_s]
117
+ rows << ["Role", admin.super_admin? ? "Super Admin" : "Standard Admin"]
118
+ rows << ["Active", admin.active_str]
119
+
120
+ puts_table(rows: rows)
114
121
  end
115
122
 
116
123
  def show_alias_details(name)
117
124
  alias_check(name)
118
125
  mail_alias = Alias.find(name)
119
- report("Alias") do
120
- puts "Address : %s" % mail_alias.address
121
- puts "Go to : %s" % mail_alias.goto
122
- puts "Active : %s" % mail_alias.active_str
123
- end
126
+
127
+ rows = []
128
+ puts_title("Alias")
129
+ rows << ["Address", mail_alias.address]
130
+ rows << ["Go to", mail_alias.goto]
131
+ rows << ["Active", mail_alias.active_str]
132
+
133
+ puts_table(rows: rows)
124
134
  end
125
135
 
126
- def show_domain
127
- index = " No. Domain Aliases Mailboxes Quota (MB) Active"
128
- report('Domains', index) do
129
- if Domain.without_all.empty?
130
- puts " No domains"
131
- next
132
- end
136
+ def show_domains
137
+ rows = []
138
+ headings = ["No.", "Domain", "Aliases", "Mailboxes","Max Quota (MB)",
139
+ "Active", "Description"]
133
140
 
134
- Domain.without_all.each_with_index do |d, i|
135
- puts "%4d %-30s %3d /%3s %3d /%3s %10d %-3s" %
136
- [i+1, d.domain, d.pure_aliases.count, max_str(d.aliases),
137
- d.rel_mailboxes.count, max_str(d.mailboxes), d.maxquota, d.active_str]
138
- end
141
+ puts_title("Domains")
142
+ if Domain.without_all.empty?
143
+ puts "No domains"
144
+ return
145
+ end
146
+
147
+ Domain.without_all.each_with_index do |d, i|
148
+ no = i + 1
149
+ aliases_str = "%4d / %4s" % [d.pure_aliases.count, d.aliases_str]
150
+ mailboxes_str = "%4d / %4s" % [d.rel_mailboxes.count, d.mailboxes_str]
151
+ rows << [no.to_s, d.domain, aliases_str, mailboxes_str,
152
+ d.maxquota_str, d.active_str, d.description]
139
153
  end
154
+
155
+ puts_table(headings: headings, rows: rows)
140
156
  end
141
157
 
142
- def add_domain(domain_name)
143
- @base.add_domain(domain_name)
158
+ def add_domain(domain_name, description: nil)
159
+ @base.add_domain(domain_name, description: description)
144
160
  puts_registered(domain_name, "a domain")
145
161
  end
146
162
 
147
- def change_admin_password(user_name, password)
148
- 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)
149
165
  end
150
166
 
151
- def change_account_password(user_name, password)
152
- 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)
153
169
  end
154
170
 
155
171
  def edit_admin(admin_name, options)
@@ -163,7 +179,7 @@ module PostfixAdmin
163
179
  admin.active = options[:active] unless options[:active].nil?
164
180
  admin.save!
165
181
 
166
- puts "Successfully updated #{admin_name}"
182
+ puts "successfully updated #{admin_name}"
167
183
  show_admin_details(admin_name)
168
184
  end
169
185
 
@@ -174,9 +190,10 @@ module PostfixAdmin
174
190
  domain.mailboxes = options[:mailboxes] if options[:mailboxes]
175
191
  domain.maxquota = options[:maxquota] if options[:maxquota]
176
192
  domain.active = options[:active] unless options[:active].nil?
193
+ domain.description = options[:description] if options[:description]
177
194
  domain.save!
178
195
 
179
- puts "Successfully updated #{domain_name}"
196
+ puts "successfully updated #{domain_name}"
180
197
  show_summary(domain_name)
181
198
  end
182
199
 
@@ -185,71 +202,101 @@ module PostfixAdmin
185
202
  puts_deleted(domain_name)
186
203
  end
187
204
 
188
- def show_admin(domain_name = nil)
205
+ def show_admins(domain_name = nil)
189
206
  admins = domain_name ? Admin.select { |a| a.rel_domains.exists?(domain_name) } : Admin.all
190
- index = " No. Admin Domains Active"
191
- report("Admins", index) do
192
- if admins.empty?
193
- puts " No admins"
194
- next
195
- end
207
+ headings = ["No.", "Admin", "Domains", "Active", "Scheme Prefix"]
196
208
 
197
- admins.each_with_index do |a, i|
198
- domains = a.super_admin? ? 'Super admin' : a.rel_domains.count
199
- puts "%4d %-40s %11s %-3s" % [i+1, a.username, domains, a.active_str]
200
- end
209
+ puts_title("Admins")
210
+ if admins.empty?
211
+ puts "No admins"
212
+ return
213
+ end
214
+
215
+ rows = []
216
+ admins.each_with_index do |a, i|
217
+ no = i + 1
218
+ domains = a.super_admin? ? 'Super Admin' : a.rel_domains.count
219
+ rows << [no.to_s, a.username, domains.to_s, a.active_str, a.scheme_prefix]
201
220
  end
221
+
222
+ puts_table(headings: headings, rows: rows)
202
223
  end
203
224
 
204
- def show_address(domain_name)
205
- domain_check(domain_name)
225
+ def show_accounts(domain_name=nil)
226
+ domain_check(domain_name) if domain_name
206
227
 
207
- mailboxes = Domain.find(domain_name).rel_mailboxes
208
- index = " No. Email Name Quota (MB) Active Maildir"
209
- report("Addresses", index) do
210
- if mailboxes.empty?
211
- puts " No addresses"
212
- next
213
- end
228
+ rows = []
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"]
214
236
 
215
- mailboxes.each_with_index do |m, i|
216
- quota = m.quota.to_f/ KB_TO_MB.to_f
217
- puts "%4d %-30s %-20s %10s %-3s %s" % [i+1, m.username, m.name, max_str(quota.to_i), m.active_str, m.maildir]
218
- end
237
+ puts_title("Accounts")
238
+ if mailboxes.empty?
239
+ puts "No accounts"
240
+ return
219
241
  end
220
- end
221
242
 
222
- def show_alias(domain_name)
223
- domain_check(domain_name)
243
+ mailboxes.each_with_index do |m, i|
244
+ no = i + 1
245
+ rows << [no.to_s, m.username, m.name, m.quota_display_str,
246
+ m.active_str, m.scheme_prefix, m.maildir]
247
+ end
248
+
249
+ puts_table(headings: headings, rows: rows)
250
+ end
224
251
 
225
- 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
226
254
 
227
- forwards.delete_if do |f|
228
- f.address == f.goto
229
- end
255
+ forwards = if domain_name
256
+ Domain.find(domain_name).rel_aliases.forward
257
+ else
258
+ Alias.forward
259
+ end
230
260
 
231
261
  show_alias_base("Forwards", forwards)
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
+
232
273
  show_alias_base("Aliases", aliases)
233
274
  end
234
275
 
235
276
  def show_admin_domain(user_name)
236
277
  admin = Admin.find(user_name)
278
+ puts_title("Admin Domains (#{user_name})")
237
279
  if admin.rel_domains.empty?
238
- puts "\nNo domain in database"
280
+ puts "\nNo domains for #{user_name}"
239
281
  return
240
282
  end
241
283
 
242
- report("Domains (#{user_name})", " No. Domain") do
243
- admin.rel_domains.each_with_index do |d, i|
244
- puts "%4d %-30s" % [i + 1, d.domain]
245
- end
284
+ rows = []
285
+ admin.rel_domains.each_with_index do |d, i|
286
+ no = i + 1
287
+ rows << [no.to_s, d.domain]
246
288
  end
289
+ puts_table(rows: rows, headings: %w[No. Domain])
247
290
  end
248
291
 
249
- 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)
250
294
  validate_password(password)
251
295
 
252
- @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
+
253
300
  if super_admin
254
301
  Admin.find(user_name).super_admin = true
255
302
  puts_registered(user_name, "a super admin")
@@ -268,12 +315,13 @@ module PostfixAdmin
268
315
  puts "#{domain_name} was successfully deleted from #{user_name}"
269
316
  end
270
317
 
271
- def add_account(address, password, scheme = nil, name = nil)
318
+ def add_account(address, password, name: nil, scheme: nil, rounds: nil)
272
319
  validate_password(password)
273
320
 
274
- @base.add_account(address, hashed_password(password, scheme), name)
321
+ h_password = hashed_password(password, user_name: address,
322
+ scheme: scheme, rounds: rounds)
323
+ @base.add_account(address, h_password, name: name)
275
324
  puts_registered(address, "an account")
276
- show_account_details(address)
277
325
  end
278
326
 
279
327
  def add_alias(address, goto)
@@ -282,10 +330,13 @@ module PostfixAdmin
282
330
  end
283
331
 
284
332
  def edit_account(address, options)
333
+ quota = options[:quota]
334
+ raise "Invalid Quota value: #{quota}" if quota && quota <= 0
335
+
285
336
  mailbox_check(address)
286
337
  mailbox = Mailbox.find(address)
287
338
  mailbox.name = options[:name] if options[:name]
288
- mailbox.quota = options[:quota] * KB_TO_MB if options[:quota]
339
+ mailbox.quota_mb = quota if quota
289
340
  mailbox.active = options[:active] unless options[:active].nil?
290
341
  mailbox.save!
291
342
 
@@ -295,7 +346,7 @@ module PostfixAdmin
295
346
  mail_alias.save!
296
347
  end
297
348
 
298
- puts "Successfully updated #{address}"
349
+ puts "successfully updated #{address}"
299
350
  show_account_details(address)
300
351
  end
301
352
 
@@ -306,7 +357,7 @@ module PostfixAdmin
306
357
  mail_alias.active = options[:active] unless options[:active].nil?
307
358
  mail_alias.save or raise "Could not save Alias"
308
359
 
309
- puts "Successfully updated #{address}"
360
+ puts "successfully updated #{address}"
310
361
  show_alias_details(address)
311
362
  end
312
363
 
@@ -325,11 +376,25 @@ module PostfixAdmin
325
376
  puts_deleted(address)
326
377
  end
327
378
 
328
- def log
329
- Log.all.each do |l|
330
- time = l.timestamp.strftime("%Y-%m-%d %X %Z")
331
- puts "#{time} #{l.username} #{l.domain} #{l.action} #{l.data}"
379
+ def log(domain: nil, last: nil)
380
+ headings = %w[Timestamp Admin Domain Action Data]
381
+ rows = []
382
+
383
+ logs = if domain
384
+ Log.where(domain: domain)
385
+ else
386
+ Log.all
387
+ end
388
+
389
+ logs = logs.last(last) if last
390
+
391
+ logs.each do |l|
392
+ # TODO: Consider if zone should be included ('%Z').
393
+ time = l.timestamp.strftime("%Y-%m-%d %X")
394
+ rows << [time, l.username, l.domain, l.action, l.data]
332
395
  end
396
+
397
+ puts_table(headings: headings, rows: rows)
333
398
  end
334
399
 
335
400
  def dump
@@ -366,17 +431,60 @@ module PostfixAdmin
366
431
 
367
432
  private
368
433
 
434
+ def show_general_summary
435
+ rows = []
436
+ title = "Summary"
437
+ rows << ["Domains", Domain.without_all.count]
438
+ rows << ["Admins", Admin.count]
439
+ rows << ["Mailboxes", Mailbox.count]
440
+ rows << ["Aliases", Alias.pure.count]
441
+
442
+ puts_title(title)
443
+ puts_table(rows: rows)
444
+ end
445
+
446
+ def show_domain_summary(domain_name)
447
+ domain_name = domain_name.downcase
448
+ domain_check(domain_name)
449
+
450
+ rows = []
451
+ domain = Domain.find(domain_name)
452
+ rows << ["Mailboxes", "%4d / %4s" % [domain.rel_mailboxes.count, domain.mailboxes_str]]
453
+ rows << ["Aliases", "%4d / %4s" % [domain.pure_aliases.count, domain.aliases_str]]
454
+ rows << ["Max Quota (MB)", domain.maxquota_str]
455
+ rows << ["Active", domain.active_str]
456
+ rows << ["Description", domain.description]
457
+
458
+ puts_title(domain_name)
459
+ puts_table(rows: rows)
460
+ end
461
+
462
+ def show_domain_details(domain_name)
463
+ show_admins(domain_name)
464
+ puts
465
+ show_accounts(domain_name)
466
+ puts
467
+ show_forwards(domain_name)
468
+ puts
469
+ show_aliases(domain_name)
470
+ end
471
+
369
472
  def show_alias_base(title, addresses)
370
- report(title, " No. Address Active Go to") do
371
- if addresses.empty?
372
- puts " No #{title.downcase}"
373
- next
374
- end
473
+ rows = []
474
+ puts_title(title)
375
475
 
376
- addresses.each_with_index do |a, i|
377
- puts "%4d %-40s %-3s %s" % [i+1, a.address, a.active_str, a.goto]
378
- end
476
+ if addresses.empty?
477
+ puts "No #{title.downcase}"
478
+ return
379
479
  end
480
+
481
+ headings = ["No.", "Address", "Active", "Go to"]
482
+ addresses.each_with_index do |a, i|
483
+ no = i + 1
484
+ rows << [no.to_s, a.address, a.active_str, a.goto]
485
+ end
486
+
487
+ puts_table(headings: headings, rows: rows)
380
488
  end
381
489
 
382
490
  def puts_registered(name, as_str)
@@ -388,7 +496,7 @@ module PostfixAdmin
388
496
  end
389
497
 
390
498
  def config_file
391
- config_file = File.expand_path(CLI.config_file)
499
+ File.expand_path(CLI.config_file)
392
500
  end
393
501
 
394
502
  def load_config
@@ -409,17 +517,12 @@ module PostfixAdmin
409
517
  File.chmod(0600, file)
410
518
  end
411
519
 
412
- def print_line
413
- puts "-"*120
520
+ def puts_table(args)
521
+ puts Terminal::Table.new(args)
414
522
  end
415
523
 
416
- def report(title, index = nil)
417
- puts "\n[#{title}]"
418
- print_line if index
419
- puts index if index
420
- print_line
421
- yield
422
- print_line
524
+ def puts_title(title)
525
+ puts "| #{title} |"
423
526
  end
424
527
 
425
528
  def account_check(user_name)
@@ -455,38 +558,44 @@ module PostfixAdmin
455
558
  end
456
559
  end
457
560
 
458
- def change_password(klass, user_name, password)
561
+ def change_password(klass, user_name, password, scheme: nil, rounds: nil)
459
562
  raise Error, "Could not find #{user_name}" unless klass.exists?(user_name)
460
563
 
461
564
  validate_password(password)
462
565
 
463
566
  obj = klass.find(user_name)
567
+ h_password = hashed_password(password, scheme: scheme, rounds: rounds,
568
+ user_name: user_name)
464
569
 
465
- if obj.update(password: hashed_password(password))
466
- 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."
467
572
  else
468
573
  raise "Could not change password of #{klass.name}"
469
574
  end
470
575
  end
471
576
 
472
- def max_str(value)
473
- case value
474
- when 0
475
- '--'
476
- when -1
477
- '0'
478
- else
479
- value.to_s
480
- end
481
- end
482
-
483
- private
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
484
587
 
485
- def hashed_password(password, in_scheme = nil)
588
+ # Generate a hashed password
589
+ def hashed_password(password, scheme: nil, rounds: nil, user_name: nil)
486
590
  prefix = @base.config[:passwordhash_prefix]
487
- scheme = in_scheme || @base.config[:scheme]
488
- puts "scheme: #{scheme}"
489
- 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)
490
599
  end
491
600
  end
492
601
  end