def __call__(self, path): """Given a node path, determine the ACL. """ acl_entries = [] for rule in self._rules: entries = yield rule(self, path) if entries: acl_entries.extend(entries) # XXX/TODO - Remove post security-integration # Allow incremental integration if not acl_entries: acl_entries.append(ZOO_OPEN_AUTH_ACL_UNSAFE) # Give cli admin access by default admin_token = yield self._token_db.get("admin") acl_entries.append(make_ace(admin_token, all=True)) # Give owner access by default if self._owner: acl_entries.append( make_ace(self._owner.get_token(), all=True)) returnValue(acl_entries)
def test_activate(self): """A principal can be used with a client connection.""" client = yield self.get_zookeeper_client().connect() self.addCleanup(lambda: client.close()) admin_credentials = "admin:admin" test_credentials = "test:test" yield self.client.add_auth("digest", admin_credentials) acl = [ make_ace(make_identity(admin_credentials), all=True), make_ace(make_identity(test_credentials), read=True, create=True) ] yield client.create("/acl-test", "content", acls=acl) # Verify the acl is active yield self.assertFailure(client.get("/acl-test"), zookeeper.NoAuthException) # Attach the principal to the connection group = GroupPrincipal(self.client, "/group-b") yield group.create("test", "test") yield group.attach(client) content, stat = yield client.get("/acl-test") self.assertEqual(content, "content")
def test_activate(self): """A principal can be used with a client connection.""" client = yield self.get_zookeeper_client().connect() self.addCleanup(lambda: client.close()) admin_credentials = "admin:admin" test_credentials = "test:test" yield self.client.add_auth("digest", admin_credentials) acl = [make_ace(make_identity(admin_credentials), all=True), make_ace(make_identity( test_credentials), read=True, create=True)] yield client.create("/acl-test", "content", acls=acl) # Verify the acl is active yield self.assertFailure( client.get("/acl-test"), zookeeper.NoAuthException) # Attach the principal to the connection group = GroupPrincipal(self.client, "/group-b") yield group.create("test", "test") yield group.attach(client) content, stat = yield client.get("/acl-test") self.assertEqual(content, "content")
def test_make_ace_with_nonbool_raises(self): """Permissions can only be enabled via ACL, other usage raises.""" identity = "admin:max" try: make_ace(identity, write=None, create=True) except SyntaxError, e: self.assertEqual( e.args[0], "Permissions can only be enabled via ACL - %s" % "write")
def test_rule_match_suppress_open_access(self): """If a rule returns an acl, then no default access is given.""" principal = Principal("foobar", "foobar") self.policy.add_rule(lambda policy, path: [ make_ace(principal.get_token(), all=True)]) acl = yield self.policy("/random") # Check for matched rule ACL self.assertIn(make_ace(principal.get_token(), all=True), acl) # Verify no default access self.assertNotIn(make_ace("auth", "world", all=True), acl)
def test_rule_match_suppress_open_access(self): """If a rule returns an acl, then no default access is given.""" principal = Principal("foobar", "foobar") self.policy.add_rule( lambda policy, path: [make_ace(principal.get_token(), all=True)]) acl = yield self.policy("/random") # Check for matched rule ACL self.assertIn(make_ace(principal.get_token(), all=True), acl) # Verify no default access self.assertNotIn(make_ace("auth", "world", all=True), acl)
def test_rule_that_returns_deferred(self): """If a rule may do additional lookups, resulting in deferred values. """ principal = Principal("foobar", "foobar") self.policy.add_rule(lambda policy, path: succeed( [make_ace(principal.get_token(), all=True)])) acl = yield self.policy("/random") # Check for matched rule ACL self.assertIn(make_ace(principal.get_token(), all=True), acl) # Verify no default access self.assertNotIn(make_ace("auth", "world", all=True), acl)
def test_rule_that_returns_deferred(self): """If a rule may do additional lookups, resulting in deferred values. """ principal = Principal("foobar", "foobar") self.policy.add_rule(lambda policy, path: succeed([ make_ace(principal.get_token(), all=True)])) acl = yield self.policy("/random") # Check for matched rule ACL self.assertIn(make_ace(principal.get_token(), all=True), acl) # Verify no default access self.assertNotIn(make_ace("auth", "world", all=True), acl)
def test_prohibit_not_in_acl(self): principal = Principal("zebra", "stripes") yield self.tokens.add(principal) path = yield self.client.create( "/abc", acls=[make_ace(self.admin.get_token(), all=True)]) acl = ACL(self.client, path) # We get to the same end state so its fine. yield acl.prohibit("zebra") acl, stat = yield self.client.get_acl(path) self.assertEqual(acl, [make_ace(self.admin.get_token(), all=True)])
def test_prohibit(self): principal = Principal("zebra", "stripes") yield self.tokens.add(principal) path = yield self.client.create("/abc", acls=[ make_ace(self.admin.get_token(), all=True), make_ace(principal.get_token(), write=True)]) acl = ACL(self.client, path) yield acl.prohibit("zebra") acl, stat = yield self.client.get_acl(path) self.assertEqual( acl, [make_ace(self.admin.get_token(), all=True)])
def test_prohibit_not_in_acl(self): principal = Principal("zebra", "stripes") yield self.tokens.add(principal) path = yield self.client.create("/abc", acls=[ make_ace(self.admin.get_token(), all=True)]) acl = ACL(self.client, path) # We get to the same end state so its fine. yield acl.prohibit("zebra") acl, stat = yield self.client.get_acl(path) self.assertEqual( acl, [make_ace(self.admin.get_token(), all=True)])
def test_create_with_policy(self): """If a policy is set ACL are determined by the policy.""" def rule(policy, path): return [make_ace(Principal("magic", "not").get_token(), all=True)] self.policy.add_rule(rule) self.client.set_security_policy(self.policy) yield self.client.create("/xyz") acl, stat = yield self.client.get_acl("/xyz") self.assertEqual(acl, [ make_ace(Principal("magic", "not").get_token(), all=True), make_ace(Principal("admin", "admin").get_token(), all=True) ])
def test_owner_ace(self): """If an owner is set, all nodes ACLs will have an owner ACE. """ owner = Principal("john", "doe") self.policy.set_owner(owner) acl = yield self.policy("/random") self.assertIn(make_ace(owner.get_token(), all=True), acl)
def create(self, name, password=None, otp_name=None, otp=None): """Create an OTP for a principal. """ if self._name: raise ValueError("OTPPrincipal has already been created.") self._name = name self._password = password or self._generate_string() self._otp_name = otp_name or self._generate_string() self._otp = otp or self._generate_string() acl = [ make_ace(make_identity("%s:%s" % (self._otp_name, self._otp)), read=True) ] # Optional additional ACL entry for unit test teardown. if self._extra_otp_ace: acl.append(self._extra_otp_ace) self._path = yield self._client.create(self._path, yaml.safe_dump( dict(name=name, password=password)), acls=acl, flags=SEQUENCE) returnValue(self)
def test_world_scheme(self): identity = "anyone" result = make_ace(identity, scheme="world", all=True) self.assertEqual(result, {"perms": zookeeper.PERM_ALL, "scheme": "world", "id": "anyone"})
def test_create_with_policy(self): """If a policy is set ACL are determined by the policy.""" def rule(policy, path): return [make_ace(Principal("magic", "not").get_token(), all=True)] self.policy.add_rule(rule) self.client.set_security_policy(self.policy) yield self.client.create("/xyz") acl, stat = yield self.client.get_acl("/xyz") self.assertEqual( acl, [make_ace(Principal("magic", "not").get_token(), all=True), make_ace(Principal("admin", "admin").get_token(), all=True)])
def create(self, name, password=None, otp_name=None, otp=None): """Create an OTP for a principal. """ if self._name: raise ValueError("OTPPrincipal has already been created.") self._name = name self._password = password or self._generate_string() self._otp_name = otp_name or self._generate_string() self._otp = otp or self._generate_string() acl = [make_ace( make_identity("%s:%s" % (self._otp_name, self._otp)), read=True)] # Optional additional ACL entry for unit test teardown. if self._extra_otp_ace: acl.append(self._extra_otp_ace) self._path = yield self._client.create( self._path, yaml.safe_dump(dict(name=name, password=password)), acls=acl, flags=SEQUENCE) returnValue(self)
def test_grant_additive(self): path = yield self.client.create("/abc") acl = ACL(self.client, "/abc") yield acl.grant("admin", read=True) yield acl.grant("admin", write=True) test_ace = make_ace(":", read=True, write=True) node_acl, stat = yield self.client.get_acl(path) self.assertEqual(node_acl[-1]["perms"], test_ace["perms"])
def test_prohibit(self): principal = Principal("zebra", "stripes") yield self.tokens.add(principal) path = yield self.client.create("/abc", acls=[ make_ace(self.admin.get_token(), all=True), make_ace(principal.get_token(), write=True) ]) acl = ACL(self.client, path) yield acl.prohibit("zebra") acl, stat = yield self.client.get_acl(path) self.assertEqual(acl, [make_ace(self.admin.get_token(), all=True)])
def test_make_ace(self): identity = "admin:moss" ace = make_ace(identity, write=True, create=True) self.assertEqual(ace["id"], identity) self.assertEqual(ace["scheme"], "digest") self.assertEqual( ace["perms"], zookeeper.PERM_WRITE|zookeeper.PERM_CREATE)
def test_default_no_rules_gives_global_authenticated_access(self): """If no rules match, the default acl gives authenticated users access. XXX/TODO: This is intended as a temporary crutch for integration of the security machinery, not a long term solution. """ acl = yield self.policy("/random") self.assertIn(make_ace("auth", "world", all=True), acl)
def test_grant(self): path = yield self.client.create("/abc") acl = ACL(self.client, path) yield acl.grant("admin", all=True) node_acl, stat = yield self.client.get_acl(path) self.assertEqual( node_acl, [ZOO_OPEN_ACL_UNSAFE, make_ace(self.admin.get_token(), all=True)])
def test_add_member(self): group = GroupPrincipal(self.client, "/group-a") yield group.create("group/a", "zebra") principal = Principal("aladdin", "genie") yield group.add_member(principal) acl, stat = yield self.client.get_acl("/group-a") self.assertEqual(acl[1:], [make_ace(principal.get_token(), read=True)]) # Adding a member again is fine yield group.add_member(principal)
def test_add_member(self): group = GroupPrincipal(self.client, "/group-a") yield group.create("group/a", "zebra") principal = Principal("aladdin", "genie") yield group.add_member(principal) acl, stat = yield self.client.get_acl("/group-a") self.assertEqual( acl[1:], [make_ace(principal.get_token(), read=True)]) # Adding a member again is fine yield group.add_member(principal)
def test_acl_without_admin(self): """A client needs an attached principle with the admin perm to set acl. """ client = yield self.get_zookeeper_client().connect() principal = Principal("zebra", "stripes") yield self.tokens.add(principal) attach_deferred = principal.attach(client) yield self.client.create( "/abc", acls=[make_ace(self.admin.get_token(), all=True)]) yield attach_deferred acl = ACL(client, "/abc") yield self.assertFailure(acl.grant("zebra", all=True), zookeeper.NoAuthException)
def __call__(self, path): """Given a node path, determine the ACL. """ acl_entries = [] for rule in self._rules: entries = yield rule(self, path) if entries: acl_entries.extend(entries) # XXX/TODO - Remove post security-integration # Allow incremental integration if not acl_entries: acl_entries.append(ZOO_OPEN_AUTH_ACL_UNSAFE) # Give cli admin access by default admin_token = yield self._token_db.get("admin") acl_entries.append(make_ace(admin_token, all=True)) # Give owner access by default if self._owner: acl_entries.append(make_ace(self._owner.get_token(), all=True)) returnValue(acl_entries)
def grant(self, principal_name, **perms): """Grant permissions on node to the given principal name.""" token = yield self._token_db.get(principal_name) ace = make_ace(token, **perms) def add(acl): index = self._principal_index(acl, principal_name) if index is not None: acl_ace = acl[index] acl_ace["perms"] = ace["perms"] | acl_ace["perms"] return acl acl.append(ace) return acl yield self._update_acl(add)
def test_acl_without_admin(self): """A client needs an attached principle with the admin perm to set acl. """ client = yield self.get_zookeeper_client().connect() principal = Principal("zebra", "stripes") yield self.tokens.add(principal) attach_deferred = principal.attach(client) yield self.client.create( "/abc", acls=[make_ace(self.admin.get_token(), all=True)]) yield attach_deferred acl = ACL(client, "/abc") yield self.assertFailure( acl.grant("zebra", all=True), zookeeper.NoAuthException)
def add_member(self, principal): """Add a principal as a member of the group. A member of the group can use the group as an additional principal attached to the connection. """ self._check() acl, stat = yield self._client.get_acl(self._path) token = principal.get_token() for ace in acl: if ace["id"] == token: return acl.append(make_ace(token, read=True)) yield self._client.set_acl(self._path, acl)
def test_default_no_owner_no_rules_gives_admin_access(self): """By default the policy setups a global access for the cli admins. """ acl = yield self.policy("/random") self.assertIn( make_ace(Principal("admin", "admin").get_token(), all=True), acl)
import base64 import random import string import yaml from zookeeper import (BadArgumentsException, BadVersionException, NoNodeException, NodeExistsException, SEQUENCE) from twisted.internet.defer import inlineCallbacks, returnValue from txzookeeper.client import ZOO_OPEN_ACL_UNSAFE, ZookeeperClient from juju.state.auth import make_identity, make_ace from juju.state.errors import (StateNotFound, PrincipalNotFound) from juju.state.utils import YAMLState ZOO_OPEN_AUTH_ACL_UNSAFE = make_ace("auth", "world", all=True) class Principal(object): """An juju/zookeeper principal. """ def __init__(self, name, password): self._name = name self._password = password @property def name(self): """A principal has a login name.""" return self._name def get_token(self):
def rule(policy, path): return [make_ace(Principal("magic", "not").get_token(), all=True)]
import yaml from zookeeper import ( BadArgumentsException, BadVersionException, NoNodeException, NodeExistsException, SEQUENCE) from twisted.internet.defer import inlineCallbacks, returnValue from txzookeeper.client import ZOO_OPEN_ACL_UNSAFE, ZookeeperClient from juju.state.auth import make_identity, make_ace from juju.state.errors import ( StateNotFound, PrincipalNotFound) from juju.state.utils import YAMLState ZOO_OPEN_AUTH_ACL_UNSAFE = make_ace("auth", "world", all=True) class Principal(object): """An juju/zookeeper principal. """ def __init__(self, name, password): self._name = name self._password = password @property def name(self): """A principal has a login name.""" return self._name