I've been looking for a way to authenticate user by user and password passed in http header.
curl --user user1:pass1 http://localhost:6543/the_resource
The idea is to check if passed credentials allow user to view *the_resource* and if not return 401 - Forbidden.
I've found only examples of authentication policy where there has to be a login and logout view or this basic authentication policy which I don't know how to bind with Pyramid's ACL.
I will appreciate any help, how to start.
One more thing came to my mind. How to force this pup-up login window for basic authentication?
In the end it became clear how to use authentication and authorization. Everything was actually written I just didn't catch the concept at once. I'll try to write how I got it working explaining in a noobish way, which I had to explain it to myself. I hope it will be useful to someone. Sources in the end may help to understand my writing ;) All comments are welcome. If I got something wrong, please correct me.
Most important is the basic authentication in which BasicAuthenticationPolicy must have methods that can be used later in pyramid application - like authenticated_userid(request). These methods use _get_basicauth_credentials() which pulls out login and password that were passed in http header. The actual checking if the login and password are correct happens in mycheck().
Now in __init__.py we must add BasicAuthenticationPolicy with method mycheck as an argument to our application configurator, so the pyramid can use it.
In a matter of authentication that is all. Now you should be able if and who was authenticated using authenticated_userid(request) (see views.py)
To use pyramid authorization to resources we need to add ACLAuthorizationPolicy to our configurator in __init__.py and add __acl__ to the resources. In most simple case to the root_factory (see this and this) ACL defines which group has what permission. If I'm not mistaken in (Allow, 'group:viewers', 'view') 'group:viewers' has to be what authentication method - mycheck() - returns.
The last step in authorization is add permission to certain view using a decorator (or in add_route). If we add the ACL permission - view - to a view_page then group:viewers is allowed to see that page (call view_page).
basic_authentication.py
import binascii
from zope.interface import implements
from paste.httpheaders import AUTHORIZATION
from paste.httpheaders import WWW_AUTHENTICATE
from pyramid.interfaces import IAuthenticationPolicy
from pyramid.security import Everyone
from pyramid.security import Authenticated
import yaml
def mycheck(credentials, request):
login = credentials['login']
password = credentials['password']
USERS = {'user1':'pass1',
'user2':'pass2'}
GROUPS = {'user1':['group:viewers'],
'user2':['group:editors']}
if login in USERS and USERS[login] == password:
return GROUPS.get(login, [])
else:
return None
def _get_basicauth_credentials(request):
authorization = AUTHORIZATION(request.environ)
try:
authmeth, auth = authorization.split(' ', 1)
except ValueError: # not enough values to unpack
return None
if authmeth.lower() == 'basic':
try:
auth = auth.strip().decode('base64')
except binascii.Error: # can't decode
return None
try:
login, password = auth.split(':', 1)
except ValueError: # not enough values to unpack
return None
return {'login':login, 'password':password}
return None
class BasicAuthenticationPolicy(object):
""" A :app:`Pyramid` :term:`authentication policy` which
obtains data from basic authentication headers.
Constructor Arguments
``check``
A callback passed the credentials and the request,
expected to return None if the userid doesn't exist or a sequence
of group identifiers (possibly empty) if the user does exist.
Required.
``realm``
Default: ``Realm``. The Basic Auth realm string.
"""
implements(IAuthenticationPolicy)
def __init__(self, check, realm='Realm'):
self.check = check
self.realm = realm
def authenticated_userid(self, request):
credentials = _get_basicauth_credentials(request)
if credentials is None:
return None
userid = credentials['login']
if self.check(credentials, request) is not None: # is not None!
return userid
def effective_principals(self, request):
effective_principals = [Everyone]
credentials = _get_basicauth_credentials(request)
if credentials is None:
return effective_principals
userid = credentials['login']
groups = self.check(credentials, request)
if groups is None: # is None!
return effective_principals
effective_principals.append(Authenticated)
effective_principals.append(userid)
effective_principals.extend(groups)
return effective_principals
def unauthenticated_userid(self, request):
creds = self._get_credentials(request)
if creds is not None:
return creds['login']
return None
def remember(self, request, principal, **kw):
return []
def forget(self, request):
head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
return head
myproject.__init__.py
from pyramid.config import Configurator
from myproject.resources import Root
from myproject.basic_authentication import BasicAuthenticationPolicy, mycheck
from pyramid.authorization import ACLAuthorizationPolicy
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(root_factory='myproject.models.RootFactory',
settings=settings,
authentication_policy=BasicAuthenticationPolicy(mycheck),
authorization_policy=ACLAuthorizationPolicy(),
)
config.add_static_view('static', 'myproject:static', cache_max_age=3600)
config.add_route('view_page', '/view')
config.add_route('edit_page', '/edit')
config.scan()
app = config.make_wsgi_app()
return app
models.py
from pyramid.security import Allow
class RootFactory(object):
__acl__ = [ (Allow, 'group:viewers', 'view'),
(Allow, 'group:editors', 'edit') ]
def __init__(self, request):
pass
views.py
from pyramid.security import authenticated_userid
from pyramid.view import view_config
#def my_view(request):
# return render_to_response('templates/simple.pt', {})
@view_config(route_name='view_page', renderer='templates/view.pt', permission='view')
def view_page(request):
return {}
@view_config(route_name='edit_page', renderer='templates/edit.pt', permission='edit')
def edit_page(request):
return {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With