Skip to content

Instantly share code, notes, and snippets.

@luhn
Created November 20, 2018 07:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luhn/4cba3eeb26c295ce70e2feb4da04e757 to your computer and use it in GitHub Desktop.
Save luhn/4cba3eeb26c295ce70e2feb4da04e757 to your computer and use it in GitHub Desktop.
# Interfaces
class IUserIdentity:
userid = None
class IIdentityPolicy:
def __call__(request):
""" Return the claimed identity of the user associated with the given
request.
"""
class IAuthenticationPolicy:
def __call__(request, identity):
""" Returns true if the claimed identity is truthful for the given
request, false otherwise.
"""
class IAuthorizationPolicy:
def __call__(context, identity, permission, request):
"""
"""
# Following implementations might be provided by Pyramid
@implementer(IUserIdentity)
class PlainUserIdentity(object):
def __init__(self, userid):
self.userid = userid
@implementer(IUserIdentity)
class DictUserIdentity(dict):
def __init__(self, userid, *args, **kwargs):
self.userid = userid
super().__init__(*args, **kwargs)
@implementer(IIdentityPolicy)
class SessionIdentityPolicy:
def __call__(self, request):
return PlainUserIdentity(request.session['userid'])
def remember(self, request, userid):
request.session['userid'] = userid
def forget(self, request):
del request.session['userid']
@implementer(IAuthorizationPolicy)
class TrustAuthenticationPolicy:
""" Trusts all identity. Useful for identity providers that are backed by
tamper-proof storage.
"""
def __call__(self, request, identity):
return True
# Mixin
class AuthRequestMixin:
@property
def unauthenticated_user(self):
return identity_policy(self)
@property
def authenticated_user(self):
identity = self.user_identity(self)
if authentication_policy(self, identity):
return identity
else:
return None
def has_permission(self, permission, context):
identity = self.authenticated_user
return authorization_policy(context, identity, permission, self)
# Bw compat
@implementer(IIdentityPolicy)
class CompatibilityIdentityPolicy(object):
""" Shims legacy authn policies."""
def __init__(self, authn_policy):
self._policy = authn_policy
def __call__(self, request):
userid = self._policy.unauthenticated_userid(request)
return PlainUserIdentity(userid)
@implementer(IAuthenticationPolicy)
class CompatibilityAuthenticationPolicy(object):
""" Shims legacy authn policies."""
def __init__(self, authn_policy):
self._policy = authn_policy
def __call__(self, request, identity):
authed = self._policy.authenticated_userid(request)
return identity.userid == authed
@implementer(IAuthorizationPolicy)
class CompatibilityAuthorizationPolicy(object):
""" Shims legacy authz policies."""
def __init__(self, authn_policy, authz_policy):
self.effective_principles = authn_policy.effective_principles
self._authz_policy = authz_policy
def __call__(self, context, identity, permission, request):
principals = self.effective_principles(request)
return self._authz_policy.permits(context, principals, permission)
class LegacyAuthRequestMixin:
@property
def unauthenticated_userid(self):
return self.unauthenticated_user.userid
@property
def authenticated_userid(self):
return self.authenticated_user.userid
@property
def effective_principals(self):
authz_policy = request.registry.queryUtility(IAuthorizationPolicy)
assert isinstance(authz_policy, CompatibilityAuthorizationPolicy)
return authz_policy.effective_principles(self)
# Application code
def my_authorization(context, identity, permission, request):
permissions = _lookup_user_permissions(identity.userid)
if permission in permissions:
return Allow()
else:
return Deny()
Configurator(
identity_policy=SessionIdentityPolicy(),
authentication_policy=TrustAuthenticationPolicy(),
authorization_policy=my_authorization,
)
@robinharms
Copy link

robinharms commented Nov 20, 2018

Regarding this, please pass along context to any function trying to figure out principals or similar. Basically anything resembling groupfinder or similar must be passed the context from has_permission('Perm', context) etc.

Longer background to this: -> Pylons/pyramid#3268

People who don't use local roles and traversal won't have this problem though :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment