Created
November 20, 2018 07:09
-
-
Save luhn/4cba3eeb26c295ce70e2feb4da04e757 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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, | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 :)