I'm using pyramid, and I've set up pretty basic security/ACL. I have a few pages which I want to deny access to for authenticated users (registration, login, etc.) which is easy enough by using this in my acl:

(Deny, Authenticated, 'guest'),

The problem is that if I have this too, it ignores the later 'Deny':

(Allow, Everyone, 'guest'),

So my thought was to add a principal on unauthenticated users which I could hook in to (seeing as there is Authenticated, but no Unauthenticated.

def authenticated(userid, request):
    if userid == unauthenticated_userid(request):
        return ['auth:guest']

    user = User.get_by_username(userid)

    if not user:
        None

    if user.admin:
        return ['group:admins', 'group:users']

    return ['group:users']

The problem is that the AuthTktAuthenticationPolicy layer doesn't appear to call the callback function if the user isn't authenticated (preferring instead to just give the principal ['system.Everyone'] and call it a day).

So what, if anything, am I missing here?

Full ACL, security, and config below:

class Root(object):
    __name__ = None
    __parent__ = None
    __acl__ = [
        (Allow, Everyone, 'view'),
        (Allow, 'auth:guest', 'guest'),
        (Deny, Authenticated, 'guest'),
        (Allow, Authenticated, 'auth'),
        (Allow, 'group:admins', 'admin'),
    ]

    def __init__(self, request):
        self.request = request

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)

    authn_policy = AuthTktAuthenticationPolicy('devdbcookiesig',
                                               callback=authenticated,
                                               hashalg='sha512')
    authz_policy = ACLAuthorizationPolicy()

    Base.metadata.bind = engine
    config = Configurator(settings=settings,
                          root_factory=Root)

    config.set_authentication_policy(authn_policy)
    config.set_authorization_policy(authz_policy)

    config.include('pyramid_chameleon')

    config.add_static_view('static', 'static', cache_max_age=3600)

    config.set_request_property(get_user, 'user', reify=True)

    # ... the rest is standard routing

security.py:

from model.user import User
from pyramid.security import unauthenticated_userid

def get_user(request):
    # the below line is just an example, use your own method of
    # accessing a database connection here (this could even be another
    # request property such as request.db, implemented using this same
    # pattern).
    userid = unauthenticated_userid(request)
    if userid is not None:
        # this should return None if the user doesn't exist
        # in the database
        return User.get_by_username(userid)

def authenticated(userid, request):
    if userid == unauthenticated_userid(request):
        return ['auth:guest']

    user = User.get_by_username(userid)

    if not user:
        None

    if user.admin:
        return ['group:admins', 'group:users']

    return ['group:users']

And finally the error:

HTTPForbidden: debug_authorization of url http://localhost/signin (view name u'' against context <devdb.Root object at 0x3dd1f10>): ACLDenied permission 'guest' via ACE '<default deny>' in ACL [('Allow', 'system.Everyone', 'view'), ('Allow', 'auth:guest', 'guest'), ('Deny', 'system.Authenticated', 'guest'), ('Allow', 'system.Authenticated', 'auth'), ('Allow', 'group:admins', 'admin')] on context <devdb.Root object at 0x3dd1f10> for principals ['system.Everyone']
有帮助吗?

解决方案

Figured it out (kind of). The order of items in the ACL matters, so if I put Deny for authenticated on guest before I put Allow for everyone, it works.

__acl__ = [
    (Allow, Everyone, 'view'),
    (Deny, Authenticated, 'guest'),
    (Allow, Everyone, 'guest'),
    (Allow, Authenticated, 'auth'),
    (Allow, 'group:admins', 'admin'),
]

works while

__acl__ = [
    (Allow, Everyone, 'view'),
    (Allow, Everyone, 'guest'),
    (Deny, Authenticated, 'guest'),
    (Allow, Authenticated, 'auth'),
    (Allow, 'group:admins', 'admin'),
]

does not (authenticated users pick up "Allow Everyone" before they run into "Deny Authenticated")

Still doesn't solve the problem of "why can't I add custom principals to unauthenticated users", but it gets me what I need.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top