postfix_admin 0.2.1 → 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 +4 -4
- data/.github/workflows/ci.yml +52 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +20 -0
- data/CHANGELOG.md +26 -12
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/README.md +26 -20
- data/Rakefile +48 -1
- data/bin/console +6 -2
- data/db/reset.rb +7 -0
- data/db/seeds.rb +26 -0
- data/docker-admin/config.local.php +6 -1
- data/docker-app/Dockerfile +2 -6
- data/docker-app/my.cnf +2 -2
- data/docker-compose.yml +20 -20
- data/lib/postfix_admin/base.rb +119 -112
- data/lib/postfix_admin/cli.rb +267 -158
- data/lib/postfix_admin/doveadm.rb +32 -20
- data/lib/postfix_admin/{admin.rb → models/admin.rb} +25 -2
- data/lib/postfix_admin/{alias.rb → models/alias.rb} +16 -26
- data/lib/postfix_admin/{application_record.rb → models/application_record.rb} +1 -1
- data/lib/postfix_admin/{concerns → models/concerns}/existing_timestamp.rb +1 -2
- data/lib/postfix_admin/models/concerns/has_password.rb +16 -0
- data/lib/postfix_admin/models/domain.rb +112 -0
- data/lib/postfix_admin/models/domain_admin.rb +19 -0
- data/lib/postfix_admin/models/log.rb +20 -0
- data/lib/postfix_admin/models/mailbox.rb +126 -0
- data/lib/postfix_admin/models/quota2.rb +18 -0
- data/lib/postfix_admin/models.rb +8 -9
- data/lib/postfix_admin/runner.rb +91 -28
- data/lib/postfix_admin/version.rb +1 -1
- data/postfix_admin.gemspec +12 -10
- metadata +61 -34
- data/.github/workflows/ruby.yml +0 -37
- data/docker-app/docker-entrypoint.sh +0 -5
- data/docker-app-2.5/Dockerfile +0 -15
- data/lib/postfix_admin/concerns/dovecot_cram_md5_password.rb +0 -30
- data/lib/postfix_admin/domain.rb +0 -98
- data/lib/postfix_admin/domain_admin.rb +0 -8
- data/lib/postfix_admin/log.rb +0 -5
- data/lib/postfix_admin/mail_domain.rb +0 -9
- data/lib/postfix_admin/mailbox.rb +0 -89
- data/lib/postfix_admin/quota.rb +0 -6
- /data/lib/postfix_admin/{concerns → models/concerns}/.keep +0 -0
data/lib/postfix_admin/cli.rb
CHANGED
@@ -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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
49
|
-
|
50
|
-
show_alias(name)
|
56
|
+
# domain name
|
57
|
+
show_domain_details(name)
|
51
58
|
else
|
52
|
-
|
53
|
-
|
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
|
61
|
-
|
62
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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 "
|
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 "
|
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
|
205
|
+
def show_admins(domain_name = nil)
|
189
206
|
admins = domain_name ? Admin.select { |a| a.rel_domains.exists?(domain_name) } : Admin.all
|
190
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
205
|
-
domain_check(domain_name)
|
225
|
+
def show_accounts(domain_name=nil)
|
226
|
+
domain_check(domain_name) if domain_name
|
206
227
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
237
|
+
puts_title("Accounts")
|
238
|
+
if mailboxes.empty?
|
239
|
+
puts "No accounts"
|
240
|
+
return
|
219
241
|
end
|
220
|
-
end
|
221
242
|
|
222
|
-
|
223
|
-
|
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
|
-
|
252
|
+
def show_forwards(domain_name=nil)
|
253
|
+
domain_check(domain_name) if domain_name
|
226
254
|
|
227
|
-
forwards
|
228
|
-
|
229
|
-
|
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
|
280
|
+
puts "\nNo domains for #{user_name}"
|
239
281
|
return
|
240
282
|
end
|
241
283
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
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
|
292
|
+
def add_admin(user_name, password, super_admin: false,
|
293
|
+
scheme: nil, rounds: nil)
|
250
294
|
validate_password(password)
|
251
295
|
|
252
|
-
|
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
|
318
|
+
def add_account(address, password, name: nil, scheme: nil, rounds: nil)
|
272
319
|
validate_password(password)
|
273
320
|
|
274
|
-
|
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.
|
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 "
|
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 "
|
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
|
-
|
330
|
-
|
331
|
-
|
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
|
-
|
371
|
-
|
372
|
-
puts " No #{title.downcase}"
|
373
|
-
next
|
374
|
-
end
|
473
|
+
rows = []
|
474
|
+
puts_title(title)
|
375
475
|
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|
-
|
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
|
413
|
-
puts
|
520
|
+
def puts_table(args)
|
521
|
+
puts Terminal::Table.new(args)
|
414
522
|
end
|
415
523
|
|
416
|
-
def
|
417
|
-
puts "
|
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:
|
466
|
-
puts "the password of #{user_name} was successfully
|
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
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
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
|
-
|
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
|
-
|
488
|
-
|
489
|
-
|
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
|