Commits

Steven Kryskalla committed 4b25f1c

adding code from barrel-0.1.3.tar.gz

  • Participants
  • Parent commits 48b5aff

Comments (0)

Files changed (10)

+= barrel - Flexible WSGI authentication and authorization tools =
+
+This distribution provides a variety of access control capabilities.
+The classes have useful defaults yet are highly configurable. 
+All interfaces are designed to be easily customized and extended.
+
+See docstrings of the various modules for details.
+
+== License ==
+
+Created and maintained by Luke Arno <luke.arno@gmail.com>
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+

File barrel/__init__.py

+"""barrel - Flexible WSGI authentication and authorization tools
+
+This distribution provides a variety of access control capabilities.
+The classes have useful defaults yet are highly configurable. 
+All interfaces are designed to be easily customized and extended.
+
+(See the docstrings of the various modules.)
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""
+
+

File barrel/basic.py

+"""barrel.basic - basic authentication support
+
+(See the docstrings of the various functions and classes.)
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""
+
+class BasicAuth(object):
+    """HTTP Basic authentication middleware.
+    
+    Also the base class for other authentication methods.
+    """
+
+    session_key = 'barrel.session'
+    session_user_key = 'barrel.user'
+    realm = 'WSGIBarrel'
+    users = None
+
+    def __init__(self, app, users=None):
+        """Take the app to wrap and optional settings."""
+        self.app = app
+        if users is not None:
+            self.users = users
+        elif self.users is None:
+            self.users = []
+
+    def valid_user(self, username, password):
+        """Is this a valid username/password? (True or False)"""
+        for usr, pwd in self.users:
+            if username == usr and password == pwd:
+                return True
+        else:
+            return False
+
+    def session_dict(self, environ):
+        """Get the session for caching username.
+        
+        The default place to look for a session is where
+        flup puts it.
+        """
+        return environ.get(self.session_key)
+
+    def save_session(self):
+        """Save out the session.
+
+        Replace with a do-nothing if you use a package that does
+        not require you to explicitly save out sessions.
+        """
+        session = self.session_dict()
+        if session is not None:
+            return session.save()
+
+    def cache_username(self, environ, username):
+        """Store the username in a session dict if found.
+        
+        Also populates REMOTE_USER.
+        """
+        environ['REMOTE_USER'] = username
+        session = self.session_dict(environ)
+        if session is not None:
+            session[self.session_user_key] = username
+
+    def get_cached_username(self, environ):
+        """Look for the username in the session if found.
+        
+        Also populates REMOTE_USER if it can.
+        """
+        session = self.session_dict(environ)
+        if session is not None:
+            return session.get(self.session_user_key)
+        else:
+            return None
+
+    def username_and_password(self, environ):
+        """Pull the creds from the AUTHORIZAITON header."""
+        # Should I check the auth type here?
+        auth_string = environ.get('HTTP_AUTHORIZATION')
+        if auth_string is None:
+            return ('', '')
+        else:
+            return auth_string[6:].strip().decode('base64').split(':')
+
+    def authenticate(self, environ):
+        """Is this request from an authenicated user? (True or False)"""
+        username, password = self.username_and_password(environ)
+        if username and password:
+            if self.valid_user(username, password):
+                self.cache_username(environ, username)
+                return True
+        else:
+            username = self.get_cached_username(environ)
+            if username is not None:
+                self.cache_username(environ, username)
+                return True
+            
+        return False
+
+    def not_authenticated(self, environ, start_response):
+        """Respond to an unauthenticated request with a 401."""
+        start_response('401 Unauthorized',
+                        [('WWW-Authenticate', 'Basic realm=' + self.realm)])
+        return ["401 Unauthorized: Please provide credentials."]
+
+    def __call__(self, environ, start_response):
+        """If request is not from an authenticated user, complain."""
+        if self.authenticate(environ):
+            return self.app(environ, start_response)
+            self.save_session()
+        else:
+            return self.not_authenticated(environ, start_response)
+
+

File barrel/combo.py

