~rashi007/mailman/docsfix

« back to all changes in this revision

Viewing changes to src/mailman/rest/tests/test_users.py

  • Committer: Barry Warsaw
  • Date: 2015-02-10 00:41:46 UTC
  • Revision ID: barry@list.org-20150210004146-5l3xgnpie95ju1s6
 * When deleting a user via REST, make sure all linked addresses are deleted.
   Found by Andrew Stuart.  (LP: #1419519)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
__all__ = [
21
21
    'TestLP1074374',
 
22
    'TestLP1419519',
22
23
    'TestLogin',
23
24
    'TestUsers',
24
25
    ]
212
213
 
213
214
 
214
215
 
 
216
class TestLogin(unittest.TestCase):
 
217
    """Test user 'login' (really just password verification)."""
 
218
 
 
219
    layer = RESTLayer
 
220
 
 
221
    def setUp(self):
 
222
        user_manager = getUtility(IUserManager)
 
223
        with transaction():
 
224
            self.anne = user_manager.create_user(
 
225
                'anne@example.com', 'Anne Person')
 
226
            self.anne.password = config.password_context.encrypt('abc123')
 
227
 
 
228
    def test_login_with_cleartext_password(self):
 
229
        # A user can log in with the correct clear text password.
 
230
        content, response = call_api(
 
231
            'http://localhost:9001/3.0/users/anne@example.com/login', {
 
232
                'cleartext_password': 'abc123',
 
233
                }, method='POST')
 
234
        self.assertEqual(response.status, 204)
 
235
        # But the user cannot log in with an incorrect password.
 
236
        with self.assertRaises(HTTPError) as cm:
 
237
            call_api(
 
238
                'http://localhost:9001/3.0/users/anne@example.com/login', {
 
239
                    'cleartext_password': 'not-the-password',
 
240
                    }, method='POST')
 
241
        self.assertEqual(cm.exception.code, 403)
 
242
 
 
243
    def test_wrong_parameter(self):
 
244
        # A bad request because it is mistyped the required attribute.
 
245
        with self.assertRaises(HTTPError) as cm:
 
246
            call_api('http://localhost:9001/3.0/users/1/login', {
 
247
                     'hashed_password': 'bad hash',
 
248
                     })
 
249
        self.assertEqual(cm.exception.code, 400)
 
250
 
 
251
    def test_not_enough_parameters(self):
 
252
        # A bad request because it is missing the required attribute.
 
253
        with self.assertRaises(HTTPError) as cm:
 
254
            call_api('http://localhost:9001/3.0/users/1/login', {
 
255
                     })
 
256
        self.assertEqual(cm.exception.code, 400)
 
257
 
 
258
    def test_too_many_parameters(self):
 
259
        # A bad request because it has too many attributes.
 
260
        with self.assertRaises(HTTPError) as cm:
 
261
            call_api('http://localhost:9001/3.0/users/1/login', {
 
262
                     'cleartext_password': 'abc123',
 
263
                     'display_name': 'Annie Personhood',
 
264
                     })
 
265
        self.assertEqual(cm.exception.code, 400)
 
266
 
 
267
    def test_successful_login_updates_password(self):
 
268
        # Passlib supports updating the hash when the hash algorithm changes.
 
269
        # When a user logs in successfully, the password will be updated if
 
270
        # necessary.
 
271
        #
 
272
        # Start by hashing Anne's password with a different hashing algorithm
 
273
        # than the one that the REST runner uses by default during testing.
 
274
        config_file = os.path.join(config.VAR_DIR, 'passlib-tmp.config')
 
275
        with open(config_file, 'w') as fp:
 
276
            print("""\
 
277
[passlib]
 
278
schemes = hex_md5
 
279
""", file=fp)
 
280
        with configuration('passwords', configuration=config_file):
 
281
            with transaction():
 
282
                self.anne.password = config.password_context.encrypt('abc123')
 
283
                # Just ensure Anne's password is hashed correctly.
 
284
                self.assertEqual(self.anne.password,
 
285
                                 'e99a18c428cb38d5f260853678922e03')
 
286
        # Now, Anne logs in with a successful password.  This should change it
 
287
        # back to the plaintext hash.
 
288
        call_api('http://localhost:9001/3.0/users/1/login', {
 
289
                 'cleartext_password': 'abc123',
 
290
                 })
 
291
        self.assertEqual(self.anne.password, '{plaintext}abc123')
 
292
 
 
293
 
 
294
 
215
295
class TestLP1074374(unittest.TestCase):
216
296
    """LP: #1074374 - deleting a user left their address records active."""
217
297
 
299
379
 
300
380
 
301
381
 
302
 
class TestLogin(unittest.TestCase):
303
 
    """Test user 'login' (really just password verification)."""
304
 
 
 
382
class TestLP1419519(unittest.TestCase):
 
383
    # LP: #1419519 - deleting a user with many linked addresses does not delete
 
384
    # all address records.
305
385
    layer = RESTLayer
306
386
 
307
387
    def setUp(self):
308
 
        user_manager = getUtility(IUserManager)
 
388
        # Create a user and link 10 addresses to that user.
 
389
        self.manager = getUtility(IUserManager)
309
390
        with transaction():
310
 
            self.anne = user_manager.create_user(
311
 
                'anne@example.com', 'Anne Person')
312
 
            self.anne.password = config.password_context.encrypt('abc123')
 
391
            anne = self.manager.create_user('anne@example.com', 'Anne Person')
 
392
            for i in range(10):
 
393
                email = 'a{:02d}@example.com'.format(i)
 
394
                address = self.manager.create_address(email)
 
395
                anne.link(address)
313
396
 
314
 
    def test_login_with_cleartext_password(self):
315
 
        # A user can log in with the correct clear text password.
 
397
    def test_delete_user(self):
 
398
        # Deleting the user deletes all their linked addresses.
 
399
        #
 
400
        # We start with 11 addresses in the database.
 
401
        emails = sorted(address.email for address in self.manager.addresses)
 
402
        self.assertEqual(emails, [
 
403
            'a00@example.com',
 
404
            'a01@example.com',
 
405
            'a02@example.com',
 
406
            'a03@example.com',
 
407
            'a04@example.com',
 
408
            'a05@example.com',
 
409
            'a06@example.com',
 
410
            'a07@example.com',
 
411
            'a08@example.com',
 
412
            'a09@example.com',
 
413
            'anne@example.com',
 
414
            ])
316
415
        content, response = call_api(
317
 
            'http://localhost:9001/3.0/users/anne@example.com/login', {
318
 
                'cleartext_password': 'abc123',
319
 
                }, method='POST')
 
416
            'http://localhost:9001/3.0/users/anne@example.com',
 
417
            method='DELETE')
