mailshears 0.0.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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +32 -0
  3. data/bin/install-fixtures.sh +27 -0
  4. data/bin/mailshears +124 -0
  5. data/doc/LICENSE +661 -0
  6. data/doc/TODO +16 -0
  7. data/doc/mailshears.example.conf.yml +37 -0
  8. data/doc/man1/mailshears.1 +184 -0
  9. data/lib/common/agendav_plugin.rb +54 -0
  10. data/lib/common/configuration.rb +116 -0
  11. data/lib/common/davical_plugin.rb +104 -0
  12. data/lib/common/domain.rb +64 -0
  13. data/lib/common/dovecot_plugin.rb +130 -0
  14. data/lib/common/errors.rb +15 -0
  15. data/lib/common/exit_codes.rb +9 -0
  16. data/lib/common/filesystem.rb +43 -0
  17. data/lib/common/plugin.rb +238 -0
  18. data/lib/common/postfixadmin_plugin.rb +180 -0
  19. data/lib/common/roundcube_plugin.rb +96 -0
  20. data/lib/common/runner.rb +73 -0
  21. data/lib/common/user.rb +120 -0
  22. data/lib/common/user_interface.rb +53 -0
  23. data/lib/mailshears.rb +7 -0
  24. data/lib/mv/mv_dummy_runner.rb +45 -0
  25. data/lib/mv/mv_plugin.rb +40 -0
  26. data/lib/mv/mv_runner.rb +56 -0
  27. data/lib/mv/plugins/agendav.rb +46 -0
  28. data/lib/mv/plugins/davical.rb +43 -0
  29. data/lib/mv/plugins/dovecot.rb +64 -0
  30. data/lib/mv/plugins/postfixadmin.rb +70 -0
  31. data/lib/mv/plugins/roundcube.rb +44 -0
  32. data/lib/prune/plugins/agendav.rb +13 -0
  33. data/lib/prune/plugins/davical.rb +13 -0
  34. data/lib/prune/plugins/dovecot.rb +11 -0
  35. data/lib/prune/plugins/postfixadmin.rb +13 -0
  36. data/lib/prune/plugins/roundcube.rb +14 -0
  37. data/lib/prune/prune_dummy_runner.rb +44 -0
  38. data/lib/prune/prune_plugin.rb +66 -0
  39. data/lib/prune/prune_runner.rb +34 -0
  40. data/lib/rm/plugins/agendav.rb +38 -0
  41. data/lib/rm/plugins/davical.rb +38 -0
  42. data/lib/rm/plugins/dovecot.rb +48 -0
  43. data/lib/rm/plugins/postfixadmin.rb +114 -0
  44. data/lib/rm/plugins/roundcube.rb +42 -0
  45. data/lib/rm/rm_dummy_runner.rb +39 -0
  46. data/lib/rm/rm_plugin.rb +77 -0
  47. data/lib/rm/rm_runner.rb +51 -0
  48. data/mailshears.gemspec +39 -0
  49. data/test/mailshears.test.conf.yml +36 -0
  50. data/test/mailshears_test.rb +250 -0
  51. data/test/sql/agendav-fixtures.sql +9 -0
  52. data/test/sql/agendav.sql +157 -0
  53. data/test/sql/davical-fixtures.sql +23 -0
  54. data/test/sql/davical.sql +4371 -0
  55. data/test/sql/postfixadmin-fixtures.sql +48 -0
  56. data/test/sql/postfixadmin.sql +737 -0
  57. data/test/sql/roundcube-fixtures.sql +4 -0
  58. data/test/sql/roundcube.sql +608 -0
  59. data/test/test_mv.rb +174 -0
  60. data/test/test_prune.rb +121 -0
  61. data/test/test_rm.rb +154 -0
  62. metadata +133 -0