+"""barrel.combo - access resource via alternate auth methods
+
+(See the docstrings of the various functions and classes.)
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""
+
+from barrel.basic import BasicAuth
+from barrel.form import FormAuth
+
+
+class BasicFormAuth(object):
+    """Use basic auth if AUTHORIZATION is sent or fall back to form auth.
+    
+    This is handy if you have both browsers and non-interactive clients
+    accessing the same resources (resource doubles as a web service).
+    """
+
+    basic_auth = BasicAuth
+    form_auth = FormAuth
+
+    def __init__(self, app, users=None):
+        """Create alternate versions of app with middlewares."""
+        self.app = app
+        self.basic_app = self.basic_auth(app, users=users)
+        self.form_app = self.form_auth(app, users=users)
+
+    def __setattr__(self, name, value):
+        """Pass option setting to both versions of app."""
+        self.__dict__[name] = value
+        if name not in ['basic_app', 'form_app']:
+            try:
+                setattr(self.basic_app, name, value)
+            except AttributeError:
+                pass
+            try:
+                setattr(self.form_app, name, value)
+            except AttributeError:
+                pass
+
+    def __call__(self, environ, start_response):
+        """Call basic auth if header is present or call form auth."""
+        if environ.get('HTTP_AUTHORIZATION'):
+            return self.basic_app(environ, start_response)
+        else:
+            return self.form_app(environ, start_response)
+
+

File barrel/cooper.py

+"""barrel.cooper - convenience interface for barrel
+
+(See the docstrings of the various functions and classes.)
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""
+
+
+from barrel.basic import BasicAuth
+from barrel.form import FormAuth
+from barrel.combo import BasicFormAuth
+from barrel.roles import RolesAuthz
+
+
+def decorize(middleware):
+    """Return customizable middleware decorator factory.
+    
+    Returns a decorator factory function that will return
+    decorators that apply the given middleware then assign
+    any keyword args to corresponding attributes of the result.
+    """
+    classname = middleware.__class__
+    def metadeco(config=None, *args, **kwargs):
+        """Decorate with middleware then set attribs."""
+        stringkwargs = ["%s=%s" % (key, repr(val)) 
+                        for (key, val) in kwargs.iteritems()]
+        def deco(app):
+            """Decorated with middleware and attribs set."""
+            newapp = middleware(app)
+            for key, value in kwargs.iteritems():
+                try:
+                    setattr(newapp, key, value)
+                except AttributeError:
+                    pass
+            return newapp
+        deco.__doc__ = """Decorate given app with %s
+        
+        Sets attributes on the resulting object (if it can):
+        
+        %s
+        """ % (middleware.__name__, "\n      ".join(stringkwargs))
+        return deco
+    metadeco.__doc__ = ("Create decorator that will use %s "
+                        "with given attributes.") % classname
+    return metadeco
+
+
+basicauth = decorize(BasicAuth)
+formauth = decorize(FormAuth)
+comboauth = decorize(BasicFormAuth)
+rolesauth = decorize(RolesAuthz)
+

File barrel/form.py

