diff --git a/example/app.py b/example/app.py index 11f74a8..a649e3b 100644 --- a/example/app.py +++ b/example/app.py @@ -78,6 +78,11 @@ def create_app(auth_config): def http(): return render_template('index.html', content='HTTP Authentication') + @app.route('/http_custom_realm') + @http_auth_required('My Realm') + def http_custom_realm(): + return render_template('index.html', content='HTTP Authentication') + @app.route('/token') @auth_token_required def token(): diff --git a/flask_security/core.py b/flask_security/core.py index dd347d0..b12fb1d 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -57,7 +57,7 @@ _default_config = { 'CONFIRM_SALT': 'confirm-salt', 'RESET_SALT': 'reset-salt', 'AUTH_SALT': 'auth-salt', - 'DEFAULT_HTTP_AUTH_HEADER': 'Basic realm="Login Required"' + 'DEFAULT_HTTP_AUTH_REALM': 'Login Required' } diff --git a/flask_security/decorators.py b/flask_security/decorators.py index e909b49..c5a100f 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -78,23 +78,26 @@ def _check_http_auth(): return app.security.pwd_context.verify(auth.password, user.password) -def http_auth_required(auth_header=None): +def http_auth_required(realm): """Decorator that protects endpoints using Basic HTTP authentication.""" - def wrapper(fn): + def decorator(fn): @wraps(fn) - def decorated(*args, **kwargs): + def wrapper(*args, **kwargs): if _check_http_auth(): return fn(*args, **kwargs) - header = auth_header or _security.default_http_auth_header - headers = {'WWW-Authenticate': header} + r = _security.default_http_auth_realm if callable(realm) else realm + h = {'WWW-Authenticate': 'Basic realm="%s"' % r} - return _get_unauthorized_response(headers=headers) + return _get_unauthorized_response(headers=h) - return decorated + return wrapper - return wrapper + if callable(realm): + return decorator(realm) + + return decorator def auth_token_required(fn): diff --git a/tests/functional_tests.py b/tests/functional_tests.py index 925b5ba..1f74580 100644 --- a/tests/functional_tests.py +++ b/tests/functional_tests.py @@ -2,6 +2,7 @@ from __future__ import with_statement +import base64 import time try: @@ -130,8 +131,30 @@ class DefaultSecurityTests(SecurityTest): r = self._get('/token', headers={"X-Auth-Token": 'X'}) self.assertEqual(401, r.status_code) + def test_http_auth(self): + r = self._get('/http', headers={ + 'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:password") + }) + self.assertIn('HTTP Authentication', r.data) -class ConfiguredURLTests(SecurityTest): + def test_invalid_http_auth(self): + r = self._get('/http', headers={ + 'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:bogus") + }) + self.assertIn('

Unauthorized

', r.data) + self.assertIn('WWW-Authenticate', r.headers) + self.assertEquals('Basic realm="Login Required"', r.headers['WWW-Authenticate']) + + def test_custom_http_auth_realm(self): + r = self._get('/http_custom_realm', headers={ + 'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:bogus") + }) + self.assertIn('

Unauthorized

', r.data) + self.assertIn('WWW-Authenticate', r.headers) + self.assertEquals('Basic realm="My Realm"', r.headers['WWW-Authenticate']) + + +class ConfiguredSecurityTests(SecurityTest): AUTH_CONFIG = { 'SECURITY_REGISTERABLE': True, @@ -141,7 +164,8 @@ class ConfiguredURLTests(SecurityTest): 'SECURITY_POST_LOGIN_VIEW': '/post_login', 'SECURITY_POST_LOGOUT_VIEW': '/post_logout', 'SECURITY_POST_REGISTER_VIEW': '/post_register', - 'SECURITY_UNAUTHORIZED_VIEW': '/unauthorized' + 'SECURITY_UNAUTHORIZED_VIEW': '/unauthorized', + 'SECURITY_DEFAULT_HTTP_AUTH_REALM': 'Custom Realm' } def test_login_view(self): @@ -171,6 +195,14 @@ class ConfiguredURLTests(SecurityTest): msg = 'You are not allowed to access the requested resouce' self.assertIn(msg, r.data) + def test_default_http_auth_realm(self): + r = self._get('/http', headers={ + 'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:bogus") + }) + self.assertIn('

Unauthorized

', r.data) + self.assertIn('WWW-Authenticate', r.headers) + self.assertEquals('Basic realm="Custom Realm"', r.headers['WWW-Authenticate']) + class RegisterableTests(SecurityTest): AUTH_CONFIG = {