def unauthorized_redirect(app): lm = LoginManager() lm.login_view = "login" lm.setup_app(app) res = lm.unauthorized() assert res.headers["Location"] == "/login?next=%2F" assert LOGIN_MESSAGE in get_flashed_messages()
def login_message(app): lm = LoginManager() lm.login_view = "login" lm.login_message = u"Log in or the owl will eat you." lm.setup_app(app) lm.unauthorized() assert u"Log in or the owl will eat you." in get_flashed_messages()
class AuthenticationModule(SystemModule): def __init__(self, *args, **kwargs): super(AuthenticationModule, self).__init__(*args, **kwargs) self.login_manager = LoginManager() self.login_manager.login_message_category = "warning" self.login_manager.user_loader(self.load_user) self.logged_in_menu = DropdownMenu() self.login_text = mydomain.lazy_gettext('Login...') self.logged_in_user_text = mydomain.lazy_gettext('User: '******'.' + view.__name__ return view def register(self, app, *args, **kwargs): super(AuthenticationModule, self).register(app, *args, **kwargs) self.login_manager.init_app(app) def login_url(self): return login_url(self.login_manager.login_view, request.url) def logged_in_text(self): return self.logged_in_user_text + ' ' + self.get_current_user_name() def get_current_user_name(self): return current_user.get_name() def user_menu(self, text, group='', values={}): def wrap(view): name = self.name + '.' + view.__name__ entry = self.menu_entry_for_view(view, text, values=values) self.logged_in_menu.add_entry(name, entry, group) return view return wrap def get_anonymous_system_menu(self): return MenuItem(text=self.login_text, url=self.login_url()) def get_system_menu_item(self): if current_user.is_anonymous(): return self.get_anonymous_system_menu() else: return MenuItem(text=self.logged_in_text(), items=self.logged_in_menu.build_real_menu()) def handle_forbidden_endpoint(self): if current_user.is_anonymous(): return self.login_manager.unauthorized() else: return self.forbidden() def forbidden(self): return abort(403)
def unauthorized_callback(app): lm = LoginManager() lm.login_view = "login" @lm.unauthorized_handler def unauth(): return "UNAUTHORIZED!" lm.setup_app(app) assert lm.unauthorized() == "UNAUTHORIZED!" assert len(get_flashed_messages()) == 0
class LoginTestCase(unittest.TestCase): """ Tests for results of the login_user function """ def setUp(self): self.app = Flask(__name__) self.app.config["SECRET_KEY"] = "deterministic" self.app.config["SESSION_PROTECTION"] = None self.app.config["TESTING"] = True self.remember_cookie_name = "remember" self.app.config["REMEMBER_COOKIE_NAME"] = self.remember_cookie_name self.login_manager = LoginManager() self.login_manager.init_app(self.app) self.login_manager._login_disabled = False @self.app.route("/") def index(): return u"Welcome!" @self.app.route("/secret") def secret(): return self.login_manager.unauthorized() @self.app.route("/login-notch") def login_notch(): return unicode(login_user(notch)) @self.app.route("/login-notch-remember") def login_notch_remember(): return unicode(login_user(notch, remember=True)) @self.app.route("/login-notch-permanent") def login_notch_permanent(): session.permanent = True return unicode(login_user(notch)) @self.app.route("/needs-refresh") def needs_refresh(): return self.login_manager.needs_refresh() @self.app.route("/confirm-login") def _confirm_login(): confirm_login() return u"" @self.app.route("/username") def username(): if current_user.is_authenticated(): return current_user.name return u"Anonymous" @self.app.route("/is-fresh") def is_fresh(): return unicode(login_fresh()) @self.app.route("/logout") def logout(): return unicode(logout_user()) @self.login_manager.user_loader def load_user(user_id): return USERS[int(user_id)] @self.login_manager.header_loader def load_user_from_header(header_value): if header_value.startswith("Basic "): header_value = header_value.replace("Basic ", "", 1) try: user_id = base64.b64decode(header_value) except TypeError: pass return USERS.get(int(user_id)) @self.login_manager.request_loader def load_user_from_request(request): user_id = request.args.get("user_id") try: user_id = int(float(user_id)) except TypeError: pass return USERS.get(user_id) @self.app.route("/empty_session") def empty_session(): return unicode(u"modified=%s" % session.modified) # This will help us with the possibility of typoes in the tests. Now # we shouldn't have to check each response to help us set up state # (such as login pages) to make sure it worked: we will always # get an exception raised (rather than return a 404 response) @self.app.errorhandler(404) def handle_404(e): raise e unittest.TestCase.setUp(self) def _get_remember_cookie(self, test_client): our_cookies = test_client.cookie_jar._cookies["localhost.local"]["/"] return our_cookies[self.remember_cookie_name] def _delete_session(self, c): # Helper method to cause the session to be deleted # as if the browser was closed. This will remove # the session regardless of the permament flag # on the session! with c.session_transaction() as sess: sess.clear() # # Login # def test_test_request_context_users_are_anonymous(self): with self.app.test_request_context(): self.assertTrue(current_user.is_anonymous()) def test_defaults_anonymous(self): with self.app.test_client() as c: result = c.get("/username") self.assertEqual(u"Anonymous", result.data.decode("utf-8")) def test_login_user(self): with self.app.test_request_context(): result = login_user(notch) self.assertTrue(result) self.assertEqual(current_user.name, u"Notch") def test_login_user_emits_signal(self): with self.app.test_request_context(): with listen_to(user_logged_in) as listener: login_user(notch) listener.assert_heard_one(self.app, user=notch) def test_login_inactive_user(self): with self.app.test_request_context(): result = login_user(creeper) self.assertTrue(current_user.is_anonymous()) self.assertFalse(result) def test_login_inactive_user_forced(self): with self.app.test_request_context(): login_user(creeper, force=True) self.assertEqual(current_user.name, u"Creeper") def test_login_user_with_header(self): user_id = 2 user_name = USERS[user_id].name with self.app.test_client() as c: basic_fmt = "Basic {0}" decoded = bytes.decode(base64.b64encode(str.encode(str(user_id)))) headers = [("Authorization", basic_fmt.format(decoded))] result = c.get("/username", headers=headers) self.assertEqual(user_name, result.data.decode("utf-8")) def test_login_invalid_user_with_header(self): user_id = 4 user_name = u"Anonymous" with self.app.test_client() as c: basic_fmt = "Basic {0}" decoded = bytes.decode(base64.b64encode(str.encode(str(user_id)))) headers = [("Authorization", basic_fmt.format(decoded))] result = c.get("/username", headers=headers) self.assertEqual(user_name, result.data.decode("utf-8")) def test_login_user_with_request(self): user_id = 2 user_name = USERS[user_id].name with self.app.test_client() as c: url = "/username?user_id={user_id}".format(user_id=user_id) result = c.get(url) self.assertEqual(user_name, result.data.decode("utf-8")) def test_login_invalid_user_with_request(self): user_id = 4 user_name = u"Anonymous" with self.app.test_client() as c: url = "/username?user_id={user_id}".format(user_id=user_id) result = c.get(url) self.assertEqual(user_name, result.data.decode("utf-8")) # # Logout # def test_logout_logs_out_current_user(self): with self.app.test_request_context(): login_user(notch) logout_user() self.assertTrue(current_user.is_anonymous()) def test_logout_emits_signal(self): with self.app.test_request_context(): login_user(notch) with listen_to(user_logged_out) as listener: logout_user() listener.assert_heard_one(self.app, user=notch) # # Unauthorized # def test_unauthorized_fires_unauthorized_signal(self): with self.app.test_client() as c: with listen_to(user_unauthorized) as listener: c.get("/secret") listener.assert_heard_one(self.app) def test_unauthorized_flashes_message_with_login_view(self): self.login_manager.login_view = "/login" expected_message = self.login_manager.login_message = u"Log in!" expected_category = self.login_manager.login_message_category = "login" with self.app.test_client() as c: c.get("/secret") msgs = get_flashed_messages(category_filter=[expected_category]) self.assertEqual([expected_message], msgs) def test_unauthorized_flash_message_localized(self): def _gettext(msg): if msg == u"Log in!": return u"Einloggen" self.login_manager.login_view = "/login" self.login_manager.localize_callback = _gettext self.login_manager.login_message = u"Log in!" expected_message = u"Einloggen" expected_category = self.login_manager.login_message_category = "login" with self.app.test_client() as c: c.get("/secret") msgs = get_flashed_messages(category_filter=[expected_category]) self.assertEqual([expected_message], msgs) self.login_manager.localize_callback = None def test_unauthorized_uses_authorized_handler(self): @self.login_manager.unauthorized_handler def _callback(): return Response("This is secret!", 401) with self.app.test_client() as c: result = c.get("/secret") self.assertEqual(result.status_code, 401) self.assertEqual(u"This is secret!", result.data.decode("utf-8")) def test_unauthorized_aborts_with_401(self): with self.app.test_client() as c: result = c.get("/secret") self.assertEqual(result.status_code, 401) def test_unauthorized_redirects_to_login_view(self): self.login_manager.login_view = "login" @self.app.route("/login") def login(): return "Login Form Goes Here!" with self.app.test_client() as c: result = c.get("/secret") self.assertEqual(result.status_code, 302) self.assertEqual(result.location, "http://localhost/login?next=%2Fsecret") # # Session Persistence/Freshness # def test_login_persists(self): with self.app.test_client() as c: c.get("/login-notch") result = c.get("/username") self.assertEqual(u"Notch", result.data.decode("utf-8")) def test_logout_persists(self): with self.app.test_client() as c: c.get("/login-notch") c.get("/logout") result = c.get("/username") self.assertEqual(result.data.decode("utf-8"), u"Anonymous") def test_incorrect_id_logs_out(self): # Ensure that any attempt to reload the user by the ID # will seem as if the user is no longer valid @self.login_manager.user_loader def new_user_loader(user_id): return with self.app.test_client() as c: # Successfully logs in c.get("/login-notch") result = c.get("/username") self.assertEqual(u"Anonymous", result.data.decode("utf-8")) def test_authentication_is_fresh(self): with self.app.test_client() as c: c.get("/login-notch-remember") result = c.get("/is-fresh") self.assertEqual(u"True", result.data.decode("utf-8")) def test_remember_me(self): with self.app.test_client() as c: c.get("/login-notch-remember") self._delete_session(c) result = c.get("/username") self.assertEqual(u"Notch", result.data.decode("utf-8")) def test_remember_me_uses_custom_cookie_parameters(self): name = self.app.config["REMEMBER_COOKIE_NAME"] = "myname" duration = self.app.config["REMEMBER_COOKIE_DURATION"] = timedelta(days=2) domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = ".localhost.local" with self.app.test_client() as c: c.get("/login-notch-remember") # TODO: Is there a better way to test this? self.assertTrue(domain in c.cookie_jar._cookies, "Custom domain not found as cookie domain") domain_cookie = c.cookie_jar._cookies[domain] self.assertTrue(name in domain_cookie["/"], "Custom name not found as cookie name") cookie = domain_cookie["/"][name] expiration_date = datetime.fromtimestamp(cookie.expires) expected_date = datetime.now() + duration difference = expected_date - expiration_date fail_msg = "The expiration date {0} was far from the expected {1}" fail_msg = fail_msg.format(expiration_date, expected_date) self.assertLess(difference, timedelta(seconds=10), fail_msg) self.assertGreater(difference, timedelta(seconds=-10), fail_msg) def test_remember_me_is_unfresh(self): with self.app.test_client() as c: c.get("/login-notch-remember") self._delete_session(c) self.assertEqual(u"False", c.get("/is-fresh").data.decode("utf-8")) def test_user_loaded_from_cookie_fired(self): with self.app.test_client() as c: c.get("/login-notch-remember") self._delete_session(c) with listen_to(user_loaded_from_cookie) as listener: c.get("/username") listener.assert_heard_one(self.app, user=notch) def test_user_loaded_from_header_fired(self): user_id = 1 user_name = USERS[user_id].name with self.app.test_client() as c: with listen_to(user_loaded_from_header) as listener: headers = [("Authorization", "Basic %s" % (bytes.decode(base64.b64encode(str.encode(str(user_id))))))] result = c.get("/username", headers=headers) self.assertEqual(user_name, result.data.decode("utf-8")) listener.assert_heard_one(self.app, user=USERS[user_id]) def test_user_loaded_from_request_fired(self): user_id = 1 user_name = USERS[user_id].name with self.app.test_client() as c: with listen_to(user_loaded_from_request) as listener: url = "/username?user_id={user_id}".format(user_id=user_id) result = c.get(url) self.assertEqual(user_name, result.data.decode("utf-8")) listener.assert_heard_one(self.app, user=USERS[user_id]) def test_logout_stays_logged_out_with_remember_me(self): with self.app.test_client() as c: c.get("/login-notch-remember") c.get("/logout") result = c.get("/username") self.assertEqual(result.data.decode("utf-8"), u"Anonymous") def test_needs_refresh_uses_handler(self): @self.login_manager.needs_refresh_handler def _on_refresh(): return u"Needs Refresh!" with self.app.test_client() as c: c.get("/login-notch-remember") result = c.get("/needs-refresh") self.assertEqual(u"Needs Refresh!", result.data.decode("utf-8")) def test_needs_refresh_fires_needs_refresh_signal(self): with self.app.test_client() as c: c.get("/login-notch-remember") with listen_to(user_needs_refresh) as listener: c.get("/needs-refresh") listener.assert_heard_one(self.app) def test_needs_refresh_fires_flash_when_redirect_to_refresh_view(self): self.login_manager.refresh_view = "/refresh_view" self.login_manager.needs_refresh_message = u"Refresh" self.login_manager.needs_refresh_message_category = "refresh" category_filter = [self.login_manager.needs_refresh_message_category] with self.app.test_client() as c: c.get("/login-notch-remember") c.get("/needs-refresh") msgs = get_flashed_messages(category_filter=category_filter) self.assertIn(self.login_manager.needs_refresh_message, msgs) def test_needs_refresh_flash_message_localized(self): def _gettext(msg): if msg == u"Refresh": return u"Aktualisieren" self.login_manager.refresh_view = "/refresh_view" self.login_manager.localize_callback = _gettext self.login_manager.needs_refresh_message = u"Refresh" self.login_manager.needs_refresh_message_category = "refresh" category_filter = [self.login_manager.needs_refresh_message_category] with self.app.test_client() as c: c.get("/login-notch-remember") c.get("/needs-refresh") msgs = get_flashed_messages(category_filter=category_filter) self.assertIn(u"Aktualisieren", msgs) self.login_manager.localize_callback = None def test_needs_refresh_aborts_403(self): with self.app.test_client() as c: c.get("/login-notch-remember") result = c.get("/needs-refresh") self.assertEqual(result.status_code, 403) def test_redirects_to_refresh_view(self): @self.app.route("/refresh-view") def refresh_view(): return "" self.login_manager.refresh_view = "refresh_view" with self.app.test_client() as c: c.get("/login-notch-remember") result = c.get("/needs-refresh") self.assertEqual(result.status_code, 302) expected = "http://localhost/refresh-view?next=%2Fneeds-refresh" self.assertEqual(result.location, expected) def test_confirm_login(self): with self.app.test_client() as c: c.get("/login-notch-remember") self._delete_session(c) self.assertEqual(u"False", c.get("/is-fresh").data.decode("utf-8")) c.get("/confirm-login") self.assertEqual(u"True", c.get("/is-fresh").data.decode("utf-8")) def test_user_login_confirmed_signal_fired(self): with self.app.test_client() as c: with listen_to(user_login_confirmed) as listener: c.get("/confirm-login") listener.assert_heard_one(self.app) def test_session_not_modified(self): with self.app.test_client() as c: # Within the request we think we didn't modify the session. self.assertEquals(u"modified=False", c.get("/empty_session").data.decode("utf-8")) # But after the request, the session could be modified by the # "after_request" handlers that call _update_remember_cookie. # Ensure that if nothing changed the session is not modified. self.assertFalse(session.modified) # # Session Protection # def test_session_protection_basic_passes_successive_requests(self): self.app.config["SESSION_PROTECTION"] = "basic" with self.app.test_client() as c: c.get("/login-notch-remember") username_result = c.get("/username") self.assertEqual(u"Notch", username_result.data.decode("utf-8")) fresh_result = c.get("/is-fresh") self.assertEqual(u"True", fresh_result.data.decode("utf-8")) def test_session_protection_strong_passes_successive_requests(self): self.app.config["SESSION_PROTECTION"] = "strong" with self.app.test_client() as c: c.get("/login-notch-remember") username_result = c.get("/username") self.assertEqual(u"Notch", username_result.data.decode("utf-8")) fresh_result = c.get("/is-fresh") self.assertEqual(u"True", fresh_result.data.decode("utf-8")) def test_session_protection_basic_marks_session_unfresh(self): self.app.config["SESSION_PROTECTION"] = "basic" with self.app.test_client() as c: c.get("/login-notch-remember") username_result = c.get("/username", headers=[("User-Agent", "different")]) self.assertEqual(u"Notch", username_result.data.decode("utf-8")) fresh_result = c.get("/is-fresh") self.assertEqual(u"False", fresh_result.data.decode("utf-8")) def test_session_protection_basic_fires_signal(self): self.app.config["SESSION_PROTECTION"] = "basic" with self.app.test_client() as c: c.get("/login-notch-remember") with listen_to(session_protected) as listener: c.get("/username", headers=[("User-Agent", "different")]) listener.assert_heard_one(self.app) def test_session_protection_basic_skips_when_remember_me(self): self.app.config["SESSION_PROTECTION"] = "basic" with self.app.test_client() as c: c.get("/login-notch-remember") # clear session to force remember me (and remove old session id) self._delete_session(c) # should not trigger protection because "sess" is empty with listen_to(session_protected) as listener: c.get("/username") listener.assert_heard_none(self.app) def test_session_protection_strong_skips_when_remember_me(self): self.app.config["SESSION_PROTECTION"] = "strong" with self.app.test_client() as c: c.get("/login-notch-remember") # clear session to force remember me (and remove old session id) self._delete_session(c) # should not trigger protection because "sess" is empty with listen_to(session_protected) as listener: c.get("/username") listener.assert_heard_none(self.app) def test_permanent_strong_session_protection_marks_session_unfresh(self): self.app.config["SESSION_PROTECTION"] = "strong" with self.app.test_client() as c: c.get("/login-notch-permanent") username_result = c.get("/username", headers=[("User-Agent", "different")]) self.assertEqual(u"Notch", username_result.data.decode("utf-8")) fresh_result = c.get("/is-fresh") self.assertEqual(u"False", fresh_result.data.decode("utf-8")) def test_permanent_strong_session_protection_fires_signal(self): self.app.config["SESSION_PROTECTION"] = "strong" with self.app.test_client() as c: c.get("/login-notch-permanent") with listen_to(session_protected) as listener: c.get("/username", headers=[("User-Agent", "different")]) listener.assert_heard_one(self.app) def test_session_protection_strong_deletes_session(self): self.app.config["SESSION_PROTECTION"] = "strong" with self.app.test_client() as c: c.get("/login-notch-remember") username_result = c.get("/username", headers=[("User-Agent", "different")]) self.assertEqual(u"Anonymous", username_result.data.decode("utf-8")) def test_session_protection_strong_fires_signal_user_agent(self): self.app.config["SESSION_PROTECTION"] = "strong" with self.app.test_client() as c: c.get("/login-notch-remember") with listen_to(session_protected) as listener: c.get("/username", headers=[("User-Agent", "different")]) listener.assert_heard_one(self.app) def test_session_protection_strong_fires_signal_x_forwarded_for(self): self.app.config["SESSION_PROTECTION"] = "strong" with self.app.test_client() as c: c.get("/login-notch-remember", headers=[("X-Forwarded-For", "10.1.1.1")]) with listen_to(session_protected) as listener: c.get("/username", headers=[("X-Forwarded-For", "10.1.1.2")]) listener.assert_heard_one(self.app) def test_session_protection_skip_when_off_and_anonymous(self): with self.app.test_client() as c: # no user access with listen_to(user_accessed) as user_listener: results = c.get("/") user_listener.assert_heard_none(self.app) # access user with no session data with listen_to(session_protected) as session_listener: results = c.get("/username") self.assertEqual(results.data.decode("utf-8"), u"Anonymous") session_listener.assert_heard_none(self.app) # verify no session data has been set self.assertFalse(session) def test_session_protection_skip_when_basic_and_anonymous(self): self.app.config["SESSION_PROTECTION"] = "basic" with self.app.test_client() as c: # no user access with listen_to(user_accessed) as user_listener: results = c.get("/") user_listener.assert_heard_none(self.app) # access user with no session data with listen_to(session_protected) as session_listener: results = c.get("/username") self.assertEqual(results.data.decode("utf-8"), u"Anonymous") session_listener.assert_heard_none(self.app) # verify no session data has been set other than '_id' self.assertIsNotNone(session.get("_id")) self.assertTrue(len(session) == 1) # # Custom Token Loader # def test_custom_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get("/login-notch-remember") self._delete_session(c) # Test that remember me functionality still works self.assertEqual(u"Notch", c.get("/username").data.decode("utf-8")) # Test that we used the custom authentication token remember_cookie = self._get_remember_cookie(c) expected_value = make_secure_token(u"Notch", key="deterministic") self.assertEqual(expected_value, remember_cookie.value) def test_change_api_key_with_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get("/login-notch-remember") self._delete_session(c) self.app.config["SECRET_KEY"] = "ima change this now" result = c.get("/username") self.assertEqual(result.data.decode("utf-8"), u"Notch") def test_custom_token_loader_with_no_user(self): @self.login_manager.token_loader def load_token(token): return with self.app.test_client() as c: c.get("/login-notch-remember") self._delete_session(c) result = c.get("/username") self.assertEqual(result.data.decode("utf-8"), u"Anonymous") # # Lazy Access User # def test_requests_without_accessing_session(self): with self.app.test_client() as c: c.get("/login-notch") # no session access with listen_to(user_accessed) as listener: c.get("/") listener.assert_heard_none(self.app) # should have a session access with listen_to(user_accessed) as listener: result = c.get("/username") listener.assert_heard_one(self.app) self.assertEqual(result.data.decode("utf-8"), u"Notch") # # View Decorators # def test_login_required_decorator(self): @self.app.route("/protected") @login_required def protected(): return u"Access Granted" with self.app.test_client() as c: result = c.get("/protected") self.assertEqual(result.status_code, 401) c.get("/login-notch") result2 = c.get("/protected") self.assertIn(u"Access Granted", result2.data.decode("utf-8")) def test_decorators_are_disabled(self): @self.app.route("/protected") @login_required @fresh_login_required def protected(): return u"Access Granted" self.app.login_manager._login_disabled = True with self.app.test_client() as c: result = c.get("/protected") self.assertIn(u"Access Granted", result.data.decode("utf-8")) def test_fresh_login_required_decorator(self): @self.app.route("/very-protected") @fresh_login_required def very_protected(): return "Access Granted" with self.app.test_client() as c: result = c.get("/very-protected") self.assertEqual(result.status_code, 401) c.get("/login-notch-remember") logged_in_result = c.get("/very-protected") self.assertEqual(u"Access Granted", logged_in_result.data.decode("utf-8")) self._delete_session(c) stale_result = c.get("/very-protected") self.assertEqual(stale_result.status_code, 403) c.get("/confirm-login") refreshed_result = c.get("/very-protected") self.assertEqual(u"Access Granted", refreshed_result.data.decode("utf-8")) # # Misc # @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_chinese_user_agent(self): with self.app.test_client() as c: result = c.get("/", headers=[("User-Agent", u"中文")]) self.assertEqual(u"Welcome!", result.data.decode("utf-8")) @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_russian_cp1251_user_agent(self): with self.app.test_client() as c: headers = [("User-Agent", u"ЯЙЮя".encode("cp1251"))] response = c.get("/", headers=headers) self.assertEqual(response.data.decode("utf-8"), u"Welcome!") def test_make_secure_token_default_key(self): with self.app.test_request_context(): self.assertEqual(make_secure_token("foo"), "0f05743a2b617b2625362ab667c0dbdf4c9ec13a") def test_user_context_processor(self): with self.app.test_request_context(): _ucp = self.app.context_processor(_user_context_processor) self.assertIsInstance(_ucp()["current_user"], AnonymousUserMixin)
def unauthorized_401(app): lm = LoginManager() lm.setup_app(app) with raises(Unauthorized): with assert_fired(user_unauthorized): res = lm.unauthorized()
class Auth: GHOST = 0 USER = 1 ADMIN = 2 SUPER_ADMIN = 3 class Anonymous(AnonymousUserMixin): def __init__(self): self._id = None def get_id(self): return unicode('') def __init__(self): self.__login_manager = LoginManager() self.config = {} def init_app(self, app, config): print 'Attaching LoginManager to app.' self.__login_manager.init_app(app) self.__login_manager.anonymous_user = Auth.Anonymous self.config = config def load_user(user_id): if user_id == '': return None Users = Database['Users'] try: return Users.User.find_one({'_id': ObjectId(user_id)}) except: return None def unauthorized(): return jsonify(error='Unauthorized'), HTTP_401_UNAUTHORIZED self.__login_manager.user_loader(load_user) self.__login_manager.unauthorized_handler(unauthorized) self.__bcrypt = Bcrypt(app) def login_manager(self): return self.__login_manager def require(self, user_level): def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if current_user.is_authenticated( ) and current_user.get_access_level() >= user_level: return f(*args, **kwargs) return self.__login_manager.unauthorized() return decorated_function return login_required def login(self, user_object, password): if user_object is not None: if self.__bcrypt.check_password_hash(user_object['password'], password): if not login_user(user_object): return resource_strings["USER_NOT_ACTIVATED"] else: return 'Invalid password' else: return 'Invalid email' return None def login_social(self, login_type, token): user_object = None try: user_object = SocialSignin.verify(login_type, token) except SocialSignin.Invalid as e: return str(e) login_user(user_object) def logout(self): logout_user() def hash_password(self, password): return self.__bcrypt.generate_password_hash(password) # decorator that protects other users from PUT/POST/DELETE on you stuff # user_id _must_ be passed in as 'user_id' def only_me(self, function): @wraps(function) def inner(*args, **kwargs): if kwargs['user_id'] != 'me': return '{}', HTTP_401_UNAUTHORIZED return function(*args, **kwargs) return inner
class LoginTestCase(unittest.TestCase): ''' Tests for results of the login_user function ''' def setUp(self): self.app = Flask(__name__) self.app.config['SECRET_KEY'] = 'deterministic' self.app.config['SESSION_PROTECTION'] = None self.app.config['TESTING'] = True self.remember_cookie_name = 'remember' self.app.config['REMEMBER_COOKIE_NAME'] = self.remember_cookie_name self.login_manager = LoginManager() self.login_manager.init_app(self.app) self.login_manager._login_disabled = False @self.app.route('/') def index(): return u'Welcome!' @self.app.route('/secret') def secret(): return self.login_manager.unauthorized() @self.app.route('/login-notch') def login_notch(): return unicode(login_user(notch)) @self.app.route('/login-notch-remember') def login_notch_remember(): return unicode(login_user(notch, remember=True)) @self.app.route('/login-notch-permanent') def login_notch_permanent(): session.permanent = True return unicode(login_user(notch)) @self.app.route('/needs-refresh') def needs_refresh(): return self.login_manager.needs_refresh() @self.app.route('/confirm-login') def _confirm_login(): confirm_login() return u'' @self.app.route('/username') def username(): if current_user.is_authenticated: return current_user.name return u'Anonymous' @self.app.route('/is-fresh') def is_fresh(): return unicode(login_fresh()) @self.app.route('/logout') def logout(): return unicode(logout_user()) @self.login_manager.user_loader def load_user(user_id): return USERS[int(user_id)] @self.login_manager.header_loader def load_user_from_header(header_value): if header_value.startswith('Basic '): header_value = header_value.replace('Basic ', '', 1) try: user_id = base64.b64decode(header_value) except TypeError: pass return USERS.get(int(user_id)) @self.login_manager.request_loader def load_user_from_request(request): user_id = request.args.get('user_id') try: user_id = int(float(user_id)) except TypeError: pass return USERS.get(user_id) @self.app.route('/empty_session') def empty_session(): return unicode(u'modified=%s' % session.modified) # This will help us with the possibility of typoes in the tests. Now # we shouldn't have to check each response to help us set up state # (such as login pages) to make sure it worked: we will always # get an exception raised (rather than return a 404 response) @self.app.errorhandler(404) def handle_404(e): raise e unittest.TestCase.setUp(self) def _get_remember_cookie(self, test_client): our_cookies = test_client.cookie_jar._cookies['localhost.local']['/'] return our_cookies[self.remember_cookie_name] def _delete_session(self, c): # Helper method to cause the session to be deleted # as if the browser was closed. This will remove # the session regardless of the permament flag # on the session! with c.session_transaction() as sess: sess.clear() def test_login_user(self): with self.app.test_request_context(): result = login_user(notch) self.assertTrue(result) self.assertEqual(current_user.name, u'Notch')
class Auth: GHOST = 0 USER = 1 ADMIN = 2 SUPER_ADMIN = 3 class Anonymous(AnonymousUserMixin): def __init__(self): self._id = None def get_id(self): return unicode('') def __init__(self): self.__login_manager = LoginManager() self.config = {} def init_app(self, app, config): print 'Attaching LoginManager to app.' self.__login_manager.init_app(app) self.__login_manager.anonymous_user = Auth.Anonymous self.config = config def load_user(user_id): if user_id == '': return None Users = Database['Users'] try: return Users.User.find_one({'_id': ObjectId(user_id)}) except: return None def unauthorized(): return jsonify(error='Unauthorized'), HTTP_401_UNAUTHORIZED self.__login_manager.user_loader(load_user) self.__login_manager.unauthorized_handler(unauthorized) self.__bcrypt = Bcrypt(app) def login_manager(self): return self.__login_manager def require(self, user_level): def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if current_user.is_authenticated() and current_user.get_access_level() >= user_level: return f(*args, **kwargs) return self.__login_manager.unauthorized() return decorated_function return login_required def login(self, user_object, password): if user_object is not None: if self.__bcrypt.check_password_hash(user_object['password'], password): if not login_user(user_object): return resource_strings["USER_NOT_ACTIVATED"] else: return 'Invalid password' else: return 'Invalid email' return None def login_social(self, login_type, token): user_object = None try: user_object = SocialSignin.verify(login_type, token) except SocialSignin.Invalid as e: return str(e) login_user(user_object) def logout(self): logout_user() def hash_password(self, password): return self.__bcrypt.generate_password_hash(password) # decorator that protects other users from PUT/POST/DELETE on you stuff # user_id _must_ be passed in as 'user_id' def only_me(self, function): @wraps(function) def inner(*args, **kwargs): if kwargs['user_id'] != 'me': return '{}', HTTP_401_UNAUTHORIZED return function(*args, **kwargs) return inner
# Set this up before touching handlers, since app is used in decorators. # Simiarly db for models. app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = config.database db = SQLAlchemy(app) login_manager = LoginManager() login_manager.init_app(app) # Now we can import the rest from admin import * from handlers import * from models import * login_manager.user_loader(User.get_by_username) login_manager.unauthorized = lambda: redirect('/login') if __name__ == '__main__': db.create_all() Organisation.query.delete() User.query.delete() db.session.commit() o1 = Organisation(name='The Big Issue') o2 = Organisation(name='Helping Hands') u1 = User(username='******', organisation=o1) u1.set_password('potato') u2 = User(username='******', organisation=o2) u2.set_password('helloworld') for obj in [o1, o2, u1, u2]:
class LoginTestCase(unittest.TestCase): ''' Tests for results of the login_user function ''' def setUp(self): self.app = Flask(__name__) self.app.config['SECRET_KEY'] = 'deterministic' self.app.config['TESTING'] = True self.login_manager = LoginManager() self.login_manager.init_app(self.app) self.login_manager._login_disabled = False @self.app.route('/') def index(): return u'Welcome!' @self.app.route('/secret') def secret(): return self.login_manager.unauthorized() @self.app.route('/login-notch') def login_notch(): return unicode(login_user(notch)) @self.app.route('/login-notch-remember') def login_notch_remember(): return unicode(login_user(notch)) @self.app.route('/login-notch-permanent') def login_notch_permanent(): session.permanent = True return unicode(login_user(notch)) @self.app.route('/username') def username(): if current_user.is_authenticated(): return current_user.name return u'Anonymous' @self.app.route('/logout') def logout(): return unicode(logout_user()) @self.login_manager.user_loader def load_user(user_id): return USERS[int(user_id)] @self.app.route('/empty_session') def empty_session(): return unicode(u'modified=%s' % session.modified) # This will help us with the possibility of typoes in the tests. Now # we shouldn't have to check each response to help us set up state # (such as login pages) to make sure it worked: we will always # get an exception raised (rather than return a 404 response) # @self.app.errorhandler(404) # def handle_404(e): # raise e unittest.TestCase.setUp(self) def _delete_session(self, c): # Helper method to cause the session to be deleted # as if the browser was closed. This will remove # the session regardless of the permament flag # on the session! with c.session_transaction() as sess: sess.clear() # # Login # def test_test_request_context_users_are_anonymous(self): with self.app.test_request_context(): self.assertTrue(current_user.is_anonymous()) def test_defaults_anonymous(self): with self.app.test_client() as c: result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_login_user(self): with self.app.test_request_context(): result = login_user(notch) self.assertTrue(result) self.assertEqual(current_user.name, u'Notch') def test_login_user_emits_signal(self): with self.app.test_request_context(): with listen_to(user_logged_in) as listener: login_user(notch) listener.assert_heard_one(self.app, user=notch) def test_login_inactive_user(self): with self.app.test_request_context(): result = login_user(creeper) self.assertTrue(current_user.is_anonymous()) self.assertFalse(result) def test_login_inactive_user_forced(self): with self.app.test_request_context(): login_user(creeper, force=True) self.assertEqual(current_user.name, u'Creeper') # # Logout # def test_logout_logs_out_current_user(self): with self.app.test_request_context(): login_user(notch) logout_user() self.assertTrue(current_user.is_anonymous()) def test_logout_emits_signal(self): with self.app.test_request_context(): login_user(notch) with listen_to(user_logged_out) as listener: logout_user() listener.assert_heard_one(self.app, user=notch) # # Unauthorized # def test_unauthorized_fires_unauthorized_signal(self): with self.app.test_client() as c: with listen_to(user_unauthorized) as listener: c.get('/secret') listener.assert_heard_one(self.app) def test_unauthorized_uses_authorized_handler(self): @self.login_manager.unauthorized_handler def _callback(): return Response('This is secret!', 401) with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) self.assertEqual(u'This is secret!', result.data.decode('utf-8')) def test_unauthorized_aborts_with_401(self): with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) # # Session Persistence/Freshness # def test_login_persists(self): with self.app.test_client() as c: c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_logout_persists(self): with self.app.test_client() as c: c.get('/login-notch') c.get('/logout') result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') def test_incorrect_id_logs_out(self): # Ensure that any attempt to reload the user by the ID # will seem as if the user is no longer valid @self.login_manager.user_loader def new_user_loader(user_id): return with self.app.test_client() as c: # Successfully logs in c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_session_not_modified(self): with self.app.test_client() as c: # Within the request we think we didn't modify the session. self.assertEquals( u'modified=False', c.get('/empty_session').data.decode('utf-8')) # But after the request, the session could be modified by the # "after_request" handlers that call _update_remember_cookie. # Ensure that if nothing changed the session is not modified. self.assertFalse(session.modified) # # Lazy Access User # def test_requests_without_accessing_session(self): with self.app.test_client() as c: c.get('/login-notch') #no session access with listen_to(user_accessed) as listener: c.get('/') listener.assert_heard_none(self.app) #should have a session access with listen_to(user_accessed) as listener: result = c.get('/username') listener.assert_heard_one(self.app) self.assertEqual(result.data.decode('utf-8'), u'Notch') # # View Decorators # def test_login_required_decorator(self): @self.app.route('/protected') @login_required def protected(): return u'Access Granted' with self.app.test_client() as c: result = c.get('/protected') self.assertEqual(result.status_code, 401) c.get('/login-notch') result2 = c.get('/protected') self.assertIn(u'Access Granted', result2.data.decode('utf-8')) def test_decorators_are_disabled(self): @self.app.route('/protected') @login_required def protected(): return u'Access Granted' self.app.login_manager._login_disabled = True with self.app.test_client() as c: result = c.get('/protected') self.assertIn(u'Access Granted', result.data.decode('utf-8'))
class LoginTestCase(unittest.TestCase): ''' Tests for results of the login_user function ''' def setUp(self): self.app = Flask(__name__) self.app.config['SECRET_KEY'] = 'deterministic' self.app.config['SESSION_PROTECTION'] = None self.app.config['TESTING'] = True self.remember_cookie_name = 'remember' self.app.config['REMEMBER_COOKIE_NAME'] = self.remember_cookie_name self.login_manager = LoginManager() self.login_manager.init_app(self.app) self.login_manager._login_disabled = False @self.app.route('/') def index(): return u'Welcome!' @self.app.route('/secret') def secret(): return self.login_manager.unauthorized() @self.app.route('/login-notch') def login_notch(): return unicode(login_user(notch)) @self.app.route('/login-notch-remember') def login_notch_remember(): return unicode(login_user(notch, remember=True)) @self.app.route('/login-notch-permanent') def login_notch_permanent(): session.permanent = True return unicode(login_user(notch)) @self.app.route('/needs-refresh') def needs_refresh(): return self.login_manager.needs_refresh() @self.app.route('/confirm-login') def _confirm_login(): confirm_login() return u'' @self.app.route('/username') def username(): if current_user.is_authenticated(): return current_user.name return u'Anonymous' @self.app.route('/is-fresh') def is_fresh(): return unicode(login_fresh()) @self.app.route('/logout') def logout(): return unicode(logout_user()) @self.login_manager.user_loader def load_user(user_id): return USERS[int(user_id)] @self.app.route('/empty_session') def empty_session(): return unicode(u'modified=%s' % session.modified) # This will help us with the possibility of typoes in the tests. Now # we shouldn't have to check each response to help us set up state # (such as login pages) to make sure it worked: we will always # get an exception raised (rather than return a 404 response) @self.app.errorhandler(404) def handle_404(e): raise e unittest.TestCase.setUp(self) def _get_remember_cookie(self, test_client): our_cookies = test_client.cookie_jar._cookies['localhost.local']['/'] return our_cookies[self.remember_cookie_name] def _delete_session(self, c): # Helper method to cause the session to be deleted # as if the browser was closed. This will remove # the session regardless of the permament flag # on the session! with c.session_transaction() as sess: sess.clear() # # Login # def test_test_request_context_users_are_anonymous(self): with self.app.test_request_context(): self.assertTrue(current_user.is_anonymous()) def test_defaults_anonymous(self): with self.app.test_client() as c: result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_login_user(self): with self.app.test_request_context(): result = login_user(notch) self.assertTrue(result) self.assertEqual(current_user.name, u'Notch') def test_login_user_emits_signal(self): with self.app.test_request_context(): with listen_to(user_logged_in) as listener: login_user(notch) listener.assert_heard_one(self.app, user=notch) def test_login_inactive_user(self): with self.app.test_request_context(): result = login_user(creeper) self.assertTrue(current_user.is_anonymous()) self.assertFalse(result) def test_login_inactive_user_forced(self): with self.app.test_request_context(): login_user(creeper, force=True) self.assertEqual(current_user.name, u'Creeper') # # Logout # def test_logout_logs_out_current_user(self): with self.app.test_request_context(): login_user(notch) logout_user() self.assertTrue(current_user.is_anonymous()) def test_logout_emits_signal(self): with self.app.test_request_context(): login_user(notch) with listen_to(user_logged_out) as listener: logout_user() listener.assert_heard_one(self.app, user=notch) # # Unauthorized # def test_unauthorized_fires_unauthorized_signal(self): with self.app.test_client() as c: with listen_to(user_unauthorized) as listener: c.get('/secret') listener.assert_heard_one(self.app) def test_unauthorized_flashes_message_with_login_view(self): self.login_manager.login_view = '/login' expected_message = self.login_manager.login_message = u'Log in!' expected_category = self.login_manager.login_message_category = 'login' with self.app.test_client() as c: c.get('/secret') msgs = get_flashed_messages(category_filter=[expected_category]) self.assertEqual([expected_message], msgs) def test_unauthorized_uses_authorized_handler(self): @self.login_manager.unauthorized_handler def _callback(): return Response('This is secret!', 401) with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) self.assertEqual(u'This is secret!', result.data.decode('utf-8')) def test_unauthorized_aborts_with_401(self): with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) def test_unauthorized_redirects_to_login_view(self): self.login_manager.login_view = 'login' @self.app.route('/login') def login(): return 'Login Form Goes Here!' with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 302) self.assertEqual(result.location, 'http://localhost/login?next=%2Fsecret') # # Session Persistence/Freshness # def test_login_persists(self): with self.app.test_client() as c: c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_logout_persists(self): with self.app.test_client() as c: c.get('/login-notch') c.get('/logout') result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') def test_incorrect_id_logs_out(self): # Ensure that any attempt to reload the user by the ID # will seem as if the user is no longer valid @self.login_manager.user_loader def new_user_loader(user_id): return with self.app.test_client() as c: # Successfully logs in c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_authentication_is_fresh(self): with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/is-fresh') self.assertEqual(u'True', result.data.decode('utf-8')) def test_remember_me(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) result = c.get('/username') self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_remember_me_uses_custom_cookie_parameters(self): name = self.app.config['REMEMBER_COOKIE_NAME'] = 'myname' duration = self.app.config['REMEMBER_COOKIE_DURATION'] = \ timedelta(days=2) domain = self.app.config['REMEMBER_COOKIE_DOMAIN'] = '.localhost.local' with self.app.test_client() as c: c.get('/login-notch-remember') # TODO: Is there a better way to test this? self.assertTrue(domain in c.cookie_jar._cookies, 'Custom domain not found as cookie domain') domain_cookie = c.cookie_jar._cookies[domain] self.assertTrue(name in domain_cookie['/'], 'Custom name not found as cookie name') cookie = domain_cookie['/'][name] expiration_date = datetime.fromtimestamp(cookie.expires) expected_date = datetime.now() + duration difference = expected_date - expiration_date fail_msg = 'The expiration date {0} was far from the expected {1}' fail_msg = fail_msg.format(expiration_date, expected_date) self.assertLess(difference, timedelta(seconds=10), fail_msg) self.assertGreater(difference, timedelta(seconds=-10), fail_msg) def test_remember_me_is_unfresh(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.assertEqual(u'False', c.get('/is-fresh').data.decode('utf-8')) def test_user_loaded_from_cookie_fired(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) with listen_to(user_loaded_from_cookie) as listener: c.get('/username') listener.assert_heard_one(self.app, user=notch) def test_logout_stays_logged_out_with_remember_me(self): with self.app.test_client() as c: c.get('/login-notch-remember') c.get('/logout') result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') def test_needs_refresh_uses_handler(self): @self.login_manager.needs_refresh_handler def _on_refresh(): return u'Needs Refresh!' with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(u'Needs Refresh!', result.data.decode('utf-8')) def test_needs_refresh_fires_needs_refresh_signal(self): with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(user_needs_refresh) as listener: c.get('/needs-refresh') listener.assert_heard_one(self.app) def test_needs_refresh_fires_flash_when_redirect_to_refresh_view(self): self.login_manager.refresh_view = '/refresh_view' self.login_manager.needs_refresh_message = u'Refresh' self.login_manager.needs_refresh_message_category = 'refresh' category_filter = [self.login_manager.needs_refresh_message_category] with self.app.test_client() as c: c.get('/login-notch-remember') c.get('/needs-refresh') msgs = get_flashed_messages(category_filter=category_filter) self.assertIn(self.login_manager.needs_refresh_message, msgs) def test_needs_refresh_aborts_403(self): with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(result.status_code, 403) def test_redirects_to_refresh_view(self): @self.app.route('/refresh-view') def refresh_view(): return '' self.login_manager.refresh_view = 'refresh_view' with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(result.status_code, 302) expected = 'http://localhost/refresh-view?next=%2Fneeds-refresh' self.assertEqual(result.location, expected) def test_confirm_login(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.assertEqual(u'False', c.get('/is-fresh').data.decode('utf-8')) c.get('/confirm-login') self.assertEqual(u'True', c.get('/is-fresh').data.decode('utf-8')) def test_user_login_confirmed_signal_fired(self): with self.app.test_client() as c: with listen_to(user_login_confirmed) as listener: c.get('/confirm-login') listener.assert_heard_one(self.app) def test_session_not_modified(self): with self.app.test_client() as c: # Within the request we think we didn't modify the session. self.assertEquals( u'modified=False', c.get('/empty_session').data.decode('utf-8')) # But after the request, the session could be modified by the # "after_request" handlers that call _update_remember_cookie. # Ensure that if nothing changed the session is not modified. self.assertFalse(session.modified) # # Session Protection # def test_session_protection_basic_passes_successive_requests(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username') self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'True', fresh_result.data.decode('utf-8')) def test_session_protection_strong_passes_successive_requests(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username') self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'True', fresh_result.data.decode('utf-8')) def test_session_protection_basic_marks_session_unfresh(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'False', fresh_result.data.decode('utf-8')) def test_session_protection_basic_fires_signal(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_basic_skips_when_remember_me(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') # clear session to force remember me (and remove old session id) self._delete_session(c) # should not trigger protection because "sess" is empty with listen_to(session_protected) as listener: c.get('/username') listener.assert_heard_none(self.app) def test_session_protection_strong_triggers_when_remember_me(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') # clear session to force remember me (and remove old session id) self._delete_session(c) # should not trigger protection because "sess" is empty with listen_to(session_protected) as listener: c.get('/username') listener.assert_heard_one(self.app) def test_permanent_strong_session_protection_marks_session_unfresh(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-permanent') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'False', fresh_result.data.decode('utf-8')) def test_permanent_strong_session_protection_fires_signal(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-permanent') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_strong_deletes_session(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Anonymous', username_result.data.decode('utf-8')) def test_session_protection_strong_fires_signal_user_agent(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_strong_fires_signal_x_forwarded_for(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember', headers=[('X-Forwarded-For', '10.1.1.1')]) with listen_to(session_protected) as listener: c.get('/username', headers=[('X-Forwarded-For', '10.1.1.2')]) listener.assert_heard_one(self.app) def test_session_protection_skip_when_off_and_anonymous(self): with self.app.test_client() as c: # no user access with listen_to(user_accessed) as user_listener: results = c.get('/') user_listener.assert_heard_none(self.app) # access user with no session data with listen_to(session_protected) as session_listener: results = c.get('/username') self.assertEqual(results.data.decode('utf-8'), u'Anonymous') session_listener.assert_heard_none(self.app) # verify no session data has been set self.assertFalse(session) def test_session_protection_skip_when_basic_and_anonymous(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: # no user access with listen_to(user_accessed) as user_listener: results = c.get('/') user_listener.assert_heard_none(self.app) # access user with no session data with listen_to(session_protected) as session_listener: results = c.get('/username') self.assertEqual(results.data.decode('utf-8'), u'Anonymous') session_listener.assert_heard_none(self.app) # verify no session data has been set self.assertFalse(session) # # Custom Token Loader # def test_custom_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) # Test that remember me functionality still works self.assertEqual(u'Notch', c.get('/username').data.decode('utf-8')) # Test that we used the custom authentication token remember_cookie = self._get_remember_cookie(c) expected_value = make_secure_token(u'Notch', key='deterministic') self.assertEqual(expected_value, remember_cookie.value) def test_change_api_key_with_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.app.config['SECRET_KEY'] = 'ima change this now' result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Notch') def test_custom_token_loader_with_no_user(self): @self.login_manager.token_loader def load_token(token): return with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') # # Lazy Access User # def test_requests_without_accessing_session(self): with self.app.test_client() as c: c.get('/login-notch') #no session access with listen_to(user_accessed) as listener: c.get('/') listener.assert_heard_none(self.app) #should have a session access with listen_to(user_accessed) as listener: result = c.get('/username') listener.assert_heard_one(self.app) self.assertEqual(result.data.decode('utf-8'), u'Notch') # # View Decorators # def test_login_required_decorator(self): @self.app.route('/protected') @login_required def protected(): return u'Access Granted' with self.app.test_client() as c: result = c.get('/protected') self.assertEqual(result.status_code, 401) c.get('/login-notch') result2 = c.get('/protected') self.assertIn(u'Access Granted', result2.data.decode('utf-8')) def test_decorators_are_disabled(self): @self.app.route('/protected') @login_required @fresh_login_required def protected(): return u'Access Granted' self.app.login_manager._login_disabled = True with self.app.test_client() as c: result = c.get('/protected') self.assertIn(u'Access Granted', result.data.decode('utf-8')) def test_fresh_login_required_decorator(self): @self.app.route('/very-protected') @fresh_login_required def very_protected(): return 'Access Granted' with self.app.test_client() as c: result = c.get('/very-protected') self.assertEqual(result.status_code, 401) c.get('/login-notch-remember') logged_in_result = c.get('/very-protected') self.assertEqual(u'Access Granted', logged_in_result.data.decode('utf-8')) self._delete_session(c) stale_result = c.get('/very-protected') self.assertEqual(stale_result.status_code, 403) c.get('/confirm-login') refreshed_result = c.get('/very-protected') self.assertEqual(u'Access Granted', refreshed_result.data.decode('utf-8')) # # Misc # @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_chinese_user_agent(self): with self.app.test_client() as c: result = c.get('/', headers=[('User-Agent', u'中文')]) self.assertEqual(u'Welcome!', result.data.decode('utf-8')) @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_russian_cp1251_user_agent(self): with self.app.test_client() as c: headers = [('User-Agent', u'ЯЙЮя'.encode('cp1251'))] response = c.get('/', headers=headers) self.assertEqual(response.data.decode('utf-8'), u'Welcome!') def test_make_secure_token_default_key(self): with self.app.test_request_context(): self.assertEqual(make_secure_token('foo'), '0f05743a2b617b2625362ab667c0dbdf4c9ec13a') def test_user_context_processor(self): with self.app.test_request_context(): _ucp = self.app.context_processor(_user_context_processor) self.assertEqual(_ucp(), {'current_user': None})
class LoginTestCase(unittest.TestCase): ''' Tests for results of the login_user function ''' def setUp(self): self.app = Flask(__name__) self.app.config['SECRET_KEY'] = 'deterministic' self.app.config['SESSION_PROTECTION'] = None self.remember_cookie_name = 'remember' self.app.config['REMEMBER_COOKIE_NAME'] = self.remember_cookie_name self.login_manager = LoginManager() self.login_manager.init_app(self.app) self.login_manager._login_disabled = False @self.app.route('/') def index(): return u'Welcome!' @self.app.route('/secret') def secret(): return self.login_manager.unauthorized() @self.app.route('/login-notch') def login_notch(): return unicode(login_user(notch)) @self.app.route('/login-notch-remember') def login_notch_remember(): return unicode(login_user(notch, remember=True)) @self.app.route('/login-notch-permanent') def login_notch_permanent(): session.permanent = True return unicode(login_user(notch)) @self.app.route('/needs-refresh') def needs_refresh(): return self.login_manager.needs_refresh() @self.app.route('/confirm-login') def _confirm_login(): confirm_login() return u'' @self.app.route('/username') def username(): if current_user.is_authenticated: return current_user.name return u'Anonymous' @self.app.route('/is-fresh') def is_fresh(): return unicode(login_fresh()) @self.app.route('/logout') def logout(): return unicode(logout_user()) @self.login_manager.user_loader def load_user(user_id): return USERS[int(user_id)] @self.login_manager.header_loader def load_user_from_header(header_value): if header_value.startswith('Basic '): header_value = header_value.replace('Basic ', '', 1) try: user_id = base64.b64decode(header_value) except TypeError: pass return USERS.get(int(user_id)) @self.login_manager.request_loader def load_user_from_request(request): user_id = request.args.get('user_id') try: user_id = int(float(user_id)) except TypeError: pass return USERS.get(user_id) @self.app.route('/empty_session') def empty_session(): return unicode(u'modified=%s' % session.modified) # This will help us with the possibility of typoes in the tests. Now # we shouldn't have to check each response to help us set up state # (such as login pages) to make sure it worked: we will always # get an exception raised (rather than return a 404 response) @self.app.errorhandler(404) def handle_404(e): raise e unittest.TestCase.setUp(self) def _get_remember_cookie(self, test_client): our_cookies = test_client.cookie_jar._cookies['localhost.local']['/'] return our_cookies[self.remember_cookie_name] def _delete_session(self, c): # Helper method to cause the session to be deleted # as if the browser was closed. This will remove # the session regardless of the permament flag # on the session! with c.session_transaction() as sess: sess.clear() # # Login # def test_test_request_context_users_are_anonymous(self): with self.app.test_request_context(): self.assertTrue(current_user.is_anonymous) def test_defaults_anonymous(self): with self.app.test_client() as c: result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_login_user(self): with self.app.test_request_context(): result = login_user(notch) self.assertTrue(result) self.assertEqual(current_user.name, u'Notch') def test_login_user_not_fresh(self): with self.app.test_request_context(): result = login_user(notch, fresh=False) self.assertTrue(result) self.assertEqual(current_user.name, u'Notch') self.assertIs(login_fresh(), False) def test_login_user_emits_signal(self): with self.app.test_request_context(): with listen_to(user_logged_in) as listener: login_user(notch) listener.assert_heard_one(self.app, user=notch) def test_login_inactive_user(self): with self.app.test_request_context(): result = login_user(creeper) self.assertTrue(current_user.is_anonymous) self.assertFalse(result) def test_login_inactive_user_forced(self): with self.app.test_request_context(): login_user(creeper, force=True) self.assertEqual(current_user.name, u'Creeper') def test_login_user_with_header(self): user_id = 2 user_name = USERS[user_id].name self.login_manager.request_callback = None with self.app.test_client() as c: basic_fmt = 'Basic {0}' decoded = bytes.decode(base64.b64encode(str.encode(str(user_id)))) headers = [('Authorization', basic_fmt.format(decoded))] result = c.get('/username', headers=headers) self.assertEqual(user_name, result.data.decode('utf-8')) def test_login_invalid_user_with_header(self): user_id = 9000 user_name = u'Anonymous' self.login_manager.request_callback = None with self.app.test_client() as c: basic_fmt = 'Basic {0}' decoded = bytes.decode(base64.b64encode(str.encode(str(user_id)))) headers = [('Authorization', basic_fmt.format(decoded))] result = c.get('/username', headers=headers) self.assertEqual(user_name, result.data.decode('utf-8')) def test_login_user_with_request(self): user_id = 2 user_name = USERS[user_id].name with self.app.test_client() as c: url = '/username?user_id={user_id}'.format(user_id=user_id) result = c.get(url) self.assertEqual(user_name, result.data.decode('utf-8')) def test_login_invalid_user_with_request(self): user_id = 9000 user_name = u'Anonymous' with self.app.test_client() as c: url = '/username?user_id={user_id}'.format(user_id=user_id) result = c.get(url) self.assertEqual(user_name, result.data.decode('utf-8')) # # Logout # def test_logout_logs_out_current_user(self): with self.app.test_request_context(): login_user(notch) logout_user() self.assertTrue(current_user.is_anonymous) def test_logout_emits_signal(self): with self.app.test_request_context(): login_user(notch) with listen_to(user_logged_out) as listener: logout_user() listener.assert_heard_one(self.app, user=notch) def test_logout_without_current_user(self): with self.app.test_request_context(): login_user(notch) del session['user_id'] with listen_to(user_logged_out) as listener: logout_user() listener.assert_heard_one(self.app, user=ANY) # # Unauthorized # def test_unauthorized_fires_unauthorized_signal(self): with self.app.test_client() as c: with listen_to(user_unauthorized) as listener: c.get('/secret') listener.assert_heard_one(self.app) def test_unauthorized_flashes_message_with_login_view(self): self.login_manager.login_view = '/login' expected_message = self.login_manager.login_message = u'Log in!' expected_category = self.login_manager.login_message_category = 'login' with self.app.test_client() as c: c.get('/secret') msgs = get_flashed_messages(category_filter=[expected_category]) self.assertEqual([expected_message], msgs) def test_unauthorized_flash_message_localized(self): def _gettext(msg): if msg == u'Log in!': return u'Einloggen' self.login_manager.login_view = '/login' self.login_manager.localize_callback = _gettext self.login_manager.login_message = u'Log in!' expected_message = u'Einloggen' expected_category = self.login_manager.login_message_category = 'login' with self.app.test_client() as c: c.get('/secret') msgs = get_flashed_messages(category_filter=[expected_category]) self.assertEqual([expected_message], msgs) self.login_manager.localize_callback = None def test_unauthorized_uses_authorized_handler(self): @self.login_manager.unauthorized_handler def _callback(): return Response('This is secret!', 401) with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) self.assertEqual(u'This is secret!', result.data.decode('utf-8')) def test_unauthorized_aborts_with_401(self): with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) def test_unauthorized_redirects_to_login_view(self): self.login_manager.login_view = 'login' @self.app.route('/login') def login(): return 'Login Form Goes Here!' with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 302) self.assertEqual(result.location, 'http://localhost/login?next=%2Fsecret') def test_unauthorized_uses_blueprint_login_view(self): with self.app.app_context(): first = Blueprint('first', 'first') second = Blueprint('second', 'second') @self.app.route('/app_login') def app_login(): return 'Login Form Goes Here!' @self.app.route('/first_login') def first_login(): return 'Login Form Goes Here!' @self.app.route('/second_login') def second_login(): return 'Login Form Goes Here!' @self.app.route('/protected') @login_required def protected(): return u'Access Granted' @first.route('/protected') @login_required def first_protected(): return u'Access Granted' @second.route('/protected') @login_required def second_protected(): return u'Access Granted' self.app.register_blueprint(first, url_prefix='/first') self.app.register_blueprint(second, url_prefix='/second') set_login_view('app_login') set_login_view('first_login', blueprint=first) set_login_view('second_login', blueprint=second) with self.app.test_client() as c: result = c.get('/protected') self.assertEqual(result.status_code, 302) expected = ('http://localhost/' 'app_login?next=%2Fprotected') self.assertEqual(result.location, expected) result = c.get('/first/protected') self.assertEqual(result.status_code, 302) expected = ('http://localhost/' 'first_login?next=%2Ffirst%2Fprotected') self.assertEqual(result.location, expected) result = c.get('/second/protected') self.assertEqual(result.status_code, 302) expected = ('http://localhost/' 'second_login?next=%2Fsecond%2Fprotected') self.assertEqual(result.location, expected) def test_set_login_view_without_blueprints(self): with self.app.app_context(): @self.app.route('/app_login') def app_login(): return 'Login Form Goes Here!' @self.app.route('/protected') @login_required def protected(): return u'Access Granted' set_login_view('app_login') with self.app.test_client() as c: result = c.get('/protected') self.assertEqual(result.status_code, 302) expected = 'http://localhost/app_login?next=%2Fprotected' self.assertEqual(result.location, expected) # # Session Persistence/Freshness # def test_login_persists(self): with self.app.test_client() as c: c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_logout_persists(self): with self.app.test_client() as c: c.get('/login-notch') c.get('/logout') result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') def test_incorrect_id_logs_out(self): # Ensure that any attempt to reload the user by the ID # will seem as if the user is no longer valid @self.login_manager.user_loader def new_user_loader(user_id): return with self.app.test_client() as c: # Successfully logs in c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_authentication_is_fresh(self): with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/is-fresh') self.assertEqual(u'True', result.data.decode('utf-8')) def test_remember_me(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) result = c.get('/username') self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_remember_me_uses_custom_cookie_parameters(self): name = self.app.config['REMEMBER_COOKIE_NAME'] = 'myname' duration = self.app.config['REMEMBER_COOKIE_DURATION'] = \ timedelta(days=2) path = self.app.config['REMEMBER_COOKIE_PATH'] = '/mypath' domain = self.app.config['REMEMBER_COOKIE_DOMAIN'] = '.localhost.local' with self.app.test_client() as c: c.get('/login-notch-remember') # TODO: Is there a better way to test this? self.assertIn(domain, c.cookie_jar._cookies, 'Custom domain not found as cookie domain') domain_cookie = c.cookie_jar._cookies[domain] self.assertIn(path, domain_cookie, 'Custom path not found as cookie path') path_cookie = domain_cookie[path] self.assertIn(name, path_cookie, 'Custom name not found as cookie name') cookie = path_cookie[name] expiration_date = datetime.utcfromtimestamp(cookie.expires) expected_date = datetime.utcnow() + duration difference = expected_date - expiration_date fail_msg = 'The expiration date {0} was far from the expected {1}' fail_msg = fail_msg.format(expiration_date, expected_date) self.assertLess(difference, timedelta(seconds=10), fail_msg) self.assertGreater(difference, timedelta(seconds=-10), fail_msg) def test_remember_me_is_unfresh(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.assertEqual(u'False', c.get('/is-fresh').data.decode('utf-8')) def test_login_persists_with_signle_x_forwarded_for(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch', headers=[('X-Forwarded-For', '10.1.1.1')]) result = c.get('/username', headers=[('X-Forwarded-For', '10.1.1.1')]) self.assertEqual(u'Notch', result.data.decode('utf-8')) result = c.get('/username', headers=[('X-Forwarded-For', '10.1.1.1')]) self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_login_persists_with_many_x_forwarded_for(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch', headers=[('X-Forwarded-For', '10.1.1.1')]) result = c.get('/username', headers=[('X-Forwarded-For', '10.1.1.1')]) self.assertEqual(u'Notch', result.data.decode('utf-8')) result = c.get('/username', headers=[('X-Forwarded-For', '10.1.1.1, 10.1.1.2')]) self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_user_loaded_from_cookie_fired(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) with listen_to(user_loaded_from_cookie) as listener: c.get('/username') listener.assert_heard_one(self.app, user=notch) def test_user_loaded_from_header_fired(self): user_id = 1 user_name = USERS[user_id].name self.login_manager.request_callback = None with self.app.test_client() as c: with listen_to(user_loaded_from_header) as listener: headers = [ ( 'Authorization', 'Basic %s' % ( bytes.decode( base64.b64encode(str.encode(str(user_id)))) ), ) ] result = c.get('/username', headers=headers) self.assertEqual(user_name, result.data.decode('utf-8')) listener.assert_heard_one(self.app, user=USERS[user_id]) def test_user_loaded_from_request_fired(self): user_id = 1 user_name = USERS[user_id].name with self.app.test_client() as c: with listen_to(user_loaded_from_request) as listener: url = '/username?user_id={user_id}'.format(user_id=user_id) result = c.get(url) self.assertEqual(user_name, result.data.decode('utf-8')) listener.assert_heard_one(self.app, user=USERS[user_id]) def test_logout_stays_logged_out_with_remember_me(self): with self.app.test_client() as c: c.get('/login-notch-remember') c.get('/logout') result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') def test_needs_refresh_uses_handler(self): @self.login_manager.needs_refresh_handler def _on_refresh(): return u'Needs Refresh!' with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(u'Needs Refresh!', result.data.decode('utf-8')) def test_needs_refresh_fires_needs_refresh_signal(self): with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(user_needs_refresh) as listener: c.get('/needs-refresh') listener.assert_heard_one(self.app) def test_needs_refresh_fires_flash_when_redirect_to_refresh_view(self): self.login_manager.refresh_view = '/refresh_view' self.login_manager.needs_refresh_message = u'Refresh' self.login_manager.needs_refresh_message_category = 'refresh' category_filter = [self.login_manager.needs_refresh_message_category] with self.app.test_client() as c: c.get('/login-notch-remember') c.get('/needs-refresh') msgs = get_flashed_messages(category_filter=category_filter) self.assertIn(self.login_manager.needs_refresh_message, msgs) def test_needs_refresh_flash_message_localized(self): def _gettext(msg): if msg == u'Refresh': return u'Aktualisieren' self.login_manager.refresh_view = '/refresh_view' self.login_manager.localize_callback = _gettext self.login_manager.needs_refresh_message = u'Refresh' self.login_manager.needs_refresh_message_category = 'refresh' category_filter = [self.login_manager.needs_refresh_message_category] with self.app.test_client() as c: c.get('/login-notch-remember') c.get('/needs-refresh') msgs = get_flashed_messages(category_filter=category_filter) self.assertIn(u'Aktualisieren', msgs) self.login_manager.localize_callback = None def test_needs_refresh_aborts_401(self): with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(result.status_code, 401) def test_redirects_to_refresh_view(self): @self.app.route('/refresh-view') def refresh_view(): return '' self.login_manager.refresh_view = 'refresh_view' with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(result.status_code, 302) expected = 'http://localhost/refresh-view?next=%2Fneeds-refresh' self.assertEqual(result.location, expected) def test_confirm_login(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.assertEqual(u'False', c.get('/is-fresh').data.decode('utf-8')) c.get('/confirm-login') self.assertEqual(u'True', c.get('/is-fresh').data.decode('utf-8')) def test_user_login_confirmed_signal_fired(self): with self.app.test_client() as c: with listen_to(user_login_confirmed) as listener: c.get('/confirm-login') listener.assert_heard_one(self.app) def test_session_not_modified(self): with self.app.test_client() as c: # Within the request we think we didn't modify the session. self.assertEquals( u'modified=False', c.get('/empty_session').data.decode('utf-8')) # But after the request, the session could be modified by the # "after_request" handlers that call _update_remember_cookie. # Ensure that if nothing changed the session is not modified. self.assertFalse(session.modified) # # Session Protection # def test_session_protection_basic_passes_successive_requests(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username') self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'True', fresh_result.data.decode('utf-8')) def test_session_protection_strong_passes_successive_requests(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username') self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'True', fresh_result.data.decode('utf-8')) def test_session_protection_basic_marks_session_unfresh(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'False', fresh_result.data.decode('utf-8')) def test_session_protection_basic_fires_signal(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_basic_skips_when_remember_me(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') # clear session to force remember me (and remove old session id) self._delete_session(c) # should not trigger protection because "sess" is empty with listen_to(session_protected) as listener: c.get('/username') listener.assert_heard_none(self.app) def test_session_protection_strong_skips_when_remember_me(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') # clear session to force remember me (and remove old session id) self._delete_session(c) # should not trigger protection because "sess" is empty with listen_to(session_protected) as listener: c.get('/username') listener.assert_heard_none(self.app) def test_permanent_strong_session_protection_marks_session_unfresh(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-permanent') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'False', fresh_result.data.decode('utf-8')) def test_permanent_strong_session_protection_fires_signal(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-permanent') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_strong_deletes_session(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: # write some unrelated data in the session, to ensure it does not # get destroyed with c.session_transaction() as sess: sess['foo'] = 'bar' c.get('/login-notch-remember') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Anonymous', username_result.data.decode('utf-8')) with c.session_transaction() as sess: self.assertIn('foo', sess) self.assertEqual('bar', sess['foo']) def test_session_protection_strong_fires_signal_user_agent(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_strong_fires_signal_x_forwarded_for(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember', headers=[('X-Forwarded-For', '10.1.1.1')]) with listen_to(session_protected) as listener: c.get('/username', headers=[('X-Forwarded-For', '10.1.1.2')]) listener.assert_heard_one(self.app) def test_session_protection_skip_when_off_and_anonymous(self): with self.app.test_client() as c: # no user access with listen_to(user_accessed) as user_listener: results = c.get('/') user_listener.assert_heard_none(self.app) # access user with no session data with listen_to(session_protected) as session_listener: results = c.get('/username') self.assertEqual(results.data.decode('utf-8'), u'Anonymous') session_listener.assert_heard_none(self.app) # verify no session data has been set self.assertFalse(session) def test_session_protection_skip_when_basic_and_anonymous(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: # no user access with listen_to(user_accessed) as user_listener: results = c.get('/') user_listener.assert_heard_none(self.app) # access user with no session data with listen_to(session_protected) as session_listener: results = c.get('/username') self.assertEqual(results.data.decode('utf-8'), u'Anonymous') session_listener.assert_heard_none(self.app) # verify no session data has been set self.assertFalse(session) # # Custom Token Loader # def test_custom_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) # Test that remember me functionality still works self.assertEqual(u'Notch', c.get('/username').data.decode('utf-8')) # Test that we used the custom authentication token remember_cookie = self._get_remember_cookie(c) expected_value = make_secure_token(u'Notch', key='deterministic') self.assertEqual(expected_value, remember_cookie.value) def test_change_api_key_with_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.app.config['SECRET_KEY'] = 'ima change this now' result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Notch') def test_custom_token_loader_with_no_user(self): @self.login_manager.token_loader def load_token(token): return with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') # # Lazy Access User # def test_requests_without_accessing_session(self): with self.app.test_client() as c: c.get('/login-notch') # no session access with listen_to(user_accessed) as listener: c.get('/') listener.assert_heard_none(self.app) # should have a session access with listen_to(user_accessed) as listener: result = c.get('/username') listener.assert_heard_one(self.app) self.assertEqual(result.data.decode('utf-8'), u'Notch') # # View Decorators # def test_login_required_decorator(self): @self.app.route('/protected') @login_required def protected(): return u'Access Granted' with self.app.test_client() as c: result = c.get('/protected') self.assertEqual(result.status_code, 401) c.get('/login-notch') result2 = c.get('/protected') self.assertIn(u'Access Granted', result2.data.decode('utf-8')) def test_decorators_are_disabled(self): @self.app.route('/protected') @login_required @fresh_login_required def protected(): return u'Access Granted' self.app.login_manager._login_disabled = True with self.app.test_client() as c: result = c.get('/protected') self.assertIn(u'Access Granted', result.data.decode('utf-8')) def test_fresh_login_required_decorator(self): @self.app.route('/very-protected') @fresh_login_required def very_protected(): return 'Access Granted' with self.app.test_client() as c: result = c.get('/very-protected') self.assertEqual(result.status_code, 401) c.get('/login-notch-remember') logged_in_result = c.get('/very-protected') self.assertEqual(u'Access Granted', logged_in_result.data.decode('utf-8')) self._delete_session(c) stale_result = c.get('/very-protected') self.assertEqual(stale_result.status_code, 401) c.get('/confirm-login') refreshed_result = c.get('/very-protected') self.assertEqual(u'Access Granted', refreshed_result.data.decode('utf-8')) # # Misc # @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_chinese_user_agent(self): with self.app.test_client() as c: result = c.get('/', headers=[('User-Agent', u'中文')]) self.assertEqual(u'Welcome!', result.data.decode('utf-8')) @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_russian_cp1251_user_agent(self): with self.app.test_client() as c: headers = [('User-Agent', u'ЯЙЮя'.encode('cp1251'))] response = c.get('/', headers=headers) self.assertEqual(response.data.decode('utf-8'), u'Welcome!') def test_make_secure_token_default_key(self): # Old test: 0f05743a2b617b2625362ab667c0dbdf4c9ec13a # New test with sha512: h1 = "47bec94a46a5d3939ca0671b01bafd5d7d5353941791734ec1e4734de40e5ce0" h2 = "a45c05c17cd33b8a18840991bb1cc154fa4ee8ef2f80a572b5a6a24b3a3afc20" with self.app.test_request_context(): self.assertEqual(make_secure_token('foo'), h1 + h2) def test_user_context_processor(self): with self.app.test_request_context(): _ucp = self.app.context_processor(_user_context_processor) self.assertIsInstance(_ucp()['current_user'], AnonymousUserMixin)
class LoginTestCase(unittest.TestCase): ''' Tests for results of the login_user function ''' def setUp(self): self.app = Flask(__name__) self.app.config['SECRET_KEY'] = 'deterministic' self.app.config['SESSION_PROTECTION'] = None self.app.config['TESTING'] = True self.remember_cookie_name = 'remember' self.app.config['REMEMBER_COOKIE_NAME'] = self.remember_cookie_name self.login_manager = LoginManager() self.login_manager.init_app(self.app) self.login_manager._login_disabled = False @self.app.route('/') def index(): return u'Welcome!' @self.app.route('/secret') def secret(): return self.login_manager.unauthorized() @self.app.route('/login-notch') def login_notch(): return unicode(login_user(notch)) @self.app.route('/login-notch-remember') def login_notch_remember(): return unicode(login_user(notch, remember=True)) @self.app.route('/login-notch-permanent') def login_notch_permanent(): session.permanent = True return unicode(login_user(notch)) @self.app.route('/needs-refresh') def needs_refresh(): return self.login_manager.needs_refresh() @self.app.route('/confirm-login') def _confirm_login(): confirm_login() return u'' @self.app.route('/username') def username(): if current_user.is_authenticated(): return current_user.name return u'Anonymous' @self.app.route('/is-fresh') def is_fresh(): return unicode(login_fresh()) @self.app.route('/logout') def logout(): return unicode(logout_user()) @self.login_manager.user_loader def load_user(user_id): return USERS[int(user_id)] @self.app.route('/empty_session') def empty_session(): return unicode(u'modified=%s' % session.modified) # This will help us with the possibility of typoes in the tests. Now # we shouldn't have to check each response to help us set up state # (such as login pages) to make sure it worked: we will always # get an exception raised (rather than return a 404 response) @self.app.errorhandler(404) def handle_404(e): raise e unittest.TestCase.setUp(self) def _get_remember_cookie(self, test_client): our_cookies = test_client.cookie_jar._cookies['localhost.local']['/'] return our_cookies[self.remember_cookie_name] def _delete_session(self, c): # Helper method to cause the session to be deleted # as if the browser was closed. This will remove # the session regardless of the permament flag # on the session! with c.session_transaction() as sess: sess.clear() # # Login # def test_test_request_context_users_are_anonymous(self): with self.app.test_request_context(): self.assertTrue(current_user.is_anonymous()) def test_defaults_anonymous(self): with self.app.test_client() as c: result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_login_user(self): with self.app.test_request_context(): result = login_user(notch) self.assertTrue(result) self.assertEqual(current_user.name, u'Notch') def test_login_user_emits_signal(self): with self.app.test_request_context(): with listen_to(user_logged_in) as listener: login_user(notch) listener.assert_heard_one(self.app, user=notch) def test_login_inactive_user(self): with self.app.test_request_context(): result = login_user(creeper) self.assertTrue(current_user.is_anonymous()) self.assertFalse(result) def test_login_inactive_user_forced(self): with self.app.test_request_context(): login_user(creeper, force=True) self.assertEqual(current_user.name, u'Creeper') # # Logout # def test_logout_logs_out_current_user(self): with self.app.test_request_context(): login_user(notch) logout_user() self.assertTrue(current_user.is_anonymous()) def test_logout_emits_signal(self): with self.app.test_request_context(): login_user(notch) with listen_to(user_logged_out) as listener: logout_user() listener.assert_heard_one(self.app, user=notch) # # Unauthorized # def test_unauthorized_fires_unauthorized_signal(self): with self.app.test_client() as c: with listen_to(user_unauthorized) as listener: c.get('/secret') listener.assert_heard_one(self.app) def test_unauthorized_flashes_message_with_login_view(self): self.login_manager.login_view = '/login' expected_message = self.login_manager.login_message = u'Log in!' expected_category = self.login_manager.login_message_category = 'login' with self.app.test_client() as c: c.get('/secret') msgs = get_flashed_messages(category_filter=[expected_category]) self.assertEqual([expected_message], msgs) def test_unauthorized_uses_authorized_handler(self): @self.login_manager.unauthorized_handler def _callback(): return Response('This is secret!', 401) with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) self.assertEqual(u'This is secret!', result.data.decode('utf-8')) def test_unauthorized_aborts_with_401(self): with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 401) def test_unauthorized_redirects_to_login_view(self): self.login_manager.login_view = 'login' @self.app.route('/login') def login(): return 'Login Form Goes Here!' with self.app.test_client() as c: result = c.get('/secret') self.assertEqual(result.status_code, 302) self.assertEqual(result.location, 'http://localhost/login?next=%2Fsecret') # # Session Persistence/Freshness # def test_login_persists(self): with self.app.test_client() as c: c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_logout_persists(self): with self.app.test_client() as c: c.get('/login-notch') c.get('/logout') result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') def test_incorrect_id_logs_out(self): # Ensure that any attempt to reload the user by the ID # will seem as if the user is no longer valid @self.login_manager.user_loader def new_user_loader(user_id): return with self.app.test_client() as c: # Successfully logs in c.get('/login-notch') result = c.get('/username') self.assertEqual(u'Anonymous', result.data.decode('utf-8')) def test_authentication_is_fresh(self): with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/is-fresh') self.assertEqual(u'True', result.data.decode('utf-8')) def test_remember_me(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) result = c.get('/username') self.assertEqual(u'Notch', result.data.decode('utf-8')) def test_remember_me_uses_custom_cookie_parameters(self): name = self.app.config['REMEMBER_COOKIE_NAME'] = 'myname' duration = self.app.config['REMEMBER_COOKIE_DURATION'] = \ timedelta(days=2) domain = self.app.config['REMEMBER_COOKIE_DOMAIN'] = '.localhost.local' with self.app.test_client() as c: c.get('/login-notch-remember') # TODO: Is there a better way to test this? self.assertTrue(domain in c.cookie_jar._cookies, 'Custom domain not found as cookie domain') domain_cookie = c.cookie_jar._cookies[domain] self.assertTrue(name in domain_cookie['/'], 'Custom name not found as cookie name') cookie = domain_cookie['/'][name] expiration_date = datetime.fromtimestamp(cookie.expires) expected_date = datetime.now() + duration difference = expected_date - expiration_date fail_msg = 'The expiration date {0} was far from the expected {1}' fail_msg = fail_msg.format(expiration_date, expected_date) self.assertLess(difference, timedelta(seconds=10), fail_msg) self.assertGreater(difference, timedelta(seconds=-10), fail_msg) def test_remember_me_is_unfresh(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.assertEqual(u'False', c.get('/is-fresh').data.decode('utf-8')) def test_user_loaded_from_cookie_fired(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) with listen_to(user_loaded_from_cookie) as listener: c.get('/username') listener.assert_heard_one(self.app, user=notch) def test_logout_stays_logged_out_with_remember_me(self): with self.app.test_client() as c: c.get('/login-notch-remember') c.get('/logout') result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') def test_needs_refresh_uses_handler(self): @self.login_manager.needs_refresh_handler def _on_refresh(): return u'Needs Refresh!' with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(u'Needs Refresh!', result.data.decode('utf-8')) def test_needs_refresh_fires_needs_refresh_signal(self): with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(user_needs_refresh) as listener: c.get('/needs-refresh') listener.assert_heard_one(self.app) def test_needs_refresh_fires_flash_when_redirect_to_refresh_view(self): self.login_manager.refresh_view = '/refresh_view' self.login_manager.needs_refresh_message = u'Refresh' self.login_manager.needs_refresh_message_category = 'refresh' category_filter = [self.login_manager.needs_refresh_message_category] with self.app.test_client() as c: c.get('/login-notch-remember') c.get('/needs-refresh') msgs = get_flashed_messages(category_filter=category_filter) self.assertIn(self.login_manager.needs_refresh_message, msgs) def test_needs_refresh_aborts_403(self): with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(result.status_code, 403) def test_redirects_to_refresh_view(self): @self.app.route('/refresh-view') def refresh_view(): return '' self.login_manager.refresh_view = 'refresh_view' with self.app.test_client() as c: c.get('/login-notch-remember') result = c.get('/needs-refresh') self.assertEqual(result.status_code, 302) expected = 'http://localhost/refresh-view?next=%2Fneeds-refresh' self.assertEqual(result.location, expected) def test_confirm_login(self): with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.assertEqual(u'False', c.get('/is-fresh').data.decode('utf-8')) c.get('/confirm-login') self.assertEqual(u'True', c.get('/is-fresh').data.decode('utf-8')) def test_user_login_confirmed_signal_fired(self): with self.app.test_client() as c: with listen_to(user_login_confirmed) as listener: c.get('/confirm-login') listener.assert_heard_one(self.app) def test_session_not_modified(self): with self.app.test_client() as c: # Within the request we think we didn't modify the session. self.assertEquals(u'modified=False', c.get('/empty_session').data.decode('utf-8')) # But after the request, the session could be modified by the # "after_request" handlers that call _update_remember_cookie. # Ensure that if nothing changed the session is not modified. self.assertFalse(session.modified) # # Session Protection # def test_session_protection_basic_marks_session_unfresh(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'False', fresh_result.data.decode('utf-8')) def test_session_protection_basic_fires_signal(self): self.app.config['SESSION_PROTECTION'] = 'basic' with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_permanent_strong_session_protection_marks_session_unfresh(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-permanent') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Notch', username_result.data.decode('utf-8')) fresh_result = c.get('/is-fresh') self.assertEqual(u'False', fresh_result.data.decode('utf-8')) def test_permanent_strong_session_protection_fires_signal(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-permanent') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_strong_deletes_session(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') username_result = c.get('/username', headers=[('User-Agent', 'different')]) self.assertEqual(u'Anonymous', username_result.data.decode('utf-8')) def test_session_protection_strong_fires_signal_user_agent(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember') with listen_to(session_protected) as listener: c.get('/username', headers=[('User-Agent', 'different')]) listener.assert_heard_one(self.app) def test_session_protection_strong_fires_signal_x_forwarded_for(self): self.app.config['SESSION_PROTECTION'] = 'strong' with self.app.test_client() as c: c.get('/login-notch-remember', headers=[('X-Forwarded-For', '10.1.1.1')]) with listen_to(session_protected) as listener: c.get('/username', headers=[('X-Forwarded-For', '10.1.1.2')]) listener.assert_heard_one(self.app) # # Custom Token Loader # def test_custom_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) # Test that remember me functionality still works self.assertEqual(u'Notch', c.get('/username').data.decode('utf-8')) # Test that we used the custom authentication token remember_cookie = self._get_remember_cookie(c) expected_value = make_secure_token(u'Notch', key='deterministic') self.assertEqual(expected_value, remember_cookie.value) def test_change_api_key_with_token_loader(self): @self.login_manager.token_loader def load_token(token): return USER_TOKENS.get(token) with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) self.app.config['SECRET_KEY'] = 'ima change this now' result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Notch') def test_custom_token_loader_with_no_user(self): @self.login_manager.token_loader def load_token(token): return with self.app.test_client() as c: c.get('/login-notch-remember') self._delete_session(c) result = c.get('/username') self.assertEqual(result.data.decode('utf-8'), u'Anonymous') # # View Decorators # def test_login_required_decorator(self): @self.app.route('/protected') @login_required def protected(): return u'Access Granted' with self.app.test_client() as c: result = c.get('/protected') self.assertEqual(result.status_code, 401) c.get('/login-notch') result2 = c.get('/protected') self.assertIn(u'Access Granted', result2.data.decode('utf-8')) def test_decorators_are_disabled(self): @self.app.route('/protected') @login_required @fresh_login_required def protected(): return u'Access Granted' self.app.login_manager._login_disabled = True with self.app.test_client() as c: result = c.get('/protected') self.assertIn(u'Access Granted', result.data.decode('utf-8')) def test_fresh_login_required_decorator(self): @self.app.route('/very-protected') @fresh_login_required def very_protected(): return 'Access Granted' with self.app.test_client() as c: result = c.get('/very-protected') self.assertEqual(result.status_code, 401) c.get('/login-notch-remember') logged_in_result = c.get('/very-protected') self.assertEqual(u'Access Granted', logged_in_result.data.decode('utf-8')) self._delete_session(c) stale_result = c.get('/very-protected') self.assertEqual(stale_result.status_code, 403) c.get('/confirm-login') refreshed_result = c.get('/very-protected') self.assertEqual(u'Access Granted', refreshed_result.data.decode('utf-8')) # # Misc # @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_chinese_user_agent(self): with self.app.test_client() as c: result = c.get('/', headers=[('User-Agent', u'中文')]) self.assertEqual(u'Welcome!', result.data.decode('utf-8')) @unittest.skipIf(werkzeug_version.startswith("0.9"), "wait for upstream implementing RFC 5987") def test_russian_cp1251_user_agent(self): with self.app.test_client() as c: headers = [('User-Agent', u'ЯЙЮя'.encode('cp1251'))] response = c.get('/', headers=headers) self.assertEqual(response.data.decode('utf-8'), u'Welcome!') def test_make_secure_token_default_key(self): with self.app.test_request_context(): self.assertEqual(make_secure_token('foo'), '0f05743a2b617b2625362ab667c0dbdf4c9ec13a') def test_user_context_processor(self): self.assertEqual(_user_context_processor(), {'current_user': None})