Mangle differently
[mspang/vmailman.git] / tests / test_security_mgr.py
1 # Copyright (C) 2001-2003 by the Free Software Foundation, Inc.
2 #
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17 """Unit tests for Mailman/SecurityManager.py
18 """
19
20 import os
21 import unittest
22 import errno
23 import md5
24 import sha
25 import Cookie
26 try:
27     import crypt
28 except ImportError:
29     crypt = None
30 # Don't use cStringIO because we're going to inherit
31 from StringIO import StringIO
32
33 from Mailman import mm_cfg
34 from Mailman import Utils
35 from Mailman import Errors
36
37 from TestBase import TestBase
38
39
40 \f
41 def password(plaintext):
42     return sha.new(plaintext).hexdigest()
43
44
45 \f
46 class TestSecurityManager(TestBase):
47     def test_init_vars(self):
48         eq = self.assertEqual
49         eq(self._mlist.mod_password, None)
50         eq(self._mlist.passwords, {})
51
52     def test_auth_context_info_authuser(self):
53         mlist = self._mlist
54         self.assertRaises(TypeError, mlist.AuthContextInfo, mm_cfg.AuthUser)
55         # Add a member
56         mlist.addNewMember('aperson@dom.ain', password='xxXXxx')
57         self.assertEqual(
58             mlist.AuthContextInfo(mm_cfg.AuthUser, 'aperson@dom.ain'),
59             ('_xtest+user+aperson--at--dom.ain', 'xxXXxx'))
60
61     def test_auth_context_moderator(self):
62         mlist = self._mlist
63         mlist.mod_password = 'yyYYyy'
64         self.assertEqual(
65             mlist.AuthContextInfo(mm_cfg.AuthListModerator),
66             ('_xtest+moderator', 'yyYYyy'))
67
68     def test_auth_context_admin(self):
69         mlist = self._mlist
70         mlist.password = 'zzZZzz'
71         self.assertEqual(
72             mlist.AuthContextInfo(mm_cfg.AuthListAdmin),
73             ('_xtest+admin', 'zzZZzz'))
74
75     def test_auth_context_site(self):
76         mlist = self._mlist
77         mlist.password = 'aaAAaa'
78         self.assertEqual(
79             mlist.AuthContextInfo(mm_cfg.AuthSiteAdmin),
80             ('_xtest+admin', 'aaAAaa'))
81
82     def test_auth_context_huh(self):
83         self.assertEqual(
84             self._mlist.AuthContextInfo('foo'),
85             (None, None))
86
87
88 \f
89 class TestAuthenticate(TestBase):
90     def setUp(self):
91         TestBase.setUp(self)
92         Utils.set_global_password('bbBBbb', siteadmin=1)
93         Utils.set_global_password('ccCCcc', siteadmin=0)
94
95     def tearDown(self):
96         try:
97             os.unlink(mm_cfg.SITE_PW_FILE)
98         except OSError, e:
99             if e.errno <> errno.ENOENT: raise
100         try:
101             os.unlink(mm_cfg.LISTCREATOR_PW_FILE)
102         except OSError, e:
103             if e.errno <> errno.ENOENT: raise
104         TestBase.tearDown(self)
105
106     def test_auth_creator(self):
107         self.assertEqual(self._mlist.Authenticate(
108             [mm_cfg.AuthCreator], 'ccCCcc'), mm_cfg.AuthCreator)
109
110     def test_auth_creator_unauth(self):
111         self.assertEqual(self._mlist.Authenticate(
112             [mm_cfg.AuthCreator], 'xxxxxx'), mm_cfg.UnAuthorized)
113
114     def test_auth_site_admin(self):
115         self.assertEqual(self._mlist.Authenticate(
116             [mm_cfg.AuthSiteAdmin], 'bbBBbb'), mm_cfg.AuthSiteAdmin)
117
118     def test_auth_site_admin_unauth(self):
119         self.assertEqual(self._mlist.Authenticate(
120             [mm_cfg.AuthSiteAdmin], 'xxxxxx'), mm_cfg.UnAuthorized)
121
122     def test_list_admin(self):
123         self._mlist.password = password('ttTTtt')
124         self.assertEqual(self._mlist.Authenticate(
125             [mm_cfg.AuthListAdmin], 'ttTTtt'), mm_cfg.AuthListAdmin)
126
127     def test_list_admin_unauth(self):
128         self._mlist.password = password('ttTTtt')
129         self.assertEqual(self._mlist.Authenticate(
130             [mm_cfg.AuthListAdmin], 'xxxxxx'), mm_cfg.UnAuthorized)
131
132     def test_list_admin_upgrade(self):
133         eq = self.assertEqual
134         mlist = self._mlist
135         mlist.password = md5.new('ssSSss').digest()
136         eq(mlist.Authenticate(
137             [mm_cfg.AuthListAdmin], 'ssSSss'), mm_cfg.AuthListAdmin)
138         eq(mlist.password, password('ssSSss'))
139         # Test crypt upgrades if crypt is supported
140         if crypt:
141             mlist.password = crypt.crypt('rrRRrr', 'zc')
142             eq(self._mlist.Authenticate(
143                 [mm_cfg.AuthListAdmin], 'rrRRrr'), mm_cfg.AuthListAdmin)
144             eq(mlist.password, password('rrRRrr'))
145
146     def test_list_admin_oldstyle_unauth(self):
147         eq = self.assertEqual
148         mlist = self._mlist
149         mlist.password = md5.new('ssSSss').digest()
150         eq(mlist.Authenticate(
151             [mm_cfg.AuthListAdmin], 'xxxxxx'), mm_cfg.UnAuthorized)
152         eq(mlist.password, md5.new('ssSSss').digest())
153         # Test crypt upgrades if crypt is supported
154         if crypt:
155             mlist.password = crypted = crypt.crypt('rrRRrr', 'zc')
156             eq(self._mlist.Authenticate(
157                 [mm_cfg.AuthListAdmin], 'xxxxxx'), mm_cfg.UnAuthorized)
158             eq(mlist.password, crypted)
159
160     def test_list_moderator(self):
161         self._mlist.mod_password = password('mmMMmm')
162         self.assertEqual(self._mlist.Authenticate(
163             [mm_cfg.AuthListModerator], 'mmMMmm'), mm_cfg.AuthListModerator)
164
165     def test_user(self):
166         mlist = self._mlist
167         mlist.addNewMember('aperson@dom.ain', password='nosrepa')
168         self.assertEqual(mlist.Authenticate(
169             [mm_cfg.AuthUser], 'nosrepa', 'aperson@dom.ain'), mm_cfg.AuthUser)
170
171     def test_wrong_user(self):
172         mlist = self._mlist
173         mlist.addNewMember('aperson@dom.ain', password='nosrepa')
174         self.assertRaises(Errors.NotAMemberError, mlist.Authenticate,
175                           [mm_cfg.AuthUser], 'nosrepa', 'bperson@dom.ain')
176
177     def test_no_user(self):
178         mlist = self._mlist
179         mlist.addNewMember('aperson@dom.ain', password='nosrepa')
180         self.assertRaises(AttributeError, mlist.Authenticate,
181                           [mm_cfg.AuthUser], 'nosrepa')
182
183     def test_user_unauth(self):
184         mlist = self._mlist
185         mlist.addNewMember('aperson@dom.ain', password='nosrepa')
186         self.assertEqual(mlist.Authenticate(
187             [mm_cfg.AuthUser], 'xxxxxx', 'aperson@dom.ain'),
188                          mm_cfg.UnAuthorized)
189
190     def test_value_error(self):
191         self.assertRaises(ValueError, self._mlist.Authenticate,
192                           ['spooge'], 'xxxxxx', 'zperson@dom.ain')
193
194
195 \f
196 class StripperIO(StringIO):
197     HEAD = 'Set-Cookie: '
198     def write(self, s):
199         if s.startswith(self.HEAD):
200             s = s[len(self.HEAD):]
201         StringIO.write(self, s)
202
203
204 class TestWebAuthenticate(TestBase):
205     def setUp(self):
206         TestBase.setUp(self)
207         Utils.set_global_password('bbBBbb', siteadmin=1)
208         Utils.set_global_password('ccCCcc', siteadmin=0)
209         mlist = self._mlist
210         mlist.mod_password = password('abcdefg')
211         mlist.addNewMember('aperson@dom.ain', password='qqQQqq')
212         # Set up the cookie data
213         sfp = StripperIO()
214         print >> sfp, mlist.MakeCookie(mm_cfg.AuthSiteAdmin)
215         # AuthCreator isn't handled in AuthContextInfo()
216         print >> sfp, mlist.MakeCookie(mm_cfg.AuthListAdmin)
217         print >> sfp, mlist.MakeCookie(mm_cfg.AuthListModerator)
218         print >> sfp, mlist.MakeCookie(mm_cfg.AuthUser, 'aperson@dom.ain')
219         # Strip off the "Set-Cookie: " prefix
220         cookie = sfp.getvalue()
221         os.environ['HTTP_COOKIE'] = cookie
222
223     def tearDown(self):
224         try:
225             os.unlink(mm_cfg.SITE_PW_FILE)
226         except OSError, e:
227             if e.errno <> errno.ENOENT: raise
228         try:
229             os.unlink(mm_cfg.LISTCREATOR_PW_FILE)
230         except OSError, e:
231             if e.errno <> errno.ENOENT: raise
232         del os.environ['HTTP_COOKIE']
233         TestBase.tearDown(self)
234
235     def test_auth_site_admin(self):
236         self.assertEqual(self._mlist.WebAuthenticate(
237             [mm_cfg.AuthSiteAdmin], 'xxxxxx'), 1)
238
239     def test_list_admin(self):
240         self.assertEqual(self._mlist.WebAuthenticate(
241             [mm_cfg.AuthListAdmin], 'xxxxxx'), 1)
242
243     def test_list_moderator(self):
244         self.assertEqual(self._mlist.WebAuthenticate(
245             [mm_cfg.AuthListModerator], 'xxxxxx'), 1)
246
247     def test_user(self):
248         self.assertEqual(self._mlist.WebAuthenticate(
249             [mm_cfg.AuthUser], 'xxxxxx'), 1)
250
251     def test_not_a_user(self):
252         self._mlist.removeMember('aperson@dom.ain')
253         self.assertEqual(self._mlist.WebAuthenticate(
254             [mm_cfg.AuthUser], 'xxxxxx', 'aperson@dom.ain'), 0)
255
256
257 \f
258 # TBD: Tests for MakeCookie(), ZapCookie(), CheckCookie() -- although the
259 # latter is implicitly tested by testing WebAuthenticate() above.
260
261
262 \f
263 def suite():
264     suite = unittest.TestSuite()
265     suite.addTest(unittest.makeSuite(TestSecurityManager))
266     suite.addTest(unittest.makeSuite(TestAuthenticate))
267     suite.addTest(unittest.makeSuite(TestWebAuthenticate))
268     return suite
269
270
271 \f
272 if __name__ == '__main__':
273     unittest.main(defaultTest='suite')