+"""barrel.form - web form based authentication support
+
+(See the docstrings of the various functions and classes.)
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""
+
+
+import string
+import cgi
+from cStringIO import StringIO
+
+
+from barrel.basic import BasicAuth
+
+default_template = """<?xml version="1.0"?>
+<!DOCTYPE html 
+  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>Form Auth</title>
+</head>
+<body>
+    <h1>Resource Requires Authentication</h1>
+    <form method="POST" action="">
+        <fieldset>
+            <legend>$message:</legend>
+            <label for="$user_field">Username:</label>
+            <input type="text" 
+                   name="$user_field" 
+                   id="$user_field" 
+                   value="$username"/>
+            <br/>
+            <label for="$pass_field">Password:</label>
+            <input type="password" name="$pass_field" id="$pass_field"/>
+            <br/>
+            <button type="submit" 
+                    name="$button" 
+                    id="$button"
+                    value="submit">Sign In</button>
+        </fieldset>
+    </form>
+</body>
+</html>"""
+
+
+class FormAuth(BasicAuth):
+    """Web Form authentication middleware."""
+    
+    user_field = 'username'
+    pass_field = 'password'
+    button = 'barrel-form-button'
+    environ_user_key = 'barrel.form.username'
+
+    first_message = "Please enter your username and password"
+    failed_message = "Sign in failed; please try again"
+    
+    template = string.Template(default_template)
+
+    def username_and_password(self, environ):
+        """Pull the creds from the form encoded request body."""
+        # How else can I tell if this is an auth request before reading?
+        if environ.get('CONTENT_LENGTH'):
+            clen = int(environ['CONTENT_LENGTH'])
+            sio = StringIO(environ['wsgi.input'].read(clen))
+            fs = cgi.FieldStorage(fp=sio,
+                                  environ=environ,
+                                  keep_blank_values=True)
+            sio.seek(0)
+            environ['wsgi.input'] = sio
+            if fs.getlist(self.button):
+                try:
+                    username = fs[self.user_field].value
+                    password = fs[self.pass_field].value
+                    environ[self.environ_user_key] = username
+                    return username, password
+                except KeyError:
+                    pass # silence
+        
+        return '', ''
+
+    def not_authenticated(self, environ, start_response):
+        """Respond to an unauthenticated request with a form."""
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        username = environ.get(self.environ_user_key, '')
+        if username:
+            message = self.failed_message
+        else:
+            message = self.first_message
+        return [self.template.safe_substitute(user_field=self.user_field,
+                                              pass_field=self.pass_field,
+                                              button=self.button,
+                                              username=username,
+                                              message=message,
+                                              **environ)]
+
+

File barrel/roles.py

+"""barrel.roles - roles based authorization support
+
+(See the docstrings of the various functions and classes.)
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""      
+
+
+class RolesAuthz(object):
+    """Authorize (or not) by compairing user roles to allowed roles."""
+
+    environ_roles_key = 'barrel.roles'
+    session_key = 'com.saddi.service.session'
+    session_roles_key = 'barrel.roles' 
+
+    def __init__(self, app, allowed_roles=None, roles_dict=None):
+        """Take the app to wrap and optional settings."""
+        self.app = app
+        if allowed_roles is None:
+            self.allowed_roles = []
+        else:
+            self.allowed_roles = allowed_roles
+        if roles_dict is None:
+            self.roles_dict = {}
+        else:
+            self.roles_dict = roles_dict
+
+    def user_roles(self, username):
+        """Get a list of roles for the given username.""" 
+        return self.roles_dict.get(username, [])
+
+    def look_up_roles(self, environ):
+        """Find the username in environ and use it to get a list of roles."""
+        roles_list = self.user_roles(environ.get('REMOTE_USER', ''))
+        self.cache_roles(environ, roles_list)
+        return roles_list
+
+    def get_roles(self, environ):
+        """Get user roles from cache or by looking them up."""
+        cached_roles = self.get_cached_roles(environ)
+        if cached_roles is not None:
+            return cached_roles
+        else:
+            return self.look_up_roles(environ)
+ 
+    def session_dict(self, environ):
+        """Get the session for caching username.
+        
+        The default place to look for a session is where
+        flup puts it.
+        """
+        service = environ.get(self.session_key)
+        if service:
+            return service.session
+
+    def cache_roles(self, environ, roles_list):
+        """Store roles in environ and session if found."""
+        environ[self.environ_roles_key] = roles_list
+        session = self.session_dict(environ)
+        if session:
+            session[self.session_roles_key] = roles_list
+
+    def get_cached_roles(self, environ):
+        """Find roles in environ or session if found."""
+        roles = environ.get(self.environ_roles_key)
+        if roles:
+            return roles
+        else:
+            session = self.session_dict(environ)
+            if session:
+                return session.get(self.session_roles_key)
+
+    def authorize(self, environ):
+        """Is this request from a user with the required roles?"""
+        for user_role in self.look_up_roles(environ):
+            if user_role in self.allowed_roles:
+                return True
+        return False
+
+    def not_authorized(self, environ, start_response):
+        """Respond to an unauthorized request with a 403."""
+        start_response('403 Forbidden', [])
+        return ["403 Forbidden: You are not allowed to access that."]
+
+    def __call__(self, environ, start_response):
+        """If request is not from an authorized user, complain."""
+        if self.authorize(environ):
+            return self.app(environ, start_response)
+        else:
+            return self.not_authorized(environ, start_response)
+
+

File barrel/yalecas.py