320
418
        self.assertEqual(response.status, 204)
321
 
        # But the user cannot log in with an incorrect password.
322
 
        with self.assertRaises(HTTPError) as cm:
323
 
            call_api(
324
 
                'http://localhost:9001/3.0/users/anne@example.com/login', {
325
 
                    'cleartext_password': 'not-the-password',
326
 
                    }, method='POST')
327
 
        self.assertEqual(cm.exception.code, 403)
328
 
 
329
 
    def test_wrong_parameter(self):
330
 
        # A bad request because it is mistyped the required attribute.
331
 
        with self.assertRaises(HTTPError) as cm:
332
 
            call_api('http://localhost:9001/3.0/users/1/login', {
333
 
                     'hashed_password': 'bad hash',
334
 
                     })
335
 
        self.assertEqual(cm.exception.code, 400)
336
 
 
337
 
    def test_not_enough_parameters(self):
338
 
        # A bad request because it is missing the required attribute.
339
 
        with self.assertRaises(HTTPError) as cm:
340
 
            call_api('http://localhost:9001/3.0/users/1/login', {
341
 
                     })
342
 
        self.assertEqual(cm.exception.code, 400)
343
 
 
344
 
    def test_too_many_parameters(self):
345
 
        # A bad request because it has too many attributes.
346
 
        with self.assertRaises(HTTPError) as cm:
347
 
            call_api('http://localhost:9001/3.0/users/1/login', {
348
 
                     'cleartext_password': 'abc123',
349
 
                     'display_name': 'Annie Personhood',
350
 
                     })
351
 
        self.assertEqual(cm.exception.code, 400)
352
 
 
353
 
    def test_successful_login_updates_password(self):
354
 
        # Passlib supports updating the hash when the hash algorithm changes.
355
 
        # When a user logs in successfully, the password will be updated if
356
 
        # necessary.
357
 
        #
358
 
        # Start by hashing Anne's password with a different hashing algorithm
359
 
        # than the one that the REST runner uses by default during testing.
360
 
        config_file = os.path.join(config.VAR_DIR, 'passlib-tmp.config')
361
 
        with open(config_file, 'w') as fp:
362
 
            print("""\
363
 
[passlib]
364
 
schemes = hex_md5
365
 
""", file=fp)
366
 
        with configuration('passwords', configuration=config_file):
367
 
            with transaction():
368
 
                self.anne.password = config.password_context.encrypt('abc123')
369
 
                # Just ensure Anne's password is hashed correctly.
370
 
                self.assertEqual(self.anne.password,
371
 
                                 'e99a18c428cb38d5f260853678922e03')
372
 
        # Now, Anne logs in with a successful password.  This should change it
373
 
        # back to the plaintext hash.
374
 
        call_api('http://localhost:9001/3.0/users/1/login', {
375
 
                 'cleartext_password': 'abc123',
376
 
                 })
377
 
        self.assertEqual(self.anne.password, '{plaintext}abc123')
 
419
        # Now there should be no addresses in the database.
 
420
        config.db.abort()
 
421
        emails = sorted(address.email for address in self.manager.addresses)
 
422
        self.assertEqual(len(emails), 0)