аутентификация с заголовком http в пирамиде

Я искал способ аутентификации пользователя по пользователю и паролю, переданному в заголовке http.

curl --user user1:pass1 http://localhost:6543/the_resource

Идея состоит в том, чтобы проверить, позволяют ли переданные учетные данные пользователю просматривать * the_resource *, и если нет, возвращать 401 - Запрещено.

Я нашел только примеры политики аутентификации, в которых должен быть режим входа и выхода или эта базовая политика аутентификации, которую я не знаю, как связать с ACL Pyramid.

Буду признателен за любую помощь, как начать.

Мне пришло в голову еще одно. Как заставить это окно входа в систему для базовой аутентификации?


person mdob    schedule 11.05.2012    source источник


Ответы (2)


В итоге стало понятно как пользоваться аутентификацией и авторизацией. На самом деле все было написано, я просто не сразу уловил замысел. Я постараюсь написать, как у меня это заработало, объясняя по-нубски, что мне пришлось объяснять самому себе. Я надеюсь, что это будет полезно для кого-то. Источники, в конце концов, могут помочь понять, что я пишу ;) Все комментарии приветствуются. Если я что-то неправильно понял, пожалуйста, поправьте меня.

Аутентификация

Наиболее важной является базовая аутентификация, в которой BasicAuthenticationPolicy иметь методы, которые можно использовать позже в приложении пирамиды, например, authentication_userid(request). Эти методы используют _get_basicauth_credentials(), который извлекает логин и пароль, которые были переданы в заголовке http. Фактическая проверка правильности логина и пароля происходит в mycheck().

Теперь в __init__.py мы должны добавить BasicAuthenticationPolicy с методом mycheck в качестве аргумента в наш конфигуратор приложения, чтобы пирамида могла его использовать.

Что касается аутентификации, то и все. Теперь вы должны быть в состоянии, если и кто был аутентифицирован с использованием authentication_userid (запрос) (см. views.py)

Авторизация

Чтобы использовать авторизацию пирамиды для ресурсов, нам нужно добавить ACLAuthorizationPolicy в наш конфигуратор в __init__.py и добавить __acl__ к ресурсам. В самом простом случае к root_factory (см. это и это) ACL определяет, какая группа имеет какое разрешение. Если я не ошибаюсь в (Разрешить, «группа: просмотрщики», «просмотр»), «группа: просмотрщики» должна быть тем, какой метод аутентификации — mycheck() — возвращает.

Последним шагом в авторизации является добавление разрешения на определенное представление с помощью декоратора (или в add_route). Если мы добавим разрешение ACL — view — на view_page, тогда group:viewers сможет видеть эту страницу (вызов 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

мой проект.__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

модели.py

from pyramid.security import Allow

class RootFactory(object):
    __acl__ = [ (Allow, 'group:viewers', 'view'),
                (Allow, 'group:editors', 'edit') ]
    def __init__(self, request):
        pass

просмотры.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 {}    
person mdob    schedule 12.05.2012

Ну, то, что вы просите, это базовая аутентификация. Вы связались с рецептом, который вы захотите использовать. Это обрабатывает идентификацию пользователей и вычисление их принципалов. Субъекты используются системой ACL вместе с разрешением, указанным в представлении, для определения разрешения/запрета доступа.

Я думаю, что хитрость заключается в том, чтобы выяснить, как справиться с отказом пользователю в доступе к ресурсу, который не указан в этом рецепте. Вы можете сделать это, предоставив настраиваемое «Запрещенное представление», которое вызывается по URL-адресу, когда представление запрещает доступ. В этот момент Basic заявляет, что вы бросаете вызов клиенту.

@forbidden_view_config()
def forbidden_view(request):
    resp = HTTPUnauthorized()
    resp.www_authenticate = 'Basic realm="Secure Area"'
    return resp

Это не проверено, но дает вам общее представление о том, как использовать запрещенное представление. Это бросит вызов клиенту, а затем он сможет сделать другой запрос (надеюсь, с учетными данными), который будет преобразован в участников, сопоставленных с интересующими вас разрешениями.

person Michael Merickel    schedule 11.05.2012