+"""yalecas - Yale CAS compatible authentication module.
+
+This is still pretty limited.
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""
+
+import urllib
+from cgi import parse_qs
+from xml.dom import minidom
+from wsgiref.util import request_uri
+
+from barrel.basic import BasicAuth
+
+
+class CASAuth(BasicAuth):
+
+    def __init__(self, app, cas_login_uri, cas_validate_uri):
+        """Take the app to wrap and optional settings."""
+        self.app = app
+        self.cas_login_uri = cas_login_uri
+        self.cas_validate_uri = cas_validate_uri
+
+    def validate_ticket(self, environ):
+        """"""
+        qs = parse_qs(environ['QUERY_STRING'])
+        ticket = qs.get('ticket', [None])[0]
+        if ticket is None:
+            return None
+        uri = self.validate_uri(environ, ticket, qs)
+        handle = urllib.urlopen(uri)
+        text = handle.read()
+        print text
+        xml = minidom.parseString(text)
+        user = xml.getElementsByTagName('cas:user')
+        if user:
+            return user[0].childNodes[0].data
+        else:
+            return None
+
+    def validate_uri(self, environ, ticket, qs):
+        cas_qs = {}
+        cas_qs['service'] = request_uri(environ, include_query=0)
+        print cas_qs['service']
+        new_qs = []
+        for k, v in qs.iteritems():
+            print k
+            if k != 'ticket':
+                for vv in v:
+                    new_qs.append((k, vv))
+        if new_qs:
+            cas_qs['service'] += '?' + urllib.urlencode(new_qs)
+        print cas_qs['service']
+        cas_qs['ticket'] = ticket
+        return self.cas_validate_uri + '?' + urllib.urlencode(cas_qs)
+
+    def login_uri(self, environ):
+        """"""
+        qs = {}
+        qs['service'] = request_uri(environ)
+        print qs['service']
+        return self.cas_login_uri + '?' + urllib.urlencode(qs)
+
+    def authenticate(self, environ):
+        """Is this request from an authenicated user? (True or False)"""
+        username = self.get_cached_username(environ)
+        if username is not None:
+            self.cache_username(environ, username)
+            return True
+        else:
+            username = self.validate_ticket(environ)
+            if username is not None:
+                self.cache_username(environ, username)
+                return True
+            else:
+                return False
+
+    def not_authenticated(self, environ, start_response):
+        """Respond to an unauthenticated request with a 401."""
+        uri = self.login_uri(environ)
+        start_response('302 Found',
+                        [('Location', uri)])
+        return ["Redirect to CAS: " + uri]
+
+    def __call__(self, environ, start_response):
+        """If request is not from an authenticated user, complain."""
+        if self.authenticate(environ):
+            return self.app(environ, start_response)
+        else:
+            return self.not_authenticated(environ, start_response)
+
+
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
+"""setup - setuptools based setup for barrel
+
+Copyright (C) 2006-2008 Luke Arno - http://lukearno.com/
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to:
+
+The Free Software Foundation, Inc., 
+51 Franklin Street, Fifth Floor, 
+Boston, MA  02110-1301, USA.
+
+Luke Arno can be found at http://lukearno.com/
+
+"""
+
+try:
+    from setuptools import setup
+except:
+    from distutils.core import setup
+
+setup(name='barrel',
+      version='0.1.3',
+      description=\
+        'Flexible WSGI authentication and authorization tools.',
+      long_description="""\
+This distribution provides a variety of access control middlewares.
+Convenience functions which can be used as decorators are included.
+The classes have useful defaults yet are highly configurable. 
+All interfaces are designed to be easily customized and extended.
+Currently supports HTTP Basic and Web forms authentication, 
+roles-based authorization and is beaker session compatible out of
+the box. (It is no problem to use some other type of sessions or none.)""",
+      author='Luke Arno',
+      author_email='luke.arno@gmail.com',
+      url='http://lukearno.com/projects/barrel/',
+      license="GPL2",
+      py_modules=[],
+      packages = ['barrel'],
+      keywords="wsgi authentication authorization web http webapps",
+      classifiers=['Development Status :: 3 - Alpha',
+                   'Environment :: Web Environment',
+                   'Intended Audience :: Developers',
+                   'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
+                   'Natural Language :: English',
+                   'Operating System :: OS Independent',
+                   'Programming Language :: Python',
+                   'Topic :: Security',
+                   'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware',
+                   'Topic :: Software Development :: Libraries',
+                   'Topic :: Utilities'])
+