4

我一直在寻找一种方法来通过在 http 标头中传递的用户和密码来验证用户。

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

这个想法是检查传递的凭据是否允许用户查看 *the_resource*,如果不返回 401 - 禁止。

我只找到了必须有登录和注销视图的身份验证策略示例,或者我不知道如何与 Pyramid 的 ACL 绑定的基本身份验证策略。

我将不胜感激任何帮助,如何开始。

我又想到了一件事。如何强制此 pup-up 登录窗口进行基本身份验证?

4

2 回答 2

12

最后,如何使用身份验证和授权变得清晰。一切都是实际写的,我只是没有立即抓住这个概念。我会试着写下我是如何用一种笨拙的方式解释它的,我不得不向自己解释。我希望它对某人有用。最后的来源可能有助于理解我的写作;)欢迎所有评论。如果我有什么不对的地方,请纠正我。

验证

最重要的是基本身份验证,其中 BasicAuthenticationPolicy 必须具有可以稍后在金字塔应用程序中使用的方法 - 例如 authenticated_userid(request)。这些方法使用 _get_basicauth_credentials() 提取在 http 标头中传递的登录名和密码。实际检查登录名和密码是否正确发生在 mycheck() 中。

现在在 __init__.py 中,我们必须将带有方法 mycheck 的 BasicAuthenticationPolicy 作为参数添加到我们的应用程序配置器中,以便金字塔可以使用它。

在身份验证问题上就是这样。现在您应该能够使用 authenticated_userid(request) 验证是否以及谁进行了身份验证(请参阅views.py)

授权

要对资源使用金字塔授权,我们需要在 __init__.py 中将 ACLAuthorizationPolicy 添加到我们的配置器中,并将 __acl__ 添加到资源中。在最简单的情况下,root_factory(参见thisthis)ACL 定义了哪个组有什么权限。如果我没有记错 (Allow, 'group:viewers', 'view') 'group:viewers' 必须是身份验证方法 - 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

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

模型.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 {}    
于 2012-05-12T08:54:10.070 回答
3

那么你要的是基本身份验证。您链接到您想要使用的食谱。这处理识别用户和计算他们的委托人。ACL 系统使用主体以及视图上指定的权限来确定允许/拒绝访问。

我认为诀窍在于弄清楚如何处理拒绝用户访问资源的问题,这在该配方中没有列举。您可以通过提供一个自定义的“禁止视图”来做到这一点,当视图拒绝访问时,该视图会在 URL 上调用。此时,Basic 声明您向客户提出挑战。

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

这是未经测试的,但可以让您大致了解如何使用禁止视图。这将挑战客户端,然后他们可以自由地发出另一个请求(希望使用凭据),该请求会变成映射到您关心的权限的主体。

于 2012-05-11T17:05:10.630 回答