postfix_admin 0.3.0 → 0.3.1

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