| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | # -*- coding: utf-8; -*- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  | from unittest.mock import MagicMock, patch | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  | from pyramid.httpexceptions import HTTPFound, HTTPForbidden | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | from wuttaweb.views import auth as mod | 
					
						
							| 
									
										
										
										
											2025-01-06 16:47:48 -06:00
										 |  |  | from wuttaweb.testing import WebTestCase | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  | class TestAuthView(WebTestCase): | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         self.setup_web() | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.pyramid_config.include("wuttaweb.views.common") | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |     def make_view(self): | 
					
						
							|  |  |  |         return mod.AuthView(self.request) | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |     def test_includeme(self): | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.pyramid_config.include("wuttaweb.views.auth") | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_login(self): | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  |         model = self.app.model | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         auth = self.app.get_auth_handler() | 
					
						
							|  |  |  |         view = self.make_view() | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         # until user exists, will redirect | 
					
						
							|  |  |  |         self.assertEqual(self.session.query(model.User).count(), 0) | 
					
						
							|  |  |  |         response = view.login(session=self.session) | 
					
						
							|  |  |  |         self.assertEqual(response.status_code, 302) | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         # make a user | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         barney = model.User(username="barney") | 
					
						
							|  |  |  |         auth.set_user_password(barney, "testpass") | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         self.session.add(barney) | 
					
						
							|  |  |  |         self.session.commit() | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         # now since user exists, form will display | 
					
						
							|  |  |  |         context = view.login(session=self.session) | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertIn("form", context) | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # redirect if user already logged in | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         with patch.object(self.request, "user", new=barney): | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |             view = self.make_view() | 
					
						
							|  |  |  |             response = view.login(session=self.session) | 
					
						
							|  |  |  |         self.assertEqual(response.status_code, 302) | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # login fails w/ wrong password | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.request.method = "POST" | 
					
						
							|  |  |  |         self.request.POST = {"username": "barney", "password": "WRONG"} | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         view = self.make_view() | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  |         context = view.login(session=self.session) | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertIn("form", context) | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # redirect if login succeeds | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.request.method = "POST" | 
					
						
							|  |  |  |         self.request.POST = {"username": "barney", "password": "testpass"} | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         view = self.make_view() | 
					
						
							|  |  |  |         response = view.login(session=self.session) | 
					
						
							|  |  |  |         self.assertEqual(response.status_code, 302) | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_logout(self): | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.pyramid_config.add_route("login", "/login") | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         view = self.make_view() | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  |         self.request.session.delete = MagicMock() | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         response = view.logout() | 
					
						
							| 
									
										
										
										
											2024-08-04 23:09:29 -05:00
										 |  |  |         self.request.session.delete.assert_called_once_with() | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         self.assertEqual(response.status_code, 302) | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_change_password(self): | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         model = self.app.model | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |         auth = self.app.get_auth_handler() | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         barney = model.User(username="barney") | 
					
						
							| 
									
										
										
										
											2024-08-14 18:29:08 -05:00
										 |  |  |         self.session.add(barney) | 
					
						
							|  |  |  |         self.session.commit() | 
					
						
							|  |  |  |         view = self.make_view() | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # unauthenticated user is redirected | 
					
						
							|  |  |  |         redirect = view.change_password() | 
					
						
							|  |  |  |         self.assertIsInstance(redirect, HTTPFound) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-24 17:19:50 -06:00
										 |  |  |         # set initial password | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         auth.set_user_password(barney, "foo") | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |         self.session.commit() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-24 17:19:50 -06:00
										 |  |  |         # forbidden if prevent_edit is set for user | 
					
						
							|  |  |  |         self.request.user = barney | 
					
						
							|  |  |  |         barney.prevent_edit = True | 
					
						
							|  |  |  |         self.assertRaises(HTTPForbidden, view.change_password) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # okay let's test with edit allowed | 
					
						
							|  |  |  |         barney.prevent_edit = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |         # view should now return context w/ form | 
					
						
							|  |  |  |         context = view.change_password() | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertIn("form", context) | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # submit valid form, ensure password is changed | 
					
						
							|  |  |  |         # (nb. this also would redirect user to home page) | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.request.method = "POST" | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |         self.request.POST = { | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |             "current_password": "foo", | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |             # nb. new_password requires colander mapping structure | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |             "__start__": "new_password:mapping", | 
					
						
							|  |  |  |             "new_password": "bar", | 
					
						
							|  |  |  |             "new_password-confirm": "bar", | 
					
						
							|  |  |  |             "__end__": "new_password:mapping", | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |         } | 
					
						
							|  |  |  |         redirect = view.change_password() | 
					
						
							|  |  |  |         self.assertIsInstance(redirect, HTTPFound) | 
					
						
							|  |  |  |         self.session.commit() | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertFalse(auth.check_user_password(barney, "foo")) | 
					
						
							|  |  |  |         self.assertTrue(auth.check_user_password(barney, "bar")) | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # at this point 'foo' is the password, now let's submit some | 
					
						
							|  |  |  |         # invalid forms and make sure we get back a context w/ form | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # first try empty data | 
					
						
							|  |  |  |         self.request.POST = {} | 
					
						
							|  |  |  |         context = view.change_password() | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertIn("form", context) | 
					
						
							|  |  |  |         dform = context["form"].get_deform() | 
					
						
							|  |  |  |         self.assertEqual(dform["current_password"].errormsg, "Required") | 
					
						
							|  |  |  |         self.assertEqual(dform["new_password"].errormsg, "Required") | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # now try bad current password | 
					
						
							|  |  |  |         self.request.POST = { | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |             "current_password": "blahblah", | 
					
						
							|  |  |  |             "__start__": "new_password:mapping", | 
					
						
							|  |  |  |             "new_password": "baz", | 
					
						
							|  |  |  |             "new_password-confirm": "baz", | 
					
						
							|  |  |  |             "__end__": "new_password:mapping", | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |         } | 
					
						
							|  |  |  |         context = view.change_password() | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertIn("form", context) | 
					
						
							|  |  |  |         dform = context["form"].get_deform() | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             dform["current_password"].errormsg, "Current password is incorrect." | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # now try bad new password | 
					
						
							|  |  |  |         self.request.POST = { | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |             "current_password": "bar", | 
					
						
							|  |  |  |             "__start__": "new_password:mapping", | 
					
						
							|  |  |  |             "new_password": "bar", | 
					
						
							|  |  |  |             "new_password-confirm": "bar", | 
					
						
							|  |  |  |             "__end__": "new_password:mapping", | 
					
						
							| 
									
										
										
										
											2024-08-05 11:45:00 -05:00
										 |  |  |         } | 
					
						
							|  |  |  |         context = view.change_password() | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertIn("form", context) | 
					
						
							|  |  |  |         dform = context["form"].get_deform() | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             dform["new_password"].errormsg, | 
					
						
							|  |  |  |             "New password must be different from old password.", | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_become_root(self): | 
					
						
							|  |  |  |         view = mod.AuthView(self.request) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # GET not allowed | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.request.method = "GET" | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  |         self.assertRaises(HTTPForbidden, view.become_root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # non-admin users also not allowed | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.request.method = "POST" | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  |         self.request.is_admin = False | 
					
						
							|  |  |  |         self.assertRaises(HTTPForbidden, view.become_root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # but admin users can become root | 
					
						
							|  |  |  |         self.request.is_admin = True | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertNotIn("is_root", self.request.session) | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  |         redirect = view.become_root() | 
					
						
							|  |  |  |         self.assertIsInstance(redirect, HTTPFound) | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertTrue(self.request.session["is_root"]) | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_stop_root(self): | 
					
						
							|  |  |  |         view = mod.AuthView(self.request) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # GET not allowed | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.request.method = "GET" | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  |         self.assertRaises(HTTPForbidden, view.stop_root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # non-admin users also not allowed | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.request.method = "POST" | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  |         self.request.is_admin = False | 
					
						
							|  |  |  |         self.assertRaises(HTTPForbidden, view.stop_root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # but admin users can stop being root | 
					
						
							|  |  |  |         # (nb. there is no check whether user is currently root) | 
					
						
							|  |  |  |         self.request.is_admin = True | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertNotIn("is_root", self.request.session) | 
					
						
							| 
									
										
										
										
											2024-08-05 14:21:54 -05:00
										 |  |  |         redirect = view.stop_root() | 
					
						
							|  |  |  |         self.assertIsInstance(redirect, HTTPFound) | 
					
						
							| 
									
										
										
										
											2025-08-31 12:26:43 -05:00
										 |  |  |         self.assertFalse(self.request.session["is_root"]) |