Merge lp:~hazmat/pyjuju/security-principal-def into lp:pyjuju

Proposed by Kapil Thangavelu
Status: Merged
Approved by: Gustavo Niemeyer
Approved revision: 270
Merged at revision: 275
Proposed branch: lp:~hazmat/pyjuju/security-principal-def
Merge into: lp:pyjuju
Diff against target: 166 lines (+157/-0)
2 files modified
ensemble/state/security.py (+62/-0)
ensemble/state/tests/test_security.py (+95/-0)
To merge this branch: bzr merge lp:~hazmat/pyjuju/security-principal-def
Reviewer Review Type Date Requested Status
William Reade (community) Approve
Gustavo Niemeyer Approve
Review via email: mp+67618@code.launchpad.net

Description of the change

Basic security constructs (principals and token db), as a basis for future security integration.

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

That looks awesome. +1!

review: Approve
Revision history for this message
William Reade (fwereade) wrote :

Needs updating from trunk before tests will run on my machine, but otherwise looks good to me :).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'ensemble/state/security.py'
--- ensemble/state/security.py 1970-01-01 00:00:00 +0000
+++ ensemble/state/security.py 2011-07-11 20:16:06 +0000
@@ -0,0 +1,62 @@
1from twisted.internet.defer import inlineCallbacks, returnValue
2from ensemble.state.auth import make_identity
3from ensemble.state.utils import YAMLState
4
5
6class Principal(object):
7 """An ensemble/zookeeper principal.
8 """
9
10 def __init__(self, name, password):
11 self._name = name
12 self._password = password
13
14 @property
15 def name(self):
16 """A principal has a login name."""
17 return self._name
18
19 def get_token(self):
20 """A principal identity token can be retrieved.
21
22 An identity token is used to construct ACLs.
23 """
24 return make_identity("%s:%s" % (self._name, self._password))
25
26 def attach(self, connection):
27 """A princpal can be attached to a connection."""
28 return connection.add_auth(
29 "digest", "%s:%s" % (self._name, self._password))
30
31
32class TokenDatabase(object):
33 """A hash map of principal names to their identity tokens.
34
35 Identity tokens are used to construct node ACLs.
36 """
37 def __init__(self, client, path="/auth-tokens"):
38 self._state = YAMLState(client, path)
39
40 @inlineCallbacks
41 def add(self, principal):
42 """Add a principal to the token database.
43 """
44 yield self._state.read()
45 self._state[principal.name] = principal.get_token()
46 yield self._state.write()
47
48 @inlineCallbacks
49 def get(self, name):
50 """Return the identity token for a principal name.
51 """
52 yield self._state.read()
53 returnValue(self._state[name])
54
55 @inlineCallbacks
56 def remove(self, name):
57 """Remove a principal by name from the token database.
58 """
59 yield self._state.read()
60 if name in self._state:
61 del self._state[name]
62 yield self._state.write()
063
=== added file 'ensemble/state/tests/test_security.py'
--- ensemble/state/tests/test_security.py 1970-01-01 00:00:00 +0000
+++ ensemble/state/tests/test_security.py 2011-07-11 20:16:06 +0000
@@ -0,0 +1,95 @@
1import yaml
2import zookeeper
3
4from twisted.internet.defer import inlineCallbacks
5
6from ensemble.state.auth import make_identity, make_ace
7from ensemble.state.security import Principal, TokenDatabase
8
9from ensemble.lib.testing import TestCase
10from txzookeeper.tests.utils import deleteTree
11
12
13class PrincipalTests(TestCase):
14
15 @inlineCallbacks
16 def setUp(self):
17 zookeeper.set_debug_level(0)
18 self.client = yield self.get_zookeeper_client().connect()
19
20 def tearDown(self):
21 deleteTree(handle=self.client.handle)
22 self.client.close()
23
24 def test_name(self):
25 """Principals have names."""
26 principal = Principal("foobar", "secret")
27 self.assertEqual(principal.name, "foobar")
28
29 def test_get_token(self):
30 """An identity token can be gotten from a Principal."""
31 principal = Principal("foobar", "secret")
32 self.assertEqual(principal.get_token(),
33 make_identity("foobar:secret"))
34
35 @inlineCallbacks
36 def test_activate(self):
37 """A principal can be used with a client connection."""
38 client = yield self.get_zookeeper_client().connect()
39 self.addCleanup(lambda: client.close())
40 admin_credentials = "admin:admin"
41 test_credentials = "test:test"
42 self.client.add_auth("digest", admin_credentials)
43
44 acl = [make_ace(make_identity(admin_credentials), all=True),
45 make_ace(make_identity(
46 test_credentials), read=True, create=True)]
47
48 yield client.create("/acl-test", "content", acls=acl)
49
50 # Verify the acl is active
51 yield self.assertFailure(
52 client.get("/acl-test"), zookeeper.NoAuthException)
53
54 # Attach the principal to the connection
55 principal = Principal("test", "test")
56 yield principal.attach(client)
57 content, stat = yield client.get("/acl-test")
58 self.assertEqual(content, "content")
59
60
61class TokenDatabaseTest(TestCase):
62
63 @inlineCallbacks
64 def setUp(self):
65 zookeeper.set_debug_level(0)
66 self.client = yield self.get_zookeeper_client().connect()
67 self.db = TokenDatabase(self.client, "/token-test")
68
69 def tearDown(self):
70 deleteTree(handle=self.client.handle)
71 self.client.close()
72
73 @inlineCallbacks
74 def test_add(self):
75 principal = Principal("zebra", "zoo")
76 yield self.db.add(principal)
77 content, stat = yield self.client.get("/token-test")
78 data = yaml.load(content)
79 self.assertEqual(data, {"zebra": principal.get_token()})
80
81 @inlineCallbacks
82 def test_remove(self):
83 principal = Principal("zebra", "zoo")
84 yield self.db.add(principal)
85 yield self.db.remove(principal)
86 content, stat = yield self.client.get("/token-test")
87 data = yaml.load(content)
88 self.assertEqual(data, {"zebra": principal.get_token()})
89
90 @inlineCallbacks
91 def test_get(self):
92 principal = Principal("zebra", "zoo")
93 yield self.db.add(principal)
94 token = yield self.db.get(principal.name)
95 self.assertEqual(token, principal.get_token())

Subscribers

People subscribed via source and target branches

to status/vote changes: