summaryrefslogtreecommitdiff
path: root/macaroonbakery/authorizer.py
diff options
context:
space:
mode:
Diffstat (limited to 'macaroonbakery/authorizer.py')
-rw-r--r--macaroonbakery/authorizer.py107
1 files changed, 107 insertions, 0 deletions
diff --git a/macaroonbakery/authorizer.py b/macaroonbakery/authorizer.py
new file mode 100644
index 0000000..b7128c0
--- /dev/null
+++ b/macaroonbakery/authorizer.py
@@ -0,0 +1,107 @@
+# Copyright 2017 Canonical Ltd.
+# Licensed under the LGPLv3, see LICENCE file for details.
+import abc
+
+import macaroonbakery
+
+
+# EVERYONE is recognized by ACLAuthorizer as the name of a
+# group that has everyone in it.
+EVERYONE = 'everyone'
+
+
+class Authorizer(object):
+ ''' Used to check whether a given user is allowed to perform a set of
+ operations.
+ '''
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def authorize(self, ctx, id, ops):
+ ''' Checks whether the given identity (which will be None when there is
+ no authenticated user) is allowed to perform the given operations.
+ It should raise an exception only when the authorization cannot be
+ determined, not when the user has been denied access.
+
+ On success, each element of allowed holds whether the respective
+ element of ops has been allowed, and caveats holds any additional
+ third party caveats that apply.
+ If allowed is shorter then ops, the additional elements are assumed to
+ be False.
+ ctx(AuthContext) is the context of the authorization request.
+ :return: a list of boolean and a list of caveats
+ '''
+ raise NotImplementedError('authorize method must be defined in '
+ 'subclass')
+
+
+class AuthorizerFunc(Authorizer):
+ ''' Implements a simplified version of Authorizer that operates on a single
+ operation at a time.
+ '''
+ def __init__(self, f):
+ '''
+ :param f: a function that takes an identity that operates on a single
+ operation at a time. Will return if this op is allowed as a boolean and
+ and a list of caveat that holds any additional third party caveats
+ that apply.
+ '''
+ self._f = f
+
+ def authorize(self, ctx, identity, ops):
+ '''Implements Authorizer.authorize by calling f with the given identity
+ for each operation.
+ '''
+ allowed = []
+ caveats = []
+ for op in ops:
+ ok, fcaveats = self._f(ctx, identity, op)
+ allowed.append(ok)
+ if fcaveats is not None:
+ caveats.extend(fcaveats)
+ return allowed, caveats
+
+
+class ACLAuthorizer(Authorizer):
+ ''' ACLAuthorizer is an Authorizer implementation that will check access
+ control list (ACL) membership of users. It uses get_acl to find out
+ the ACLs that apply to the requested operations and will authorize an
+ operation if an ACL contains the group "everyone" or if the identity is
+ an instance of ACLIdentity and its allow method returns True for the ACL.
+ '''
+ def __init__(self, get_acl, allow_public=False):
+ '''
+ :param get_acl get_acl will be called with an auth context and an Op.
+ It should return the ACL that applies (an array of string ids).
+ If an entity cannot be found or the action is not recognised,
+ get_acl should return an empty list but no error.
+ :param allow_public: boolean, If True and an ACL contains "everyone",
+ then authorization will be granted even if there is no logged in user.
+ '''
+ self._allow_public = allow_public
+ self._get_acl = get_acl
+
+ def authorize(self, ctx, identity, ops):
+ '''Implements Authorizer.authorize by calling identity.allow to
+ determine whether the identity is a member of the ACLs associated with
+ the given operations.
+ '''
+ if len(ops) == 0:
+ # Anyone is allowed to do nothing.
+ return [], []
+ allowed = [False] * len(ops)
+ has_allow = isinstance(identity, macaroonbakery.ACLIdentity)
+ for i, op in enumerate(ops):
+ acl = self._get_acl(ctx, op)
+ if has_allow:
+ allowed[i] = identity.allow(ctx, acl)
+ else:
+ allowed[i] = self._allow_public and EVERYONE in acl
+ return allowed, []
+
+
+class ClosedAuthorizer(Authorizer):
+ ''' An Authorizer implementation that will never authorize anything.
+ '''
+ def authorize(self, ctx, id, ops):
+ return [False] * len(ops), []