New file |
| | |
| | | """ Simple BFG application demonstrating use of repoze.who in "hybrid" mode. |
| | | |
| | | - repoze.who middleware intercepts and validates existing request credentials, |
| | | leaving 'REMOTE_USER' in the WSGI environ if they are OK> |
| | | |
| | | - Application handles login / logout directly, using the repoze.who API |
| | | to validate credentials and set headers. |
| | | """ |
| | | import logging |
| | | import os |
| | | import sys |
| | | from StringIO import StringIO |
| | | |
| | | from paste.httpserver import serve |
| | | from repoze.bfg.authentication import RemoteUserAuthenticationPolicy |
| | | from repoze.bfg.authorization import ACLAuthorizationPolicy |
| | | from repoze.bfg.configuration import Configurator |
| | | from repoze.bfg.security import Allow |
| | | from repoze.bfg.security import Authenticated |
| | | from repoze.bfg.security import DENY_ALL |
| | | from repoze.bfg.security import Everyone |
| | | from repoze.who.api import get_api |
| | | from repoze.who.interfaces import IChallenger |
| | | from repoze.who.middleware import PluggableAuthenticationMiddleware as PAM |
| | | from repoze.who.plugins.basicauth import BasicAuthPlugin |
| | | from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin |
| | | from repoze.who.plugins.redirector import RedirectorPlugin |
| | | from repoze.who.plugins.htpasswd import HTPasswdPlugin |
| | | from repoze.who.classifiers import default_request_classifier |
| | | from repoze.who.classifiers import default_challenge_decider |
| | | from webob import Response |
| | | from webob.exc import HTTPFound |
| | | |
| | | LINK = ' <p><a href="%(url)s">%(title)s</a></p>' |
| | | |
| | | ACTIONS = { |
| | | 'root': {'url': '/', 'title': 'Root'}, |
| | | 'protected': {'url': '/protected.html', 'title': 'Protected'}, |
| | | 'login': {'url': '/login.html', 'title': 'Login'}, |
| | | 'logout': {'url': '/logout.html', 'title': 'Logout'}, |
| | | } |
| | | |
| | | def _actions(request): |
| | | names = ['root'] |
| | | if 'REMOTE_USER' in request.environ: |
| | | names.append('protected') |
| | | names.append('logout') |
| | | else: |
| | | names.append('login') |
| | | return '\n'.join([LINK % ACTIONS[x] for x in names]) |
| | | |
| | | |
| | | PAGE = """\ |
| | | <html> |
| | | <body> |
| | | <h1>%(page_title)s</h1> |
| | | %(actions)s |
| | | </body> |
| | | </html> |
| | | """ |
| | | |
| | | def unprotected(request): |
| | | return Response(PAGE % {'page_title': 'Unprotected Page', |
| | | 'actions': _actions(request), |
| | | }) |
| | | |
| | | def protected(request): |
| | | return Response(PAGE % {'page_title': 'protected Page', |
| | | 'actions': _actions(request), |
| | | }) |
| | | |
| | | |
| | | LOGIN_FORM = """\ |
| | | <html> |
| | | <body> |
| | | <h1> Log In </h1> |
| | | <form method="POST"> |
| | | %(came_from)s |
| | | <p>%(message)s</p> |
| | | <p>Login name: <input type="text" name="login" /></p> |
| | | <p>Password: <input type="password" name="password" /></p> |
| | | <p><input type="submit" name="form.login" value="Log In"/></p> |
| | | </form> |
| | | </body> |
| | | </html> |
| | | """ |
| | | |
| | | def login(request): |
| | | message = '' |
| | | info = {} |
| | | |
| | | # Remember any 'came_from', for redirection on succcesful login. |
| | | came_from = request.params.get('came_from') |
| | | if came_from is not None: |
| | | info['came_from'] = ( |
| | | '<input type="hidden" name="came_from" value="%s" />' % came_from) |
| | | else: |
| | | info['came_from'] = '' |
| | | |
| | | who_api = get_api(request.environ) |
| | | if 'form.login' in request.POST: |
| | | # Validate credentials. |
| | | creds = {} |
| | | creds['login'] = request.POST['login'] |
| | | creds['password'] = request.POST['password'] |
| | | authenticated, headers = who_api.login(creds) |
| | | |
| | | if authenticated: |
| | | # Redirect to 'came_from', or to root. |
| | | # headers here are "remember" headers, setting the |
| | | # auth_tkt cookies. |
| | | return HTTPFound(location=came_from or '/', |
| | | headers=headers) |
| | | |
| | | else: |
| | | message = 'Invalid login.' |
| | | else: |
| | | # Forcefully forget any existing credentials. |
| | | _, headers = who_api.login({}) |
| | | |
| | | # Headers here are "forget" headers, clearing the auth_tkt cookies. |
| | | request.response_headerlist = headers |
| | | |
| | | if 'REMOTE_USER' in request.environ: |
| | | del request.environ['REMOTE_USER'] |
| | | |
| | | info['message'] = message |
| | | |
| | | return Response(LOGIN_FORM % info) |
| | | |
| | | |
| | | def logout(request): |
| | | # Use repoze.who API to get "forget" headers. |
| | | who_api = get_api(request.environ) |
| | | return HTTPFound(location='/', headers=who_api.logout()) |
| | | |
| | | |
| | | class Root(object): |
| | | __acl__ = [(Allow, Authenticated, ('view_protected',)), |
| | | (Allow, Everyone, ('view',)), |
| | | DENY_ALL, |
| | | ] |
| | | |
| | | def get_root(*args, **kw): |
| | | return Root() |
| | | |
| | | |
| | | if __name__ == '__main__': |
| | | # Configure the BFG application |
| | | |
| | | ## Set up security policies, root object, etc. |
| | | authentication_policy=RemoteUserAuthenticationPolicy() |
| | | authorization_policy=ACLAuthorizationPolicy() |
| | | config = Configurator( |
| | | root_factory=get_root, |
| | | default_permission='view', |
| | | authentication_policy=authentication_policy, |
| | | authorization_policy=authorization_policy, |
| | | ) |
| | | config.begin() |
| | | |
| | | ## Configure views |
| | | config.add_view(unprotected) |
| | | config.add_view(protected, 'protected.html', permission='view_protected') |
| | | config.add_view(login, 'login.html') |
| | | config.add_view(logout, 'logout.html') |
| | | config.end() |
| | | |
| | | ## Create the app object. |
| | | app = config.make_wsgi_app() |
| | | |
| | | # Configure the repoze.who middleware: |
| | | |
| | | ## fake .htpasswd authentication source |
| | | io = StringIO() |
| | | for name, password in [('admin', 'admin'), |
| | | ('user', 'user')]: |
| | | io.write('%s:%s\n' % (name, password)) |
| | | io.seek(0) |
| | | def cleartext_check(password, hashed): |
| | | return password == hashed |
| | | htpasswd = HTPasswdPlugin(io, cleartext_check) |
| | | |
| | | ## other plugins |
| | | basicauth = BasicAuthPlugin('repoze.who') |
| | | auth_tkt = AuthTktCookiePlugin('secret', 'auth_tkt') |
| | | redirector = RedirectorPlugin(login_url='/login.html') |
| | | redirector.classifications = {IChallenger:['browser'] } # only for browser |
| | | |
| | | ## group / order plugins by function |
| | | identifiers = [('auth_tkt', auth_tkt), |
| | | ('basicauth', basicauth)] |
| | | authenticators = [('auth_tkt', auth_tkt), |
| | | ('htpasswd', htpasswd)] |
| | | challengers = [('redirector', redirector), |
| | | ('basicauth', basicauth)] |
| | | mdproviders = [] |
| | | |
| | | ## set up who logging, if desired |
| | | log_stream = None |
| | | if os.environ.get('WHO_LOG'): |
| | | log_stream = sys.stdout |
| | | |
| | | # Wrap the middleware around the application. |
| | | middleware = PAM(app, |
| | | identifiers, |
| | | authenticators, |
| | | challengers, |
| | | mdproviders, |
| | | default_request_classifier, |
| | | default_challenge_decider, |
| | | log_stream = log_stream, |
| | | log_level = logging.DEBUG |
| | | ) |
| | | |
| | | # Serve up the WSGI stack. |
| | | serve(middleware, host='0.0.0.0') |