data/test/test_mv.rb ADDED
@@ -0,0 +1,174 @@
1
+ # WARNING: Test output is dependent on the order these classes include
2
+ # certain modules; i.e. on the 'require' order.
3
+ require 'common/domain'
4
+ require 'common/user'
5
+ require 'mailshears_test'
6
+ require 'mv/plugins/agendav'
7
+ require 'mv/plugins/davical'
8
+ require 'mv/plugins/dovecot'
9
+ require 'mv/plugins/postfixadmin'
10
+ require 'mv/plugins/roundcube'
11
+ require 'mv/mv_runner'
12
+
13
+
14
+ class TestMv < MailshearsTest
15
+
16
+ def test_mv_once
17
+ cfg = configuration()
18
+ src = User.new("alice@example.com")
19
+ dst = User.new("alice@example.net")
20
+
21
+ output_buffer = StringIO.new()
22
+
23
+ $stdout = output_buffer
24
+ MvPlugin.run(cfg, src, dst)
25
+ $stdout = STDOUT
26
+
27
+ actual = output_buffer.string()
28
+
29
+ expected =
30
+ "AgendavMv - Source user alice@example.com not found.\n" +
31
+ "DavicalMv - Moved user alice@example.com (Principal ID: 1) " +
32
+ "to alice@example.net (Principal ID: 1).\n" +
33
+ "DovecotMv - Moved user alice@example.com " +
34
+ "(#{cfg.dovecot_mail_root()}/example.com/alice) to " +
35
+ "alice@example.net (#{cfg.dovecot_mail_root()}/example.net/alice).\n" +
36
+ "PostfixadminMv - Moved user alice@example.com to alice@example.net.\n" +
37
+ "RoundcubeMv - Moved user alice@example.com (User ID: 1) to " +
38
+ "alice@example.net (User ID: 1).\n"
39
+
40
+ assert_equal(expected, actual)
41
+
42
+ # Now check the database.
43
+
44
+ amv = AgendavMv.new(cfg)
45
+ actual = amv.list_users()
46
+ expected = [User.new('adam@example.net'),User.new('booger@example.com')]
47
+ assert_equal(expected.sort(), actual.sort())
48
+
49
+ dmv = DavicalMv.new(cfg)
50
+ actual = dmv.list_users()
51
+ expected = [User.new('alice@example.net'), User.new('booger@example.com')]
52
+ assert_equal(expected.sort(), actual.sort())
53
+
54
+ pfamv = PostfixadminMv.new(cfg)
55
+ actual = pfamv.list_users()
56
+ expected = [User.new('alice@example.net'),
57
+ User.new('bob@example.com'),
58
+ User.new('adam@example.net'),
59
+ User.new('beth@example.net'),
60
+ User.new('carol@example.net')]
61
+ assert_equal(expected.sort(), actual.sort())
62
+
63
+ actual = pfamv.list_domains()
64
+ expected = [Domain.new('example.com'), Domain.new('example.net')]
65
+ assert_equal(expected.sort(), actual.sort())
66
+
67
+ actual = pfamv.list_aliases()
68
+ expected = [{'address' => 'adam@example.net',
69
+ 'goto' => 'adam@example.net'},
70
+ {'address' => 'alice@example.net',
71
+ 'goto' => 'alice@example.net,' +
72
+ 'adam@example.net,' +
73
+ 'bob@example.com,' +
74
+ 'carol@example.net'},
75
+ {'address' => 'beth@example.net',
76
+ 'goto' => 'beth@example.net'},
77
+ {'address' => 'bob@example.com',
78
+ 'goto' => 'bob@example.com'},
79
+ {'address' => 'carol@example.net',
80
+ 'goto' => 'carol@example.net'}]
81
+ expected.each { |e| assert(actual.include?(e)) } # can't sort dicts
82
+ actual.each { |a| assert(expected.include?(a)) } # can't sort dicts
83
+
84
+
85
+ rmv = RoundcubeMv.new(cfg)
86
+ actual = rmv.list_users()
87
+ expected = [User.new('alice@example.net'),
88
+ User.new('booger@example.com'),
89
+ User.new('adam@example.net')]
90
+ assert_equal(expected.sort(), actual.sort())
91
+
92
+ # All the maildirs are still there.
93
+ assert(maildir_exists('example.net/alice'))
94
+ assert(maildir_exists('example.com/booger'))
95
+ assert(maildir_exists('example.com/jeremy'))
96
+ assert(maildir_exists('example.net/adam'))
97
+ end
98
+
99
+
100
+ def test_mv_twice
101
+ #
102
+ # If we move a user and then move it back, we should wind up back
103
+ # where we started.
104
+ #
105
+ cfg = configuration()
106
+ src = User.new("alice@example.com")
107
+ dst = User.new("alice@example.net")
108
+
109
+ output_buffer = StringIO.new()
110
+
111
+ $stdout = output_buffer
112
+ MvPlugin.run(cfg, src, dst)
113
+ MvPlugin.run(cfg, dst, src)
114
+ $stdout = STDOUT
115
+
116
+ # Skip output verification, it's ugly. But make sure the database
117
+ # has what we expect.
118
+
119
+ amv = AgendavMv.new(cfg)
120
+ actual = amv.list_users()
121
+ expected = [User.new('adam@example.net'),User.new('booger@example.com')]
122
+ assert_equal(expected.sort(), actual.sort())
123
+
124
+ dmv = DavicalMv.new(cfg)
125
+ actual = dmv.list_users()
126
+ expected = [User.new('alice@example.com'), User.new('booger@example.com')]
127
+ assert_equal(expected.sort(), actual.sort())
128
+
129
+ pfamv = PostfixadminMv.new(cfg)
130
+ actual = pfamv.list_users()
131
+ expected = [User.new('alice@example.com'),
132
+ User.new('bob@example.com'),
133
+ User.new('adam@example.net'),
134
+ User.new('beth@example.net'),
135
+ User.new('carol@example.net')]
136
+ assert_equal(expected.sort(), actual.sort())
137
+
138
+ actual = pfamv.list_domains()
139
+ expected = [Domain.new('example.com'), Domain.new('example.net')]
140
+ assert_equal(expected.sort(), actual.sort())
141
+
142
+ actual = pfamv.list_aliases()
143
+ expected = [{'address' => 'adam@example.net',
144
+ 'goto' => 'adam@example.net'},
145
+ {'address' => 'alice@example.com',
146
+ 'goto' => 'alice@example.com,' +
147
+ 'adam@example.net,' +
148
+ 'bob@example.com,' +
149
+ 'carol@example.net'},
150
+ {'address' => 'beth@example.net',
151
+ 'goto' => 'beth@example.net'},
152
+ {'address' => 'bob@example.com',
153
+ 'goto' => 'bob@example.com'},
154
+ {'address' => 'carol@example.net',
155
+ 'goto' => 'carol@example.net'}]
156
+ expected.each { |e| assert(actual.include?(e)) } # can't sort dicts
157
+ actual.each { |a| assert(expected.include?(a)) } # can't sort dicts
158
+
159
+
160
+ rmv = RoundcubeMv.new(cfg)
161
+ actual = rmv.list_users()
162
+ expected = [User.new('alice@example.com'),
163
+ User.new('booger@example.com'),
164
+ User.new('adam@example.net')]
165
+ assert_equal(expected.sort(), actual.sort())
166
+
167
+ # All the maildirs are still there.
168
+ assert(maildir_exists('example.com/alice'))
169
+ assert(maildir_exists('example.com/booger'))
170
+ assert(maildir_exists('example.com/jeremy'))
171
+ assert(maildir_exists('example.net/adam'))
172
+ end
173
+
174
+ end
@@ -0,0 +1,121 @@
1
+ # WARNING: Test output is dependent on the order these classes include
2
+ # certain modules; i.e. on the 'require' order.
3
+ require 'common/domain'
4
+ require 'common/user'
5
+ require 'mailshears_test'
6
+ require 'prune/plugins/agendav'
7
+ require 'prune/plugins/davical'
8
+ require 'prune/plugins/dovecot'
9
+ require 'prune/plugins/postfixadmin'
10
+ require 'prune/plugins/roundcube'
11
+ require 'prune/prune_runner'
12
+
13
+
14
+ class TestPrune < MailshearsTest
15
+
16
+ def check_assertions(actual)
17
+ cfg = configuration()
18
+
19
+ # Both of our tests have the same expected output / results, so
20
+ # check them both using the same function.
21
+ expected =
22
+ "AgendavPrune - Removed user booger@example.com.\n" +
23
+ "DavicalPrune - Removed user booger@example.com (Principal ID: 2).\n" +
24
+ "DovecotPrune - Removed user booger@example.com " +
25
+ "(#{cfg.dovecot_mail_root()}/example.com/booger).\n" +
26
+ "DovecotPrune - Removed user jeremy@example.com " +
27
+ "(#{cfg.dovecot_mail_root()}/example.com/jeremy).\n" +
28
+ "RoundcubePrune - Removed user booger@example.com (User ID: 2).\n"
29
+
30
+ assert_equal(expected, actual)
31
+
32
+ # Now make sure the database has what we expect.
33
+
34
+ apr = AgendavPrune.new(cfg)
35
+ actual = apr.list_users()
36
+ expected = [User.new('adam@example.net')]
37
+ assert_equal(expected, actual)
38
+
39
+ dpr = DavicalPrune.new(cfg)
40
+ actual = dpr.list_users()
41
+ expected = [User.new('alice@example.com')]
42
+ assert_equal(expected, actual)
43
+
44
+ pfapr = PostfixadminPrune.new(cfg)
45
+ actual = pfapr.list_users()
46
+ expected = [User.new('alice@example.com'),
47
+ User.new('bob@example.com'),
48
+ User.new('adam@example.net'),
49
+ User.new('beth@example.net'),
50
+ User.new('carol@example.net')]
51
+ assert_equal(expected, actual)
52
+
53
+ actual = pfapr.list_domains()
54
+ expected = [Domain.new('example.com'), Domain.new('example.net')]
55
+ assert_equal(expected, actual)
56
+
57
+ actual = pfapr.list_aliases()
58
+ expected = [{'address' => 'adam@example.net',
59
+ 'goto' => 'adam@example.net'},
60
+ {'address' => 'alice@example.com',
61
+ 'goto' => 'alice@example.com,' +
62
+ 'adam@example.net,' +
63
+ 'bob@example.com,' +
64
+ 'carol@example.net'},
65
+ {'address' => 'beth@example.net',
66
+ 'goto' => 'beth@example.net'},
67
+ {'address' => 'bob@example.com',
68
+ 'goto' => 'bob@example.com'},
69
+ {'address' => 'carol@example.net',
70
+ 'goto' => 'carol@example.net'}]
71
+ expected.each { |e| assert(actual.include?(e)) } # can't sort dicts
72
+ actual.each { |a| assert(expected.include?(a)) } # can't sort dicts
73
+
74
+ rpr = RoundcubePrune.new(cfg)
75
+ actual = rpr.list_users()
76
+ expected = [User.new('alice@example.com'), User.new('adam@example.net')]
77
+ assert_equal(expected, actual)
78
+
79
+ # Booger and Jeremy should get pruned.
80
+ assert(maildir_exists('example.com/alice'))
81
+ assert(maildir_exists('example.net/adam'))
82
+ assert(!maildir_exists('example.com/booger'))
83
+ assert(!maildir_exists('example.com/jeremy'))
84
+ end
85
+
86
+
87
+ def test_single_prune()
88
+ # Run prune once and see what happens.
89
+ cfg = configuration()
90
+
91
+ output_buffer = StringIO.new()
92
+
93
+ $stdout = output_buffer
94
+ PrunePlugin.run(cfg)
95
+ $stdout = STDOUT
96
+
97
+ actual = output_buffer.string()
98
+
99
+ check_assertions(actual)
100
+ end
101
+
102
+
103
+ def test_double_prune
104
+ # Run prune twice. This should have the exact same output as
105
+ # running it once, since the second time around, there's nothing
106
+ # to prune.
107
+ cfg = configuration()
108
+
109
+ output_buffer = StringIO.new()
110
+
111
+ $stdout = output_buffer
112
+ PrunePlugin.run(cfg)
113
+ PrunePlugin.run(cfg)
114
+ $stdout = STDOUT
115
+
116
+ actual = output_buffer.string()
117
+
118
+ check_assertions(actual)
119
+ end
120
+
121
+ end
data/test/test_rm.rb ADDED
@@ -0,0 +1,154 @@
1
+ # WARNING: Test output is dependent on the order these classes include
2
+ # certain modules; i.e. on the 'require' order.
3
+ require 'common/domain'
4
+ require 'common/user'
5
+ require 'mailshears_test'
6
+ require 'rm/plugins/agendav'
7
+ require 'rm/plugins/davical'
8
+ require 'rm/plugins/dovecot'
9
+ require 'rm/plugins/postfixadmin'
10
+ require 'rm/plugins/roundcube'
11
+ require 'rm/rm_runner'
12
+
13
+
14
+ class TestRm < MailshearsTest
15
+
16
+ def test_rm_user
17
+ cfg = configuration()
18
+ users = [ User.new("adam@example.net") ]
19
+
20
+ output_buffer = StringIO.new()
21
+
22
+ $stdout = output_buffer
23
+ RmPlugin.run(cfg, *users)
24
+ $stdout = STDOUT
25
+
26
+ actual = output_buffer.string()
27
+
28
+ expected =
29
+ "AgendavRm - Removed user adam@example.net.\n" +
30
+ "DavicalRm - User adam@example.net not found.\n" +
31
+ "DovecotRm - Removed user adam@example.net " +
32
+ "(#{cfg.dovecot_mail_root}/example.net/adam).\n" +
33
+ "PostfixadminRm - Removed user adam@example.net.\n" +
34
+ "RoundcubeRm - Removed user adam@example.net (User ID: 3).\n"
35
+
36
+ assert_equal(expected, actual)
37
+
38
+ # Now make sure the database has what we expect.
39
+
40
+ arm = AgendavRm.new(cfg)
41
+ actual = arm.list_users()
42
+ expected = [User.new('booger@example.com')]
43
+ assert_equal(expected, actual)
44
+
45
+ drm = DavicalRm.new(cfg)
46
+ actual = drm.list_users()
47
+ expected = [User.new('alice@example.com'), User.new('booger@example.com')]
48
+ assert_equal(expected, actual)
49
+
50
+ pfarm = PostfixadminRm.new(cfg)
51
+ actual = pfarm.list_users()
52
+ expected = [User.new('alice@example.com'),
53
+ User.new('bob@example.com'),
54
+ User.new('beth@example.net'),
55
+ User.new('carol@example.net')]
56
+ assert_equal(expected, actual)
57
+
58
+ actual = pfarm.list_domains()
59
+ expected = [Domain.new('example.com'), Domain.new('example.net')]
60
+ assert_equal(expected, actual)
61
+
62
+ actual = pfarm.list_aliases()
63
+ expected = [{'address' => 'alice@example.com',
64
+ 'goto' => 'alice@example.com,' +
65
+ 'bob@example.com,' +
66
+ 'carol@example.net'},
67
+ {'address' => 'beth@example.net',
68
+ 'goto' => 'beth@example.net'},
69
+ {'address' => 'bob@example.com',
70
+ 'goto' => 'bob@example.com'},
71
+ {'address' => 'carol@example.net',
72
+ 'goto' => 'carol@example.net'}]
73
+ expected.each { |e| assert(actual.include?(e)) } # can't sort dicts
74
+ actual.each { |a| assert(expected.include?(a)) } # can't sort dicts
75
+
76
+ rrm = RoundcubeRm.new(cfg)
77
+ actual = rrm.list_users()
78
+ expected = [User.new('alice@example.com'), User.new('booger@example.com')]
79
+ assert_equal(expected, actual)
80
+
81
+ # Check that adam's directory is gone but that the rest remain.
82
+ assert(maildir_exists('example.com/alice'))
83
+ assert(maildir_exists('example.com/booger'))
84
+ assert(maildir_exists('example.com/jeremy'))
85
+ assert(!maildir_exists('example.net/adam'))
86
+ end
87
+
88
+
89
+ def test_rm_domain
90
+ cfg = configuration()
91
+ domains = [Domain.new("example.net")]
92
+
93
+ output_buffer = StringIO.new()
94
+
95
+ $stdout = output_buffer
96
+ RmPlugin.run(cfg, *domains)
97
+ $stdout = STDOUT
98
+
99
+ actual = output_buffer.string()
100
+
101
+ expected =
102
+ "AgendavRm - Removed domain example.net.\n" +
103
+ "DavicalRm - Domain example.net not found.\n" +
104
+ "DovecotRm - Removed domain example.net " +
105
+ "(#{cfg.dovecot_mail_root}/example.net).\n" +
106
+ "PostfixadminRm - Removed domain example.net.\n" +
107
+ "RoundcubeRm - Removed domain example.net.\n"
108
+
109
+ assert_equal(expected, actual)
110
+
111
+ # Now make sure the database has what we expect.
112
+
113
+ arm = AgendavRm.new(cfg)
114
+ actual = arm.list_users()
115
+ expected = [User.new('booger@example.com')]
116
+ assert_equal(expected, actual)
117
+
118
+ drm = DavicalRm.new(cfg)
119
+ actual = drm.list_users()
120
+ expected = [User.new('alice@example.com'), User.new('booger@example.com')]
121
+ assert_equal(expected, actual)
122
+
123
+ pfarm = PostfixadminRm.new(cfg)
124
+ actual = pfarm.list_users()
125
+ expected = [User.new('alice@example.com'),
126
+ User.new('bob@example.com')]
127
+ assert_equal(expected, actual)
128
+
129
+ actual = pfarm.list_domains()
130
+ expected = [Domain.new('example.com')]
131
+ assert_equal(expected, actual)
132
+
133
+ actual = pfarm.list_aliases()
134
+ expected = [{'address' => 'alice@example.com',
135
+ 'goto' => 'alice@example.com,bob@example.com'},
136
+ {'address' => 'bob@example.com',
137
+ 'goto' => 'bob@example.com'}]
138
+ expected.each { |e| assert(actual.include?(e)) } # can't sort dicts
139
+ actual.each { |a| assert(expected.include?(a)) } # can't sort dicts
140
+
141
+ rrm = RoundcubeRm.new(cfg)
142
+ actual = rrm.list_users()
143
+ expected = [User.new('alice@example.com'), User.new('booger@example.com')]
144
+ assert_equal(expected, actual)
145
+
146
+ # Check that example.net's directory is gone but that the rest remain.
147
+ assert(maildir_exists('example.com/alice'))
148
+ assert(maildir_exists('example.com/booger'))
149
+ assert(maildir_exists('example.com/jeremy'))
150
+ assert(!maildir_exists('example.net'))
151
+ end
152
+
153
+
154
+ end