Merge lp:~hazmat/pyjuju/security-policy-rules-redux into lp:pyjuju
- security-policy-rules-redux
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~hazmat/pyjuju/security-policy-rules-redux |
Merge into: | lp:pyjuju |
Prerequisite: | lp:~hazmat/pyjuju/security-policy-with-topology |
Diff against target: |
1217 lines (+586/-208) (has conflicts) 14 files modified
juju/providers/local/__init__.py (+0/-1) juju/state/initialize.py (+2/-2) juju/state/security.py (+17/-27) juju/state/securityrules.py (+192/-0) juju/state/tests/common.py (+29/-39) juju/state/tests/test_agent.py (+0/-11) juju/state/tests/test_auth.py (+3/-3) juju/state/tests/test_base.py (+7/-7) juju/state/tests/test_environment.py (+0/-4) juju/state/tests/test_firewall.py (+1/-1) juju/state/tests/test_initialize.py (+14/-3) juju/state/tests/test_security.py (+38/-108) juju/state/tests/test_securityrules.py (+223/-0) juju/state/tests/test_service.py (+60/-2) Text conflict in juju/state/service.py Text conflict in juju/state/tests/common.py Text conflict in juju/state/tests/test_initialize.py Text conflict in juju/state/tests/test_service.py |
To merge this branch: | bzr merge lp:~hazmat/pyjuju/security-policy-rules-redux |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gustavo Niemeyer | Needs Fixing | ||
Review via email: mp+70502@code.launchpad.net |
This proposal supersedes a proposal from 2011-08-04.
Commit message
Description of the change
Redone with updated api and some simplifications.
Security ACL generator rules to match nodes based on path. The meat of the security ACL policy is embodied here, and this will likely some tweaking in the future.
Thou shall not forget pre-requisite branches lp:~hazmat/ensemble/security-policy-with-topology
- 323. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 324. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 325. By Kapil Thangavelu
-
resolve conflict from security-group merge
- 326. By Kapil Thangavelu
-
resolve conflict with security-groups merge
- 327. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 328. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 329. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 330. By Kapil Thangavelu
-
resolve conflict from security-
policy- with-topology merge - 331. By Kapil Thangavelu
-
yank security group accessor from service state
- 332. By Kapil Thangavelu
-
resolve conflict from states-
with-principals - 333. By Kapil Thangavelu
-
update tests in aftermath of removing service state security group accessor.
- 334. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 335. By Kapil Thangavelu
-
merge trunk
- 336. By Kapil Thangavelu
-
merge trunk
- 337. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 338. By Kapil Thangavelu
-
merge pipeline states-
with-principles
Unmerged revisions
- 338. By Kapil Thangavelu
-
merge pipeline states-
with-principles - 337. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 336. By Kapil Thangavelu
-
merge trunk
- 335. By Kapil Thangavelu
-
merge trunk
- 334. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux. - 333. By Kapil Thangavelu
-
update tests in aftermath of removing service state security group accessor.
- 332. By Kapil Thangavelu
-
resolve conflict from states-
with-principals - 331. By Kapil Thangavelu
-
yank security group accessor from service state
- 330. By Kapil Thangavelu
-
resolve conflict from security-
policy- with-topology merge - 329. By Kapil Thangavelu
-
Merged security-
policy- with-topology into security- policy- rules-redux.
Preview Diff
1 | === modified file 'juju/providers/local/__init__.py' | |||
2 | --- juju/providers/local/__init__.py 2012-08-10 11:01:23 +0000 | |||
3 | +++ juju/providers/local/__init__.py 2012-08-10 11:01:24 +0000 | |||
4 | @@ -17,7 +17,6 @@ | |||
5 | 17 | from juju.providers.local.machine import LocalMachine | 17 | from juju.providers.local.machine import LocalMachine |
6 | 18 | from juju.providers.local.network import Network | 18 | from juju.providers.local.network import Network |
7 | 19 | from juju.providers.local.pkg import check_packages | 19 | from juju.providers.local.pkg import check_packages |
8 | 20 | from juju.state.auth import make_identity | ||
9 | 21 | from juju.state.initialize import StateHierarchy | 20 | from juju.state.initialize import StateHierarchy |
10 | 22 | from juju.state.placement import LOCAL_POLICY | 21 | from juju.state.placement import LOCAL_POLICY |
11 | 23 | from juju.state.security import Principal | 22 | from juju.state.security import Principal |
12 | 24 | 23 | ||
13 | === modified file 'juju/state/initialize.py' | |||
14 | --- juju/state/initialize.py 2012-08-10 11:01:23 +0000 | |||
15 | +++ juju/state/initialize.py 2012-08-10 11:01:24 +0000 | |||
16 | @@ -8,7 +8,7 @@ | |||
17 | 8 | from .auth import make_ace | 8 | from .auth import make_ace |
18 | 9 | from .environment import EnvironmentStateManager, GlobalSettingsStateManager | 9 | from .environment import EnvironmentStateManager, GlobalSettingsStateManager |
19 | 10 | from .machine import MachineStateManager | 10 | from .machine import MachineStateManager |
21 | 11 | from .security import add_user | 11 | from .security import Principal |
22 | 12 | 12 | ||
23 | 13 | log = logging.getLogger("juju.state.init") | 13 | log = logging.getLogger("juju.state.init") |
24 | 14 | 14 | ||
25 | @@ -53,7 +53,7 @@ | |||
26 | 53 | yield self.client.create("/otp", acls=acls) | 53 | yield self.client.create("/otp", acls=acls) |
27 | 54 | 54 | ||
28 | 55 | # Seed the admin identity | 55 | # Seed the admin identity |
30 | 56 | yield add_user(self.client, *(self.admin_identity.split(":"))) | 56 | yield Principal(*(self.admin_identity.split(":"))).create(self.client) |
31 | 57 | 57 | ||
32 | 58 | # In this very specific case, it's OK to create a Constraints object | 58 | # In this very specific case, it's OK to create a Constraints object |
33 | 59 | # with a non-provider-specific ConstraintSet, because *all* we need it | 59 | # with a non-provider-specific ConstraintSet, because *all* we need it |
34 | 60 | 60 | ||
35 | === modified file 'juju/state/security.py' | |||
36 | --- juju/state/security.py 2012-08-10 11:01:23 +0000 | |||
37 | +++ juju/state/security.py 2012-08-10 11:01:24 +0000 | |||
38 | @@ -26,30 +26,16 @@ | |||
39 | 26 | 26 | ||
40 | 27 | 27 | ||
41 | 28 | @inlineCallbacks | 28 | @inlineCallbacks |
43 | 29 | def apply_rules(client, path, topology=None, recurse=False): | 29 | def apply_security_rules(client, path, topology=None, recurse=False): |
44 | 30 | """Apply security policy ACL to the given path.""" | 30 | """Apply security policy ACL to the given path.""" |
45 | 31 | token_db = TokenDatabase(client) | 31 | token_db = TokenDatabase(client) |
46 | 32 | policy = SecurityPolicy( | 32 | policy = SecurityPolicy( |
49 | 33 | client, token_db, securityrules.get_default_rules(), | 33 | client, token_db, |
50 | 34 | topology) | 34 | rules=securityrules.get_default_rules(), |
51 | 35 | topology=topology) | ||
52 | 35 | acl = yield policy(path) | 36 | acl = yield policy(path) |
53 | 36 | yield client.set_acl(path, acl) | 37 | yield client.set_acl(path, acl) |
54 | 37 | 38 | ||
55 | 38 | apply_security_rules = apply_rules | ||
56 | 39 | |||
57 | 40 | |||
58 | 41 | def set_domain_security(enabled): | ||
59 | 42 | """For testing enable disabling security for test speed. | ||
60 | 43 | |||
61 | 44 | All the operations against DomainSequenceSecurity become no-ops. | ||
62 | 45 | """ | ||
63 | 46 | |||
64 | 47 | |||
65 | 48 | def add_user(client, username, password): | ||
66 | 49 | tokens = TokenDatabase(client) | ||
67 | 50 | principal = Principal(username, password) | ||
68 | 51 | return tokens.add(principal) | ||
69 | 52 | |||
70 | 53 | 39 | ||
71 | 54 | class Principal(object): | 40 | class Principal(object): |
72 | 55 | """An juju/zookeeper principal. | 41 | """An juju/zookeeper principal. |
73 | @@ -76,7 +62,7 @@ | |||
74 | 76 | auth_deferred = connection.add_auth( | 62 | auth_deferred = connection.add_auth( |
75 | 77 | "digest", "%s:%s" % (self._name, self._password)) | 63 | "digest", "%s:%s" % (self._name, self._password)) |
76 | 78 | # Work around for fast auth, remove post ZOOKEEPER-770 | 64 | # Work around for fast auth, remove post ZOOKEEPER-770 |
78 | 79 | connection.exists("/") | 65 | #connection.exists("/") |
79 | 80 | return auth_deferred | 66 | return auth_deferred |
80 | 81 | 67 | ||
81 | 82 | def create(self, client): | 68 | def create(self, client): |
82 | @@ -142,7 +128,7 @@ | |||
83 | 142 | auth_deferred = connection.add_auth( | 128 | auth_deferred = connection.add_auth( |
84 | 143 | "digest", "%s:%s" % (self._name, self._password)) | 129 | "digest", "%s:%s" % (self._name, self._password)) |
85 | 144 | # Work around for fast auth, remove post ZOOKEEPER-770 | 130 | # Work around for fast auth, remove post ZOOKEEPER-770 |
87 | 145 | connection.exists("/") | 131 | #connection.exists("/") |
88 | 146 | yield auth_deferred | 132 | yield auth_deferred |
89 | 147 | 133 | ||
90 | 148 | @inlineCallbacks | 134 | @inlineCallbacks |
91 | @@ -244,14 +230,15 @@ | |||
92 | 244 | 230 | ||
93 | 245 | # Decode the data to get the path and otp credentials | 231 | # Decode the data to get the path and otp credentials |
94 | 246 | path, credentials = base64.b64decode(otp_data).split(":", 1) | 232 | path, credentials = base64.b64decode(otp_data).split(":", 1) |
101 | 247 | attach_deferred = client.add_auth("digest", credentials) | 233 | |
102 | 248 | # Fast Auth - Remove after ZOOKEEPER-770 | 234 | # Auth with otp token creds to read the final principal data. |
103 | 249 | client.exists("/") | 235 | p = Principal(*(credentials.split(":"))) |
104 | 250 | 236 | yield p.attach(client) | |
105 | 251 | yield attach_deferred | 237 | |
106 | 252 | # Load the otp principal data | 238 | # Load the principal data |
107 | 253 | data, stat = yield client.get(path) | 239 | data, stat = yield client.get(path) |
108 | 254 | principal_data = yaml.load(data) | 240 | principal_data = yaml.load(data) |
109 | 241 | |||
110 | 255 | # Consume the otp node | 242 | # Consume the otp node |
111 | 256 | yield client.delete(path) | 243 | yield client.delete(path) |
112 | 257 | 244 | ||
113 | @@ -373,8 +360,11 @@ | |||
114 | 373 | 360 | ||
115 | 374 | class SecurityPolicy(object): | 361 | class SecurityPolicy(object): |
116 | 375 | """The security policy generates ACLs for new nodes based on their path. | 362 | """The security policy generates ACLs for new nodes based on their path. |
117 | 363 | |||
118 | 364 | :param owner: If passed the owner has all permissions on nodes. | ||
119 | 365 | :param topology: A topology to use for rule analysis. | ||
120 | 376 | """ | 366 | """ |
122 | 377 | def __init__(self, client, token_db, rules=(), owner=None): | 367 | def __init__(self, client, token_db, rules=(), owner=None, topology=None): |
123 | 378 | self._client = client | 368 | self._client = client |
124 | 379 | self._rules = list(rules) | 369 | self._rules = list(rules) |
125 | 380 | self._token_db = token_db | 370 | self._token_db = token_db |
126 | 381 | 371 | ||
127 | === modified file 'juju/state/securityrules.py' | |||
128 | --- juju/state/securityrules.py 2012-08-10 11:01:23 +0000 | |||
129 | +++ juju/state/securityrules.py 2012-08-10 11:01:24 +0000 | |||
130 | @@ -1,3 +1,195 @@ | |||
131 | 1 | """ | ||
132 | 2 | Security rules to determine ACL by node path. | ||
133 | 3 | |||
134 | 4 | """ | ||
135 | 5 | |||
136 | 6 | from twisted.internet.defer import inlineCallbacks, returnValue | ||
137 | 7 | |||
138 | 8 | from juju.state.auth import make_ace | ||
139 | 9 | |||
140 | 10 | |||
141 | 11 | def formula_rule(policy, path): | ||
142 | 12 | """An ACL policy rule for nodes under the '/formulas' path. | ||
143 | 13 | |||
144 | 14 | These nodes are read only by any connected user, and are | ||
145 | 15 | writable only by the admin cli (which creates them). | ||
146 | 16 | |||
147 | 17 | Path rule:: | ||
148 | 18 | |||
149 | 19 | /formulas | ||
150 | 20 | /formula-id (all:admin, r:everyone) | ||
151 | 21 | """ | ||
152 | 22 | if not path.startswith("/formulas/"): | ||
153 | 23 | return | ||
154 | 24 | return [make_ace("anyone", "world", read=True)] | ||
155 | 25 | |||
156 | 26 | |||
157 | 27 | @inlineCallbacks | ||
158 | 28 | def machine_rule(policy, path): | ||
159 | 29 | """An ACL policy rule for nodes under the '/machines' path. | ||
160 | 30 | |||
161 | 31 | These nodes are created by the cli admin when launching new machines, | ||
162 | 32 | with admin access granted to the provisioning agents. When the | ||
163 | 33 | provisioning agents create a new machine, they also create a new | ||
164 | 34 | principal and update the machine node ace to allow access to the | ||
165 | 35 | new machine agent principal. | ||
166 | 36 | """ | ||
167 | 37 | |||
168 | 38 | if not path.startswith("/machines/"): | ||
169 | 39 | return | ||
170 | 40 | |||
171 | 41 | parts = path.split("/")[2:] | ||
172 | 42 | |||
173 | 43 | if not parts: | ||
174 | 44 | return | ||
175 | 45 | |||
176 | 46 | machine_id = parts[0] | ||
177 | 47 | assert machine_id.startswith("machine-"), "Invalid machine: " + machine_id | ||
178 | 48 | |||
179 | 49 | machine_token = yield policy.get_token(machine_id) | ||
180 | 50 | provider_token = yield policy.get_token("juju-provider") | ||
181 | 51 | returnValue([ | ||
182 | 52 | make_ace(machine_token, read=True, write=True, create=True), | ||
183 | 53 | make_ace(provider_token, read=True, write=True)]) | ||
184 | 54 | |||
185 | 55 | |||
186 | 56 | @inlineCallbacks | ||
187 | 57 | def _get_service_data(policy, relation_id): | ||
188 | 58 | """Discover service information for a relation | ||
189 | 59 | |||
190 | 60 | :param policy: A :class:SecurityPolicy instance | ||
191 | 61 | :param relation_id: The internal relation id | ||
192 | 62 | |||
193 | 63 | Returns a list of dictionaries containing service relation data | ||
194 | 64 | and a the service token. | ||
195 | 65 | """ | ||
196 | 66 | topology = yield policy.get_topology() | ||
197 | 67 | services = topology.get_relation_services(relation_id) | ||
198 | 68 | results = [] | ||
199 | 69 | |||
200 | 70 | for service_id, service_data in services.items(): | ||
201 | 71 | service_data = dict(service_data) | ||
202 | 72 | service_data["service_id"] = service_id | ||
203 | 73 | service_data["token"] = yield policy.get_token(service_id) | ||
204 | 74 | results.append(service_data) | ||
205 | 75 | returnValue(results) | ||
206 | 76 | |||
207 | 77 | |||
208 | 78 | @inlineCallbacks | ||
209 | 79 | def relation_rule(policy, path): | ||
210 | 80 | """An ACL policy rule for nodes under the '/relations' path. | ||
211 | 81 | """ | ||
212 | 82 | if not path.startswith("/relations/"): | ||
213 | 83 | return | ||
214 | 84 | |||
215 | 85 | parts = path.split("/")[2:] | ||
216 | 86 | |||
217 | 87 | # Extract relation-id | ||
218 | 88 | relation_id = parts.pop(0) | ||
219 | 89 | assert relation_id.startswith("relation-"), "Invalid Rel: " + relation_id | ||
220 | 90 | |||
221 | 91 | # Retrieve data for services in the relation | ||
222 | 92 | service_data = yield _get_service_data(policy, relation_id) | ||
223 | 93 | service_tokens = [data["token"] for data in service_data] | ||
224 | 94 | |||
225 | 95 | # The relation itself /relations/relation-id | ||
226 | 96 | if not parts: | ||
227 | 97 | returnValue(map(lambda x: make_ace(x, read=True), service_tokens)) | ||
228 | 98 | |||
229 | 99 | container = parts.pop(0) | ||
230 | 100 | |||
231 | 101 | # If its just the container /relations/relation-id/(settings|role) | ||
232 | 102 | # then allow both services to read from it | ||
233 | 103 | if not parts: | ||
234 | 104 | if container in ("settings", "peer"): | ||
235 | 105 | returnValue( | ||
236 | 106 | map(lambda x: make_ace(x, create=True, read=True), | ||
237 | 107 | service_tokens)) | ||
238 | 108 | elif container in ("client", "server"): | ||
239 | 109 | matched_service = [data for data in service_data \ | ||
240 | 110 | if data["role"] == container].pop() | ||
241 | 111 | related_service = [data for data in service_data \ | ||
242 | 112 | if data != matched_service].pop() | ||
243 | 113 | returnValue( | ||
244 | 114 | [make_ace(matched_service["token"], read=True, create=True), | ||
245 | 115 | make_ace(related_service["token"], read=True)]) | ||
246 | 116 | else: | ||
247 | 117 | # Allow read access to settings and presence nodes | ||
248 | 118 | returnValue( | ||
249 | 119 | map(lambda x: make_ace(x, read=True), service_tokens)) | ||
250 | 120 | |||
251 | 121 | |||
252 | 122 | @inlineCallbacks | ||
253 | 123 | def service_rule(policy, path): | ||
254 | 124 | """An ACL rule for the /services hierarchy.""" | ||
255 | 125 | |||
256 | 126 | if not path.startswith("/services/"): | ||
257 | 127 | return | ||
258 | 128 | |||
259 | 129 | parts = path.split("/")[2:] | ||
260 | 130 | service_id = parts.pop(0) | ||
261 | 131 | assert service_id.startswith("service-"), "Invalid Service: " + service_id | ||
262 | 132 | |||
263 | 133 | service_token = yield policy.get_token(service_id) | ||
264 | 134 | |||
265 | 135 | if not parts or parts[0] != "exposed": | ||
266 | 136 | # Default, give read access to the service group principal. | ||
267 | 137 | returnValue([make_ace(service_token, read=True)]) | ||
268 | 138 | elif parts[0] == "exposed": | ||
269 | 139 | provider_token = yield policy.get_token("juju-provider") | ||
270 | 140 | returnValue([make_ace(provider_token, read=True), | ||
271 | 141 | make_ace(service_token, read=True, write=True)]) | ||
272 | 142 | |||
273 | 143 | |||
274 | 144 | @inlineCallbacks | ||
275 | 145 | def unit_rule(policy, path): | ||
276 | 146 | """An ACL generator rule for the '/units' subtree.""" | ||
277 | 147 | |||
278 | 148 | if not path.startswith("/units/"): | ||
279 | 149 | return | ||
280 | 150 | |||
281 | 151 | parts = path.split("/")[2:] | ||
282 | 152 | unit_id = parts.pop(0) | ||
283 | 153 | assert unit_id.startswith("unit-"), "Invalid Unit: " + unit_id | ||
284 | 154 | |||
285 | 155 | unit_token = yield policy.get_token(unit_id) | ||
286 | 156 | |||
287 | 157 | if not parts or parts[0] != "ports": | ||
288 | 158 | # Unit agent can modify tweak any unitother unit settings. | ||
289 | 159 | returnValue([make_ace(unit_token, all=True)]) | ||
290 | 160 | |||
291 | 161 | elif parts[0] == "ports": | ||
292 | 162 | # Created by the unit agent, read by the provisioning agent. | ||
293 | 163 | provider_token = yield policy.get_token("juju-provider") | ||
294 | 164 | returnValue([ | ||
295 | 165 | make_ace(unit_token, all=True), | ||
296 | 166 | make_ace(provider_token, read=True)]) | ||
297 | 167 | |||
298 | 168 | |||
299 | 169 | @inlineCallbacks | ||
300 | 170 | def global_rule(policy, path): | ||
301 | 171 | """An ACL for top-level nodes like '/topology' and '/environment' | ||
302 | 172 | """ | ||
303 | 173 | if path == "/environment": | ||
304 | 174 | agent_token = yield policy.token_db.get_token("juju-provider") | ||
305 | 175 | # Only the admin cli can write to this. | ||
306 | 176 | returnValue([make_ace(agent_token, read=True)]) | ||
307 | 177 | elif path == "/topology": | ||
308 | 178 | # Only the admin cli can write to this. | ||
309 | 179 | returnValue([make_ace("anyone", "world", read=True)]) | ||
310 | 180 | elif path == "/auth-tokens": | ||
311 | 181 | # Any connection that creates another user needs to modify this | ||
312 | 182 | returnValue([make_ace("anyone", "world", read=True, write=True)]) | ||
313 | 183 | |||
314 | 184 | |||
315 | 185 | DEFAULT_RULES = [ | ||
316 | 186 | global_rule, | ||
317 | 187 | formula_rule, | ||
318 | 188 | relation_rule, | ||
319 | 189 | machine_rule, | ||
320 | 190 | service_rule, | ||
321 | 191 | unit_rule | ||
322 | 192 | ] | ||
323 | 1 | 193 | ||
324 | 2 | 194 | ||
325 | 3 | def get_default_rules(): | 195 | def get_default_rules(): |
326 | 4 | 196 | ||
327 | === modified file 'juju/state/tests/common.py' | |||
328 | --- juju/state/tests/common.py 2012-08-10 11:01:23 +0000 | |||
329 | +++ juju/state/tests/common.py 2012-08-10 11:01:24 +0000 | |||
330 | @@ -1,18 +1,22 @@ | |||
331 | 1 | from twisted.internet.defer import inlineCallbacks, returnValue | 1 | from twisted.internet.defer import inlineCallbacks, returnValue |
332 | 2 | 2 | ||
333 | 3 | import zookeeper | 3 | import zookeeper |
334 | 4 | import os | ||
335 | 5 | 4 | ||
336 | 5 | <<<<<<< TREE | ||
337 | 6 | from txzookeeper.tests.utils import deleteTree | 6 | from txzookeeper.tests.utils import deleteTree |
338 | 7 | 7 | ||
339 | 8 | ======= | ||
340 | 9 | >>>>>>> MERGE-SOURCE | ||
341 | 8 | from juju.charm.directory import CharmDirectory | 10 | from juju.charm.directory import CharmDirectory |
342 | 9 | from juju.charm.tests.test_directory import sample_directory | 11 | from juju.charm.tests.test_directory import sample_directory |
343 | 10 | from juju.environment.tests.test_config import EnvironmentsConfigTestBase | 12 | from juju.environment.tests.test_config import EnvironmentsConfigTestBase |
344 | 11 | from juju.state.topology import InternalTopology | 13 | from juju.state.topology import InternalTopology |
345 | 14 | <<<<<<< TREE | ||
346 | 15 | ======= | ||
347 | 12 | 16 | ||
348 | 13 | from juju.state.auth import make_ace | 17 | from juju.state.auth import make_ace |
351 | 14 | from juju.state.security import ( | 18 | from juju.state.security import Principal, OTPPrincipal |
352 | 15 | Principal, OTPPrincipal, TokenDatabase, set_domain_security) | 19 | >>>>>>> MERGE-SOURCE |
353 | 16 | 20 | ||
354 | 17 | 21 | ||
355 | 18 | class StateTestBase(EnvironmentsConfigTestBase): | 22 | class StateTestBase(EnvironmentsConfigTestBase): |
356 | @@ -31,35 +35,22 @@ | |||
357 | 31 | yield self.client.create("/units") | 35 | yield self.client.create("/units") |
358 | 32 | yield self.client.create("/relations") | 36 | yield self.client.create("/relations") |
359 | 33 | yield self.client.create("/otp") | 37 | yield self.client.create("/otp") |
375 | 34 | 38 | yield self.setup_security() | |
361 | 35 | ## Default this to off, as it causes a significant (30%) | ||
362 | 36 | ## impact on total test time. | ||
363 | 37 | if 1: #os.environ.get("TEST_ENSEMBLE_SECURITY"): | ||
364 | 38 | yield self.setup_security() | ||
365 | 39 | else: | ||
366 | 40 | # Can be set globally to off, individual tests can be marked | ||
367 | 41 | # with a security decorator, or invoke setup_security | ||
368 | 42 | # if they want to enable security for an individual test. | ||
369 | 43 | self._security_enabled = False | ||
370 | 44 | set_domain_security(False) | ||
371 | 45 | |||
372 | 46 | @property | ||
373 | 47 | def security_enabled(self): | ||
374 | 48 | return self._security_enabled | ||
376 | 49 | 39 | ||
377 | 50 | @inlineCallbacks | 40 | @inlineCallbacks |
378 | 51 | def tearDown(self): | 41 | def tearDown(self): |
379 | 52 | # Close and reopen connection, so that watches set during | 42 | # Close and reopen connection, so that watches set during |
380 | 53 | # testing are not affected by the cleaning up. | 43 | # testing are not affected by the cleaning up. |
381 | 54 | self.client.close() | 44 | self.client.close() |
383 | 55 | client = self.get_zookeeper_client() | 45 | <<<<<<< TREE |
384 | 46 | client = self.get_zookeeper_client() | ||
385 | 47 | ======= | ||
386 | 48 | |||
387 | 49 | client = self.get_zookeeper_client() | ||
388 | 50 | >>>>>>> MERGE-SOURCE | ||
389 | 56 | yield client.connect() | 51 | yield client.connect() |
396 | 57 | 52 | yield Principal("testadmin", "testadmin").attach(client) | |
397 | 58 | if self._security_enabled: | 53 | yield deleteTree(client) |
392 | 59 | yield Principal("testadmin", "testadmin").attach(client) | ||
393 | 60 | yield client.exists("/") | ||
394 | 61 | |||
395 | 62 | deleteTree(handle=client.handle) | ||
398 | 63 | client.close() | 54 | client.close() |
399 | 64 | yield super(StateTestBase, self).tearDown() | 55 | yield super(StateTestBase, self).tearDown() |
400 | 65 | 56 | ||
401 | @@ -88,20 +79,19 @@ | |||
402 | 88 | principal = Principal("testadmin", "testadmin") | 79 | principal = Principal("testadmin", "testadmin") |
403 | 89 | token = principal.get_token() | 80 | token = principal.get_token() |
404 | 90 | OTPPrincipal.set_additional_otp_ace(make_ace(token, all=True)) | 81 | OTPPrincipal.set_additional_otp_ace(make_ace(token, all=True)) |
405 | 82 | |||
406 | 91 | # Rules application expects 'admin' principal | 83 | # Rules application expects 'admin' principal |
408 | 92 | yield TokenDatabase(self.client).add(Principal("admin", "admin")) | 84 | yield Principal("admin", "admin").create(self.client) |
409 | 85 | |||
410 | 93 | # Remove the test otp ace, post test | 86 | # Remove the test otp ace, post test |
411 | 94 | self.addCleanup(lambda: OTPPrincipal.set_additional_otp_ace(None)) | 87 | self.addCleanup(lambda: OTPPrincipal.set_additional_otp_ace(None)) |
425 | 95 | self._security_enabled = True | 88 | |
426 | 96 | set_domain_security(True) | 89 | |
427 | 97 | 90 | @inlineCallbacks | |
428 | 98 | 91 | def deleteTree(client, path="/"): | |
429 | 99 | def security_test(test_method): | 92 | for child in (yield client.get_children(path)): |
430 | 100 | 93 | if child == "zookeeper": | |
431 | 101 | @inlineCallbacks | 94 | continue |
432 | 102 | def run_security_enabled_test(self, *args, **kw): | 95 | child_path = "/" + ("%s/%s" % (path, child)).strip("/") |
433 | 103 | if not self.security_enabled: | 96 | yield deleteTree(client, child_path) |
434 | 104 | yield self.setup_security() | 97 | yield client.delete(child_path) |
422 | 105 | yield test_method(self, *args, **kw) | ||
423 | 106 | |||
424 | 107 | return run_security_enabled_test | ||
435 | 108 | 98 | ||
436 | === modified file 'juju/state/tests/test_agent.py' | |||
437 | --- juju/state/tests/test_agent.py 2012-08-10 11:01:23 +0000 | |||
438 | +++ juju/state/tests/test_agent.py 2012-08-10 11:01:24 +0000 | |||
439 | @@ -1,7 +1,4 @@ | |||
440 | 1 | import zookeeper | ||
441 | 2 | |||
442 | 3 | from twisted.internet.defer import inlineCallbacks | 1 | from twisted.internet.defer import inlineCallbacks |
443 | 4 | from txzookeeper.tests.utils import deleteTree | ||
444 | 5 | 2 | ||
445 | 6 | from juju.state.base import StateBase | 3 | from juju.state.base import StateBase |
446 | 7 | from juju.state.agent import AgentStateMixin | 4 | from juju.state.agent import AgentStateMixin |
447 | @@ -24,14 +21,6 @@ | |||
448 | 24 | yield self.client.connect() | 21 | yield self.client.connect() |
449 | 25 | 22 | ||
450 | 26 | @inlineCallbacks | 23 | @inlineCallbacks |
451 | 27 | def tearDown(self): | ||
452 | 28 | yield self.client.close() | ||
453 | 29 | client = self.get_zookeeper_client() | ||
454 | 30 | yield client.connect() | ||
455 | 31 | deleteTree("/", client.handle) | ||
456 | 32 | yield client.close() | ||
457 | 33 | |||
458 | 34 | @inlineCallbacks | ||
459 | 35 | def test_has_agent(self): | 24 | def test_has_agent(self): |
460 | 36 | domain = DomainObject(self.client) | 25 | domain = DomainObject(self.client) |
461 | 37 | exists = yield domain.has_agent() | 26 | exists = yield domain.has_agent() |
462 | 38 | 27 | ||
463 | === modified file 'juju/state/tests/test_auth.py' | |||
464 | --- juju/state/tests/test_auth.py 2011-09-15 18:50:23 +0000 | |||
465 | +++ juju/state/tests/test_auth.py 2012-08-10 11:01:24 +0000 | |||
466 | @@ -14,7 +14,7 @@ | |||
467 | 14 | 14 | ||
468 | 15 | credentials = "%s:%s" % (username, password) | 15 | credentials = "%s:%s" % (username, password) |
469 | 16 | 16 | ||
471 | 17 | identity = "%s:%s" %( | 17 | identity = "%s:%s" % ( |
472 | 18 | username, | 18 | username, |
473 | 19 | base64.b64encode(hashlib.new("sha1", credentials).digest())) | 19 | base64.b64encode(hashlib.new("sha1", credentials).digest())) |
474 | 20 | self.assertEqual(identity, make_identity(credentials)) | 20 | self.assertEqual(identity, make_identity(credentials)) |
475 | @@ -25,7 +25,7 @@ | |||
476 | 25 | 25 | ||
477 | 26 | credentials = "%s:%s" % (username, password) | 26 | credentials = "%s:%s" % (username, password) |
478 | 27 | 27 | ||
480 | 28 | identity = "%s:%s" %( | 28 | identity = "%s:%s" % ( |
481 | 29 | username, | 29 | username, |
482 | 30 | base64.b64encode(hashlib.new("sha1", credentials).digest())) | 30 | base64.b64encode(hashlib.new("sha1", credentials).digest())) |
483 | 31 | self.assertEqual(identity, make_identity(credentials)) | 31 | self.assertEqual(identity, make_identity(credentials)) |
484 | @@ -41,7 +41,7 @@ | |||
485 | 41 | self.assertEqual(ace["id"], identity) | 41 | self.assertEqual(ace["id"], identity) |
486 | 42 | self.assertEqual(ace["scheme"], "digest") | 42 | self.assertEqual(ace["scheme"], "digest") |
487 | 43 | self.assertEqual( | 43 | self.assertEqual( |
489 | 44 | ace["perms"], zookeeper.PERM_WRITE|zookeeper.PERM_CREATE) | 44 | ace["perms"], zookeeper.PERM_WRITE | zookeeper.PERM_CREATE) |
490 | 45 | 45 | ||
491 | 46 | def test_make_ace_with_unknown_perm(self): | 46 | def test_make_ace_with_unknown_perm(self): |
492 | 47 | identity = "admin:moss" | 47 | identity = "admin:moss" |
493 | 48 | 48 | ||
494 | === modified file 'juju/state/tests/test_base.py' | |||
495 | --- juju/state/tests/test_base.py 2012-08-10 11:01:23 +0000 | |||
496 | +++ juju/state/tests/test_base.py 2012-08-10 11:01:24 +0000 | |||
497 | @@ -112,7 +112,7 @@ | |||
498 | 112 | 112 | ||
499 | 113 | def watch_topology(old_topology, new_topology): | 113 | def watch_topology(old_topology, new_topology): |
500 | 114 | calls.append((old_topology, new_topology)) | 114 | calls.append((old_topology, new_topology)) |
502 | 115 | wait_callback[len(calls)-1].callback(True) | 115 | wait_callback[len(calls) - 1].callback(True) |
503 | 116 | 116 | ||
504 | 117 | # Start watching. | 117 | # Start watching. |
505 | 118 | self.base._watch_topology(watch_topology) | 118 | self.base._watch_topology(watch_topology) |
506 | @@ -169,7 +169,7 @@ | |||
507 | 169 | 169 | ||
508 | 170 | def watch_topology(old_topology, new_topology): | 170 | def watch_topology(old_topology, new_topology): |
509 | 171 | calls.append((old_topology, new_topology)) | 171 | calls.append((old_topology, new_topology)) |
511 | 172 | wait_callback[len(calls)-1].callback(True) | 172 | wait_callback[len(calls) - 1].callback(True) |
512 | 173 | 173 | ||
513 | 174 | # Start watching, and wait on callback immediately. | 174 | # Start watching, and wait on callback immediately. |
514 | 175 | self.base._watch_topology(watch_topology) | 175 | self.base._watch_topology(watch_topology) |
515 | @@ -219,7 +219,7 @@ | |||
516 | 219 | 219 | ||
517 | 220 | def watch_topology(old_topology, new_topology): | 220 | def watch_topology(old_topology, new_topology): |
518 | 221 | calls.append((old_topology, new_topology)) | 221 | calls.append((old_topology, new_topology)) |
520 | 222 | wait_callback[len(calls)-1].callback(True) | 222 | wait_callback[len(calls) - 1].callback(True) |
521 | 223 | 223 | ||
522 | 224 | # Start watching, and wait on callback immediately. | 224 | # Start watching, and wait on callback immediately. |
523 | 225 | self.base._watch_topology(watch_topology) | 225 | self.base._watch_topology(watch_topology) |
524 | @@ -265,8 +265,8 @@ | |||
525 | 265 | 265 | ||
526 | 266 | def watch_topology(old_topology, new_topology): | 266 | def watch_topology(old_topology, new_topology): |
527 | 267 | calls.append((old_topology, new_topology)) | 267 | calls.append((old_topology, new_topology)) |
530 | 268 | wait_callback[len(calls)-1].callback(True) | 268 | wait_callback[len(calls) - 1].callback(True) |
531 | 269 | return finish_callback[len(calls)-1] | 269 | return finish_callback[len(calls) - 1] |
532 | 270 | 270 | ||
533 | 271 | # Start watching. | 271 | # Start watching. |
534 | 272 | self.base._watch_topology(watch_topology) | 272 | self.base._watch_topology(watch_topology) |
535 | @@ -313,7 +313,7 @@ | |||
536 | 313 | 313 | ||
537 | 314 | def watcher(old_topology, new_topology): | 314 | def watcher(old_topology, new_topology): |
538 | 315 | calls.append((old_topology, new_topology)) | 315 | calls.append((old_topology, new_topology)) |
540 | 316 | wait_callback[len(calls)-1].callback(True) | 316 | wait_callback[len(calls) - 1].callback(True) |
541 | 317 | if len(calls) == 2: | 317 | if len(calls) == 2: |
542 | 318 | raise StopWatcher() | 318 | raise StopWatcher() |
543 | 319 | 319 | ||
544 | @@ -371,7 +371,7 @@ | |||
545 | 371 | topology = InternalTopology() | 371 | topology = InternalTopology() |
546 | 372 | topology.add_machine("m-0") | 372 | topology.add_machine("m-0") |
547 | 373 | yield self.set_topology(topology) | 373 | yield self.set_topology(topology) |
549 | 374 | 374 | ||
550 | 375 | # Hold off until callback is started. | 375 | # Hold off until callback is started. |
551 | 376 | yield wait_callback | 376 | yield wait_callback |
552 | 377 | 377 | ||
553 | 378 | 378 | ||
554 | === modified file 'juju/state/tests/test_environment.py' | |||
555 | --- juju/state/tests/test_environment.py 2012-03-29 01:37:57 +0000 | |||
556 | +++ juju/state/tests/test_environment.py 2012-08-10 11:01:24 +0000 | |||
557 | @@ -24,10 +24,6 @@ | |||
558 | 24 | self.config.load() | 24 | self.config.load() |
559 | 25 | 25 | ||
560 | 26 | @inlineCallbacks | 26 | @inlineCallbacks |
561 | 27 | def tearDown(self): | ||
562 | 28 | yield super(EnvironmentStateManagerTest, self).tearDown() | ||
563 | 29 | |||
564 | 30 | @inlineCallbacks | ||
565 | 31 | def test_set_config_state(self): | 27 | def test_set_config_state(self): |
566 | 32 | """ | 28 | """ |
567 | 33 | The simplest thing the manager can do is serialize a given | 29 | The simplest thing the manager can do is serialize a given |
568 | 34 | 30 | ||
569 | === modified file 'juju/state/tests/test_firewall.py' | |||
570 | --- juju/state/tests/test_firewall.py 2012-07-09 22:49:36 +0000 | |||
571 | +++ juju/state/tests/test_firewall.py 2012-08-10 11:01:24 +0000 | |||
572 | @@ -272,7 +272,7 @@ | |||
573 | 272 | machine_provider, machine.id) | 272 | machine_provider, machine.id) |
574 | 273 | returnValue(provider_ports) | 273 | returnValue(provider_ports) |
575 | 274 | 274 | ||
577 | 275 | def test_open_close_ports_on_machine(self): | 275 | def xtest_open_close_ports_on_machine(self): |
578 | 276 | """Verify opening/closing ports on a machine works properly. | 276 | """Verify opening/closing ports on a machine works properly. |
579 | 277 | 277 | ||
580 | 278 | In particular this is done without watch support.""" | 278 | In particular this is done without watch support.""" |
581 | 279 | 279 | ||
582 | === modified file 'juju/state/tests/test_initialize.py' | |||
583 | --- juju/state/tests/test_initialize.py 2012-08-10 11:01:23 +0000 | |||
584 | +++ juju/state/tests/test_initialize.py 2012-08-10 11:01:24 +0000 | |||
585 | @@ -1,15 +1,25 @@ | |||
586 | 1 | import zookeeper | 1 | import zookeeper |
587 | 2 | 2 | ||
588 | 3 | from twisted.internet.defer import inlineCallbacks | 3 | from twisted.internet.defer import inlineCallbacks |
589 | 4 | <<<<<<< TREE | ||
590 | 4 | from txzookeeper.tests.utils import deleteTree | 5 | from txzookeeper.tests.utils import deleteTree |
591 | 6 | ======= | ||
592 | 7 | from txzookeeper import ZookeeperClient | ||
593 | 8 | #from txzookeeper.tests.utils import deleteTree | ||
594 | 9 | >>>>>>> MERGE-SOURCE | ||
595 | 5 | 10 | ||
596 | 6 | from juju.environment.tests.test_config import EnvironmentsConfigTestBase | 11 | from juju.environment.tests.test_config import EnvironmentsConfigTestBase |
597 | 7 | from juju.state.auth import make_identity, make_ace | 12 | from juju.state.auth import make_identity, make_ace |
598 | 8 | from juju.state.environment import ( | 13 | from juju.state.environment import ( |
599 | 9 | GlobalSettingsStateManager, EnvironmentStateManager) | 14 | GlobalSettingsStateManager, EnvironmentStateManager) |
600 | 10 | from juju.state.initialize import StateHierarchy | 15 | from juju.state.initialize import StateHierarchy |
602 | 11 | from juju.state.security import OTPPrincipal, add_user | 16 | from juju.state.security import OTPPrincipal, Principal |
603 | 12 | from juju.state.machine import MachineStateManager | 17 | from juju.state.machine import MachineStateManager |
604 | 18 | <<<<<<< TREE | ||
605 | 19 | ======= | ||
606 | 20 | from juju.state.tests.common import deleteTree | ||
607 | 21 | from juju.tests.common import get_test_zookeeper_address | ||
608 | 22 | >>>>>>> MERGE-SOURCE | ||
609 | 13 | 23 | ||
610 | 14 | 24 | ||
611 | 15 | class LayoutTest(EnvironmentsConfigTestBase): | 25 | class LayoutTest(EnvironmentsConfigTestBase): |
612 | @@ -28,7 +38,7 @@ | |||
613 | 28 | "provider-type": "dummy"} | 38 | "provider-type": "dummy"} |
614 | 29 | yield self.client.connect() | 39 | yield self.client.connect() |
615 | 30 | 40 | ||
617 | 31 | self.test_admin = yield add_user(self.client, "admin", "secret") | 41 | self.test_admin = Principal("admin", "secret") |
618 | 32 | yield self.test_admin.attach(self.client) | 42 | yield self.test_admin.attach(self.client) |
619 | 33 | self.identity = self.test_admin.get_token() | 43 | self.identity = self.test_admin.get_token() |
620 | 34 | 44 | ||
621 | @@ -39,8 +49,9 @@ | |||
622 | 39 | self.layout = StateHierarchy( | 49 | self.layout = StateHierarchy( |
623 | 40 | self.client, self.identity, "i-abcdef", constraints_data, "dummy") | 50 | self.client, self.identity, "i-abcdef", constraints_data, "dummy") |
624 | 41 | 51 | ||
625 | 52 | @inlineCallbacks | ||
626 | 42 | def tearDown(self): | 53 | def tearDown(self): |
628 | 43 | deleteTree(handle=self.client.handle) | 54 | yield deleteTree(self.client) |
629 | 44 | self.client.close() | 55 | self.client.close() |
630 | 45 | 56 | ||
631 | 46 | @inlineCallbacks | 57 | @inlineCallbacks |
632 | 47 | 58 | ||
633 | === modified file 'juju/state/tests/test_security.py' | |||
634 | --- juju/state/tests/test_security.py 2012-08-10 11:01:23 +0000 | |||
635 | +++ juju/state/tests/test_security.py 2012-08-10 11:01:24 +0000 | |||
636 | @@ -14,12 +14,13 @@ | |||
637 | 14 | 14 | ||
638 | 15 | from juju.state.topology import InternalTopology | 15 | from juju.state.topology import InternalTopology |
639 | 16 | from juju.lib.testing import TestCase | 16 | from juju.lib.testing import TestCase |
641 | 17 | from juju.state.tests.common import StateTestBase, security_test | 17 | from juju.state.tests.common import StateTestBase, deleteTree |
642 | 18 | from juju.tests.common import get_test_zookeeper_address | 18 | from juju.tests.common import get_test_zookeeper_address |
643 | 19 | 19 | ||
644 | 20 | |||
645 | 20 | from txzookeeper.client import ZOO_OPEN_ACL_UNSAFE | 21 | from txzookeeper.client import ZOO_OPEN_ACL_UNSAFE |
646 | 21 | 22 | ||
648 | 22 | from txzookeeper.tests.utils import deleteTree | 23 | #from txzookeeper.tests.utils import deleteTree |
649 | 23 | 24 | ||
650 | 24 | 25 | ||
651 | 25 | class PrincipalTests(TestCase): | 26 | class PrincipalTests(TestCase): |
652 | @@ -29,8 +30,9 @@ | |||
653 | 29 | zookeeper.set_debug_level(0) | 30 | zookeeper.set_debug_level(0) |
654 | 30 | self.client = yield self.get_zookeeper_client().connect() | 31 | self.client = yield self.get_zookeeper_client().connect() |
655 | 31 | 32 | ||
656 | 33 | @inlineCallbacks | ||
657 | 32 | def tearDown(self): | 34 | def tearDown(self): |
659 | 33 | deleteTree(handle=self.client.handle) | 35 | yield deleteTree(self.client) |
660 | 34 | self.client.close() | 36 | self.client.close() |
661 | 35 | 37 | ||
662 | 36 | def test_name(self): | 38 | def test_name(self): |
663 | @@ -81,8 +83,9 @@ | |||
664 | 81 | zookeeper.set_debug_level(0) | 83 | zookeeper.set_debug_level(0) |
665 | 82 | self.client = yield self.get_zookeeper_client().connect() | 84 | self.client = yield self.get_zookeeper_client().connect() |
666 | 83 | 85 | ||
667 | 86 | @inlineCallbacks | ||
668 | 84 | def tearDown(self): | 87 | def tearDown(self): |
670 | 85 | deleteTree(handle=self.client.handle) | 88 | yield deleteTree(self.client) |
671 | 86 | self.client.close() | 89 | self.client.close() |
672 | 87 | 90 | ||
673 | 88 | def test_uninitialized_usage(self): | 91 | def test_uninitialized_usage(self): |
674 | @@ -209,8 +212,9 @@ | |||
675 | 209 | self.client = yield self.get_zookeeper_client().connect() | 212 | self.client = yield self.get_zookeeper_client().connect() |
676 | 210 | self.client.create("/otp") | 213 | self.client.create("/otp") |
677 | 211 | 214 | ||
678 | 215 | @inlineCallbacks | ||
679 | 212 | def tearDown(self): | 216 | def tearDown(self): |
681 | 213 | deleteTree(handle=self.client.handle) | 217 | yield deleteTree(self.client) |
682 | 214 | self.client.close() | 218 | self.client.close() |
683 | 215 | 219 | ||
684 | 216 | def set_otp_test_ace(self, test_ace=ZOO_OPEN_ACL_UNSAFE): | 220 | def set_otp_test_ace(self, test_ace=ZOO_OPEN_ACL_UNSAFE): |
685 | @@ -304,6 +308,27 @@ | |||
686 | 304 | children = yield self.client.get_children("/otp") | 308 | children = yield self.client.get_children("/otp") |
687 | 305 | self.assertFalse(children) | 309 | self.assertFalse(children) |
688 | 306 | 310 | ||
689 | 311 | @inlineCallbacks | ||
690 | 312 | def test_create_and_store(self): | ||
691 | 313 | """Creates an otp principal and stores it serialization at path. | ||
692 | 314 | """ | ||
693 | 315 | yield OTPPrincipal(self.client).create( | ||
694 | 316 | "aladdin", store="/zoo") | ||
695 | 317 | |||
696 | 318 | # Verify the otp serialization on the node | ||
697 | 319 | contents, stat = yield self.client.get("/zoo") | ||
698 | 320 | data = yaml.load(contents) | ||
699 | 321 | self.assertIn("otp-identity", data) | ||
700 | 322 | |||
701 | 323 | # Compare the persitsent user generated token to that in the tokendb | ||
702 | 324 | creds = yield OTPPrincipal.consume(self.client, path="/zoo") | ||
703 | 325 | |||
704 | 326 | token = yield TokenDatabase(self.client).get("aladdin") | ||
705 | 327 | self.assertEqual( | ||
706 | 328 | token, make_identity(creds)) | ||
707 | 329 | |||
708 | 330 | self.assertTrue((yield self.client.exists("/zoo"))) | ||
709 | 331 | |||
710 | 307 | 332 | ||
711 | 308 | class TokenDatabaseTest(TestCase): | 333 | class TokenDatabaseTest(TestCase): |
712 | 309 | 334 | ||
713 | @@ -313,8 +338,9 @@ | |||
714 | 313 | self.client = yield self.get_zookeeper_client().connect() | 338 | self.client = yield self.get_zookeeper_client().connect() |
715 | 314 | self.db = TokenDatabase(self.client, "/token-test") | 339 | self.db = TokenDatabase(self.client, "/token-test") |
716 | 315 | 340 | ||
717 | 341 | @inlineCallbacks | ||
718 | 316 | def tearDown(self): | 342 | def tearDown(self): |
720 | 317 | deleteTree(handle=self.client.handle) | 343 | yield deleteTree(self.client) |
721 | 318 | self.client.close() | 344 | self.client.close() |
722 | 319 | 345 | ||
723 | 320 | @inlineCallbacks | 346 | @inlineCallbacks |
724 | @@ -359,8 +385,9 @@ | |||
725 | 359 | yield self.tokens.add(Principal("admin", "admin")) | 385 | yield self.tokens.add(Principal("admin", "admin")) |
726 | 360 | self.policy = SecurityPolicy(self.client, self.tokens) | 386 | self.policy = SecurityPolicy(self.client, self.tokens) |
727 | 361 | 387 | ||
728 | 388 | @inlineCallbacks | ||
729 | 362 | def tearDown(self): | 389 | def tearDown(self): |
731 | 363 | deleteTree(handle=self.client.handle) | 390 | yield deleteTree(self.client) |
732 | 364 | self.client.close() | 391 | self.client.close() |
733 | 365 | 392 | ||
734 | 366 | @inlineCallbacks | 393 | @inlineCallbacks |
735 | @@ -483,8 +510,9 @@ | |||
736 | 483 | self.policy = SecurityPolicy(self.client, self.token_db, owner=admin) | 510 | self.policy = SecurityPolicy(self.client, self.token_db, owner=admin) |
737 | 484 | yield admin.attach(self.client) | 511 | yield admin.attach(self.client) |
738 | 485 | 512 | ||
739 | 513 | @inlineCallbacks | ||
740 | 486 | def tearDown(self): | 514 | def tearDown(self): |
742 | 487 | deleteTree(handle=self.client.handle) | 515 | yield deleteTree(self.client) |
743 | 488 | self.client.close() | 516 | self.client.close() |
744 | 489 | 517 | ||
745 | 490 | @inlineCallbacks | 518 | @inlineCallbacks |
746 | @@ -529,8 +557,9 @@ | |||
747 | 529 | self.policy = SecurityPolicy(self.client, self.tokens) | 557 | self.policy = SecurityPolicy(self.client, self.tokens) |
748 | 530 | yield self.admin.attach(self.client) | 558 | yield self.admin.attach(self.client) |
749 | 531 | 559 | ||
750 | 560 | @inlineCallbacks | ||
751 | 532 | def tearDown(self): | 561 | def tearDown(self): |
753 | 533 | deleteTree(handle=self.client.handle) | 562 | yield deleteTree(self.client) |
754 | 534 | self.client.close() | 563 | self.client.close() |
755 | 535 | 564 | ||
756 | 536 | @inlineCallbacks | 565 | @inlineCallbacks |
757 | @@ -647,7 +676,6 @@ | |||
758 | 647 | Test of the top level conviencce functions in the security module. | 676 | Test of the top level conviencce functions in the security module. |
759 | 648 | """ | 677 | """ |
760 | 649 | 678 | ||
761 | 650 | @security_test | ||
762 | 651 | @inlineCallbacks | 679 | @inlineCallbacks |
763 | 652 | def test_apply_rules(self): | 680 | def test_apply_rules(self): |
764 | 653 | """Calculates and sets an acl using the security rules. | 681 | """Calculates and sets an acl using the security rules. |
765 | @@ -676,101 +704,3 @@ | |||
766 | 676 | acl, | 704 | acl, |
767 | 677 | [make_ace(token, all=True), | 705 | [make_ace(token, all=True), |
768 | 678 | make_ace(cli_group.get_token(), all=True)]) | 706 | make_ace(cli_group.get_token(), all=True)]) |
769 | 679 | |||
770 | 680 | |||
771 | 681 | class DomainSecurityTest(object): | ||
772 | 682 | |||
773 | 683 | @inlineCallbacks | ||
774 | 684 | def setUp(self): | ||
775 | 685 | yield super(DomainSecurityTest, self).setUp() | ||
776 | 686 | path = yield self.client.create("/zoo") | ||
777 | 687 | self.security = NodeSecurity(self.client, path) | ||
778 | 688 | |||
779 | 689 | @inlineCallbacks | ||
780 | 690 | def test_add_group(self): | ||
781 | 691 | """Creates a group principal and adds it to the token db. | ||
782 | 692 | """ | ||
783 | 693 | self.security.enabled = False | ||
784 | 694 | group = yield self.security.add_group("group-a") | ||
785 | 695 | self.assertEqual(group, None) | ||
786 | 696 | self.assertFalse((yield self.client.exists("/zoo/group"))) | ||
787 | 697 | |||
788 | 698 | self.security.enabled = True | ||
789 | 699 | group = yield self.security.add_group("group-a") | ||
790 | 700 | self.assertTrue(isinstance(group, GroupPrincipal)) | ||
791 | 701 | token = yield TokenDatabase(self.client).get("group-a") | ||
792 | 702 | self.assertEqual(token, group.get_token()) | ||
793 | 703 | |||
794 | 704 | @security_test | ||
795 | 705 | @inlineCallbacks | ||
796 | 706 | def test_consume_otp(self): | ||
797 | 707 | """OTP can be consumed to retrieve the persistent principal.""" | ||
798 | 708 | |||
799 | 709 | yield self.assertFailure( | ||
800 | 710 | self.security.consume_otp(), PrincipalNotFound) | ||
801 | 711 | |||
802 | 712 | self.security.enabled = False | ||
803 | 713 | self.assertEqual((yield self.security.consume_otp()), None) | ||
804 | 714 | |||
805 | 715 | self.security.enabled = True | ||
806 | 716 | yield self.security.apply_otp("aladdin") | ||
807 | 717 | principal = yield self.security.consume_otp() | ||
808 | 718 | self.assertEqual(principal.name, "aladdin") | ||
809 | 719 | token = yield TokenDatabase(self.client).get("aladdin") | ||
810 | 720 | self.assertEqual(principal.get_token(), token) | ||
811 | 721 | self.assertFalse( | ||
812 | 722 | (yield self.client.get_children("/otp"))) | ||
813 | 723 | |||
814 | 724 | @security_test | ||
815 | 725 | @inlineCallbacks | ||
816 | 726 | def test_apply_otp(self): | ||
817 | 727 | """Creates an otp principal and stores it serialization at path. | ||
818 | 728 | """ | ||
819 | 729 | self.security.enabled = False | ||
820 | 730 | yield self.security.apply_otp("aladdin") | ||
821 | 731 | |||
822 | 732 | # Verify the otp serialization on the node | ||
823 | 733 | contents, stat = yield self.client.get("/zoo") | ||
824 | 734 | data = yaml.load(contents) | ||
825 | 735 | self.assertFalse(data) | ||
826 | 736 | |||
827 | 737 | self.security.enabled = True | ||
828 | 738 | yield self.security.apply_otp("aladdin") | ||
829 | 739 | |||
830 | 740 | # Verify the otp serialization on the node | ||
831 | 741 | contents, stat = yield self.client.get("/zoo") | ||
832 | 742 | data = yaml.load(contents) | ||
833 | 743 | self.assertIn("otp-identity", data) | ||
834 | 744 | |||
835 | 745 | # Compare the persitsent user generated token to that in the tokendb | ||
836 | 746 | user, password = yield OTPPrincipal.consume( | ||
837 | 747 | self.client, data["otp-identity"]) | ||
838 | 748 | token = yield TokenDatabase(self.client).get("aladdin") | ||
839 | 749 | self.assertEqual( | ||
840 | 750 | token, make_identity("%s:%s" % (user, password))) | ||
841 | 751 | |||
842 | 752 | @inlineCallbacks | ||
843 | 753 | def test_add_remove_group_member(self): | ||
844 | 754 | """Adding and removing members of a security group.""" | ||
845 | 755 | self.security.enabled = True | ||
846 | 756 | group = yield self.security.add_group("group-a") | ||
847 | 757 | |||
848 | 758 | self.security.enabled = False | ||
849 | 759 | yield self.security.add_group_member(Principal("music", "match")) | ||
850 | 760 | members = yield group.get_members() | ||
851 | 761 | self.assertNotIn("music", members) | ||
852 | 762 | |||
853 | 763 | self.security.enabled = True | ||
854 | 764 | yield self.security.add_group_member(Principal("music", "match")) | ||
855 | 765 | members = yield group.get_members() | ||
856 | 766 | self.assertIn("music", members) | ||
857 | 767 | |||
858 | 768 | self.security.enabled = False | ||
859 | 769 | yield self.security.remove_group_member("music") | ||
860 | 770 | members = yield group.get_members() | ||
861 | 771 | self.assertIn("music", members) | ||
862 | 772 | |||
863 | 773 | self.security.enabled = True | ||
864 | 774 | yield self.security.remove_group_member("music") | ||
865 | 775 | members = yield group.get_members() | ||
866 | 776 | self.assertNotIn("music", members) | ||
867 | 777 | 707 | ||
868 | === added file 'juju/state/tests/test_securityrules.py' | |||
869 | --- juju/state/tests/test_securityrules.py 1970-01-01 00:00:00 +0000 | |||
870 | +++ juju/state/tests/test_securityrules.py 2012-08-10 11:01:24 +0000 | |||
871 | @@ -0,0 +1,223 @@ | |||
872 | 1 | from twisted.internet.defer import inlineCallbacks | ||
873 | 2 | |||
874 | 3 | from juju.state.auth import make_ace | ||
875 | 4 | from juju.state.security import Principal, TokenDatabase, SecurityPolicy | ||
876 | 5 | from juju.state import securityrules as rules | ||
877 | 6 | from juju.state.topology import InternalTopology | ||
878 | 7 | |||
879 | 8 | from juju.state.tests.common import StateTestBase | ||
880 | 9 | |||
881 | 10 | |||
882 | 11 | class RulesTest(StateTestBase): | ||
883 | 12 | |||
884 | 13 | @inlineCallbacks | ||
885 | 14 | def setUp(self): | ||
886 | 15 | yield super(RulesTest, self).setUp() | ||
887 | 16 | self.principals = { | ||
888 | 17 | "admin": Principal("admin", "admin"), | ||
889 | 18 | "provider/0": Principal("provider/0", "provisioning-agent"), | ||
890 | 19 | "juju-provider": Principal( | ||
891 | 20 | "juju-provider", "provisioning-group"), | ||
892 | 21 | "machine/0": Principal("machine/0", "machine-agent"), | ||
893 | 22 | "service/0": Principal("service/0", "unit-agent"), | ||
894 | 23 | "service": Principal("service", "service-group") | ||
895 | 24 | } | ||
896 | 25 | self.token_db = TokenDatabase(self.client) | ||
897 | 26 | |||
898 | 27 | for p in self.principals.values(): | ||
899 | 28 | yield self.token_db.add(p) | ||
900 | 29 | |||
901 | 30 | self.policy = SecurityPolicy(self.client, self.token_db) | ||
902 | 31 | |||
903 | 32 | @inlineCallbacks | ||
904 | 33 | def test_formula_rule(self): | ||
905 | 34 | self.assertEqual( | ||
906 | 35 | (yield rules.formula_rule(self.policy, "/abc")), | ||
907 | 36 | None) | ||
908 | 37 | self.assertEqual( | ||
909 | 38 | (yield rules.formula_rule(self.policy, "/formulas/formula-0")), | ||
910 | 39 | [make_ace("anyone", "world", read=True)]) | ||
911 | 40 | |||
912 | 41 | @inlineCallbacks | ||
913 | 42 | def test_machine_rule(self): | ||
914 | 43 | yield self.token_db.add(Principal("machine-1", "")) | ||
915 | 44 | |||
916 | 45 | self.assertEqual( | ||
917 | 46 | (yield rules.machine_rule(self.policy, "/xyz")), | ||
918 | 47 | None) | ||
919 | 48 | self.assertEqual( | ||
920 | 49 | (yield rules.machine_rule(self.policy, "/machines")), | ||
921 | 50 | None) | ||
922 | 51 | self.assertEqual( | ||
923 | 52 | (yield rules.machine_rule(self.policy, "/machines/machine-1")), | ||
924 | 53 | [make_ace(Principal("machine-1", "").get_token(), | ||
925 | 54 | read=True, write=True, create=True), | ||
926 | 55 | make_ace(self.principals["juju-provider"].get_token(), | ||
927 | 56 | read=True, write=True)]) | ||
928 | 57 | |||
929 | 58 | @inlineCallbacks | ||
930 | 59 | def test_relation_rule_client_server(self): | ||
931 | 60 | topology = InternalTopology() | ||
932 | 61 | topology.add_service("service-0000001", "mysql") | ||
933 | 62 | topology.add_service("service-0000002", "wordpress") | ||
934 | 63 | topology.add_relation("relation-0000001", "database") | ||
935 | 64 | topology.assign_service_to_relation( | ||
936 | 65 | "relation-0000001", "service-0000001", "app", "server") | ||
937 | 66 | topology.assign_service_to_relation( | ||
938 | 67 | "relation-0000001", "service-0000002", "db", "client") | ||
939 | 68 | |||
940 | 69 | mysql_principal = Principal("service-0000001", "mysql") | ||
941 | 70 | wordpress_principal = Principal("service-0000002", "wordpress") | ||
942 | 71 | yield self.token_db.add(mysql_principal) | ||
943 | 72 | yield self.token_db.add(wordpress_principal) | ||
944 | 73 | |||
945 | 74 | yield self.client.create("/topology", topology.dump()) | ||
946 | 75 | |||
947 | 76 | self.assertEqual( | ||
948 | 77 | (yield rules.relation_rule(self.policy, "/xyz")), | ||
949 | 78 | None) | ||
950 | 79 | |||
951 | 80 | self.assertEqual( | ||
952 | 81 | (yield rules.relation_rule(self.policy, "/relations")), | ||
953 | 82 | None) | ||
954 | 83 | |||
955 | 84 | self.assertEqual( | ||
956 | 85 | (yield rules.relation_rule( | ||
957 | 86 | self.policy, "/relations/relation-0000001/settings/unit-x")), | ||
958 | 87 | [make_ace(mysql_principal.get_token(), read=True), | ||
959 | 88 | make_ace(wordpress_principal.get_token(), read=True)] | ||
960 | 89 | ) | ||
961 | 90 | |||
962 | 91 | self.assertEqual( | ||
963 | 92 | (yield rules.relation_rule( | ||
964 | 93 | self.policy, "/relations/relation-0000001/client/unit-x")), | ||
965 | 94 | [make_ace(mysql_principal.get_token(), read=True), | ||
966 | 95 | make_ace(wordpress_principal.get_token(), read=True)] | ||
967 | 96 | ) | ||
968 | 97 | |||
969 | 98 | self.assertEqual( | ||
970 | 99 | (yield rules.relation_rule( | ||
971 | 100 | self.policy, "/relations/relation-0000001/server")), | ||
972 | 101 | [make_ace(mysql_principal.get_token(), create=True, read=True), | ||
973 | 102 | make_ace(wordpress_principal.get_token(), read=True)] | ||
974 | 103 | ) | ||
975 | 104 | |||
976 | 105 | self.assertEqual( | ||
977 | 106 | (yield rules.relation_rule( | ||
978 | 107 | self.policy, "/relations/relation-0000001/client")), | ||
979 | 108 | [make_ace(wordpress_principal.get_token(), create=True, read=True), | ||
980 | 109 | make_ace(mysql_principal.get_token(), read=True)] | ||
981 | 110 | ) | ||
982 | 111 | |||
983 | 112 | @inlineCallbacks | ||
984 | 113 | def test_relation_rule_peer(self): | ||
985 | 114 | topology = InternalTopology() | ||
986 | 115 | topology.add_service("service-0000001", "riak") | ||
987 | 116 | topology.add_relation("relation-0000001", "ring") | ||
988 | 117 | topology.assign_service_to_relation( | ||
989 | 118 | "relation-0000001", "service-0000001", "ring", "peer") | ||
990 | 119 | riak_principal = Principal("service-0000001", "riak") | ||
991 | 120 | yield self.token_db.add(riak_principal) | ||
992 | 121 | yield self.client.create("/topology", topology.dump()) | ||
993 | 122 | |||
994 | 123 | self.assertEqual( | ||
995 | 124 | (yield rules.relation_rule( | ||
996 | 125 | self.policy, "/relations/relation-0000001/settings/unit-x")), | ||
997 | 126 | [make_ace(riak_principal.get_token(), read=True)] | ||
998 | 127 | ) | ||
999 | 128 | |||
1000 | 129 | self.assertEqual( | ||
1001 | 130 | (yield rules.relation_rule( | ||
1002 | 131 | self.policy, "/relations/relation-0000001/peer/unit-x")), | ||
1003 | 132 | [make_ace(riak_principal.get_token(), read=True)] | ||
1004 | 133 | ) | ||
1005 | 134 | |||
1006 | 135 | self.assertEqual( | ||
1007 | 136 | (yield rules.relation_rule( | ||
1008 | 137 | self.policy, "/relations/relation-0000001/peer")), | ||
1009 | 138 | [make_ace(riak_principal.get_token(), create=True, read=True)] | ||
1010 | 139 | ) | ||
1011 | 140 | |||
1012 | 141 | @inlineCallbacks | ||
1013 | 142 | def test_global_rule(self): | ||
1014 | 143 | self.assertEqual( | ||
1015 | 144 | (yield rules.global_rule(self.policy, "/xyz")), | ||
1016 | 145 | None) | ||
1017 | 146 | self.assertEqual( | ||
1018 | 147 | (yield rules.global_rule(self.policy, "/topology")), | ||
1019 | 148 | [make_ace("anyone", "world", read=True)]) | ||
1020 | 149 | |||
1021 | 150 | self.assertEqual( | ||
1022 | 151 | (yield rules.global_rule(self.policy, "/auth-tokens")), | ||
1023 | 152 | [make_ace("anyone", "world", write=True, read=True)]) | ||
1024 | 153 | |||
1025 | 154 | @inlineCallbacks | ||
1026 | 155 | def test_service_rule(self): | ||
1027 | 156 | |||
1028 | 157 | topology = InternalTopology() | ||
1029 | 158 | topology.add_service("service-0000001", "riak") | ||
1030 | 159 | topology.add_service_unit("service-0000001", "unit-0000001") | ||
1031 | 160 | service_principal = Principal("service-0000001", "") | ||
1032 | 161 | |||
1033 | 162 | yield self.token_db.add(service_principal) | ||
1034 | 163 | yield self.client.create("/topology", topology.dump()) | ||
1035 | 164 | |||
1036 | 165 | self.assertEqual( | ||
1037 | 166 | (yield rules.service_rule(self.policy, "/abc")), | ||
1038 | 167 | None) | ||
1039 | 168 | |||
1040 | 169 | self.assertEqual( | ||
1041 | 170 | (yield rules.service_rule(self.policy, "/services")), | ||
1042 | 171 | None) | ||
1043 | 172 | |||
1044 | 173 | self.assertEqual( | ||
1045 | 174 | (yield rules.service_rule( | ||
1046 | 175 | self.policy, "/services/service-0000001")), | ||
1047 | 176 | [make_ace(service_principal.get_token(), read=True)] | ||
1048 | 177 | ) | ||
1049 | 178 | |||
1050 | 179 | self.assertEqual( | ||
1051 | 180 | (yield rules.service_rule( | ||
1052 | 181 | self.policy, "/services/service-0000001/config")), | ||
1053 | 182 | [make_ace( | ||
1054 | 183 | service_principal.get_token(), read=True)] | ||
1055 | 184 | ) | ||
1056 | 185 | |||
1057 | 186 | self.assertEqual( | ||
1058 | 187 | (yield rules.service_rule( | ||
1059 | 188 | self.policy, "/services/service-0000001/exposed")), | ||
1060 | 189 | [make_ace( | ||
1061 | 190 | self.principals["juju-provider"].get_token(), read=True), | ||
1062 | 191 | make_ace(service_principal.get_token(), read=True, write=True)] | ||
1063 | 192 | ) | ||
1064 | 193 | |||
1065 | 194 | @inlineCallbacks | ||
1066 | 195 | def test_unit_rule(self): | ||
1067 | 196 | |||
1068 | 197 | topology = InternalTopology() | ||
1069 | 198 | topology.add_service("service-0000001", "riak") | ||
1070 | 199 | topology.add_service_unit("service-0000001", "unit-0000001") | ||
1071 | 200 | yield self.client.create("/topology", topology.dump()) | ||
1072 | 201 | |||
1073 | 202 | riak_unit = Principal("unit-0000001", "abc") | ||
1074 | 203 | yield self.token_db.add(riak_unit) | ||
1075 | 204 | |||
1076 | 205 | self.assertEqual( | ||
1077 | 206 | (yield rules.unit_rule(self.policy, "/xyz")), None) | ||
1078 | 207 | |||
1079 | 208 | self.assertEqual( | ||
1080 | 209 | (yield rules.unit_rule(self.policy, "/units")), None) | ||
1081 | 210 | |||
1082 | 211 | self.assertEqual( | ||
1083 | 212 | (yield rules.unit_rule(self.policy, "/units/unit-0000001")), | ||
1084 | 213 | [make_ace(riak_unit.get_token(), all=True)]) | ||
1085 | 214 | |||
1086 | 215 | self.assertEqual( | ||
1087 | 216 | (yield rules.unit_rule(self.policy, "/units/unit-0000001/ports")), | ||
1088 | 217 | [make_ace(riak_unit.get_token(), all=True), | ||
1089 | 218 | make_ace( | ||
1090 | 219 | self.principals["juju-provider"].get_token(), read=True)]) | ||
1091 | 220 | |||
1092 | 221 | self.assertEqual( | ||
1093 | 222 | (yield rules.unit_rule(self.policy, "/units/unit-0000001/debug")), | ||
1094 | 223 | [make_ace(riak_unit.get_token(), all=True)]) | ||
1095 | 0 | 224 | ||
1096 | === modified file 'juju/state/tests/test_service.py' | |||
1097 | --- juju/state/tests/test_service.py 2012-08-10 11:01:23 +0000 | |||
1098 | +++ juju/state/tests/test_service.py 2012-08-10 11:01:24 +0000 | |||
1099 | @@ -15,11 +15,14 @@ | |||
1100 | 15 | from juju.lib.pick import pick_attr | 15 | from juju.lib.pick import pick_attr |
1101 | 16 | from juju.state.charm import CharmStateManager | 16 | from juju.state.charm import CharmStateManager |
1102 | 17 | from juju.charm.tests.test_repository import unbundled_repository | 17 | from juju.charm.tests.test_repository import unbundled_repository |
1103 | 18 | from juju.state.auth import make_identity | ||
1104 | 18 | from juju.state.endpoint import RelationEndpoint | 19 | from juju.state.endpoint import RelationEndpoint |
1105 | 19 | from juju.state.service import ( | 20 | from juju.state.service import ( |
1106 | 20 | ServiceStateManager, NO_HOOKS, RETRY_HOOKS, parse_service_name) | 21 | ServiceStateManager, NO_HOOKS, RETRY_HOOKS, parse_service_name) |
1107 | 21 | from juju.state.machine import MachineStateManager | 22 | from juju.state.machine import MachineStateManager |
1108 | 22 | from juju.state.relation import RelationStateManager | 23 | from juju.state.relation import RelationStateManager |
1109 | 24 | from juju.state.security import GroupPrincipal | ||
1110 | 25 | |||
1111 | 23 | from juju.state.utils import YAMLState | 26 | from juju.state.utils import YAMLState |
1112 | 24 | from juju.state.errors import ( | 27 | from juju.state.errors import ( |
1113 | 25 | StateChanged, ServiceStateNotFound, ServiceUnitStateNotFound, | 28 | StateChanged, ServiceStateNotFound, ServiceUnitStateNotFound, |
1114 | @@ -191,6 +194,17 @@ | |||
1115 | 191 | "service-0000000001") | 194 | "service-0000000001") |
1116 | 192 | 195 | ||
1117 | 193 | @inlineCallbacks | 196 | @inlineCallbacks |
1118 | 197 | def test_add_service_security(self): | ||
1119 | 198 | """Adding a service state also creates a corresponding service group. | ||
1120 | 199 | """ | ||
1121 | 200 | yield self.service_state_manager.add_service_state( | ||
1122 | 201 | "wordpress", self.charm_state, dummy_constraints) | ||
1123 | 202 | acl, stat = yield self.client.get_acl("/services/service-0000000000") | ||
1124 | 203 | self.assertACE(acl, make_identity("admin:admin")) | ||
1125 | 204 | self.assertTrue( | ||
1126 | 205 | (yield self.client.exists("/services/service-0000000000/group"))) | ||
1127 | 206 | |||
1128 | 207 | @inlineCallbacks | ||
1129 | 194 | def test_add_service_with_duplicated_name(self): | 208 | def test_add_service_with_duplicated_name(self): |
1130 | 195 | """ | 209 | """ |
1131 | 196 | If a service is added with a duplicated name, a meaningful | 210 | If a service is added with a duplicated name, a meaningful |
1132 | @@ -234,24 +248,42 @@ | |||
1133 | 234 | else: | 248 | else: |
1134 | 235 | self.fail("Error not raised") | 249 | self.fail("Error not raised") |
1135 | 236 | 250 | ||
1136 | 251 | @inlineCallbacks | ||
1137 | 237 | def test_get_unit_state(self): | 252 | def test_get_unit_state(self): |
1138 | 238 | """A unit state can be retrieved by name from the service manager.""" | 253 | """A unit state can be retrieved by name from the service manager.""" |
1139 | 254 | <<<<<<< TREE | ||
1140 | 239 | self.assertFailure(self.service_state_manager.get_unit_state( | 255 | self.assertFailure(self.service_state_manager.get_unit_state( |
1141 | 240 | "wordpress/1"), ServiceStateNotFound) | 256 | "wordpress/1"), ServiceStateNotFound) |
1142 | 257 | ======= | ||
1143 | 258 | yield self.assertFailure(self.service_state_manager.get_unit_state( | ||
1144 | 259 | "wordpress/1"), ServiceStateNotFound) | ||
1145 | 260 | >>>>>>> MERGE-SOURCE | ||
1146 | 241 | 261 | ||
1147 | 262 | <<<<<<< TREE | ||
1148 | 242 | self.assertFailure(self.service_state_manager.get_unit_state( | 263 | self.assertFailure(self.service_state_manager.get_unit_state( |
1149 | 243 | "wordpress1"), ServiceUnitStateNotFound) | 264 | "wordpress1"), ServiceUnitStateNotFound) |
1150 | 244 | 265 | ||
1151 | 266 | ======= | ||
1152 | 267 | >>>>>>> MERGE-SOURCE | ||
1153 | 245 | wordpress_state = yield self.service_state_manager.add_service_state( | 268 | wordpress_state = yield self.service_state_manager.add_service_state( |
1154 | 246 | "wordpress", self.charm_state, dummy_constraints) | 269 | "wordpress", self.charm_state, dummy_constraints) |
1155 | 247 | 270 | ||
1156 | 271 | <<<<<<< TREE | ||
1157 | 248 | self.assertFailure(self.service_state_manager.get_unit_state( | 272 | self.assertFailure(self.service_state_manager.get_unit_state( |
1158 | 249 | "wordpress/1"), ServiceUnitStateNotFound) | 273 | "wordpress/1"), ServiceUnitStateNotFound) |
1159 | 274 | ======= | ||
1160 | 275 | yield self.assertFailure(self.service_state_manager.get_unit_state( | ||
1161 | 276 | "wordpress/0"), ServiceUnitStateNotFound) | ||
1162 | 277 | >>>>>>> MERGE-SOURCE | ||
1163 | 250 | 278 | ||
1165 | 251 | wordpress_unit = wordpress_state.add_unit_state() | 279 | wordpress_unit = yield wordpress_state.add_unit_state() |
1166 | 252 | 280 | ||
1167 | 253 | unit_state = yield self.service_state_manager.get_unit_state( | 281 | unit_state = yield self.service_state_manager.get_unit_state( |
1168 | 282 | <<<<<<< TREE | ||
1169 | 254 | "wordpress/1") | 283 | "wordpress/1") |
1170 | 284 | ======= | ||
1171 | 285 | "wordpress/0") | ||
1172 | 286 | >>>>>>> MERGE-SOURCE | ||
1173 | 255 | 287 | ||
1174 | 256 | self.assertEqual(unit_state.internal_id, wordpress_unit.internal_id) | 288 | self.assertEqual(unit_state.internal_id, wordpress_unit.internal_id) |
1175 | 257 | 289 | ||
1176 | @@ -646,6 +678,30 @@ | |||
1177 | 646 | exists = yield self.client.exists("/units/%s" % unit_state.internal_id) | 678 | exists = yield self.client.exists("/units/%s" % unit_state.internal_id) |
1178 | 647 | self.assertFalse(exists) | 679 | self.assertFalse(exists) |
1179 | 648 | 680 | ||
1180 | 681 | @inlineCallbacks | ||
1181 | 682 | def test_remove_service_unit_security(self): | ||
1182 | 683 | """ | ||
1183 | 684 | """ | ||
1184 | 685 | service_state = yield self.service_state_manager.add_service_state( | ||
1185 | 686 | "wordpress", self.charm_state, dummy_constraints) | ||
1186 | 687 | |||
1187 | 688 | unit_state0 = yield service_state.add_unit_state() | ||
1188 | 689 | unit_state1 = yield service_state.add_unit_state() | ||
1189 | 690 | |||
1190 | 691 | group = GroupPrincipal( | ||
1191 | 692 | self.client, "/services/%s/group" % service_state.internal_id) | ||
1192 | 693 | members = yield group.get_members() | ||
1193 | 694 | |||
1194 | 695 | self.assertTrue(unit_state0.internal_id in members) | ||
1195 | 696 | self.assertTrue(unit_state1.internal_id in members) | ||
1196 | 697 | |||
1197 | 698 | yield service_state.remove_unit_state(unit_state0) | ||
1198 | 699 | |||
1199 | 700 | members = yield group.get_members() | ||
1200 | 701 | self.assertFalse(unit_state0.internal_id in members) | ||
1201 | 702 | self.assertTrue(unit_state1.internal_id in members) | ||
1202 | 703 | |||
1203 | 704 | @inlineCallbacks | ||
1204 | 649 | def test_remove_service_unit_nonexistant(self): | 705 | def test_remove_service_unit_nonexistant(self): |
1205 | 650 | """Removing a non existant service unit, is fine.""" | 706 | """Removing a non existant service unit, is fine.""" |
1206 | 651 | 707 | ||
1207 | @@ -653,7 +709,9 @@ | |||
1208 | 653 | "wordpress", self.charm_state, dummy_constraints) | 709 | "wordpress", self.charm_state, dummy_constraints) |
1209 | 654 | unit_state = yield service_state.add_unit_state() | 710 | unit_state = yield service_state.add_unit_state() |
1210 | 655 | yield service_state.remove_unit_state(unit_state) | 711 | yield service_state.remove_unit_state(unit_state) |
1212 | 656 | yield service_state.remove_unit_state(unit_state) | 712 | yield self.assertFailure( |
1213 | 713 | service_state.remove_unit_state(unit_state), | ||
1214 | 714 | StateChanged) | ||
1215 | 657 | 715 | ||
1216 | 658 | @inlineCallbacks | 716 | @inlineCallbacks |
1217 | 659 | def test_get_all_service_states(self): | 717 | def test_get_all_service_states(self): |
This is looking very good overall. Thanks!
A few comments:
[1]
+ machine_token = yield policy. get_token( machine_ id) get_token( "ensemble- provider" ) machine_ token, read=True, write=True, create=True), provider_ token, read=True, write=True)])
+ provider_token = yield policy.
+ returnValue([
+ make_ace(
+ make_ace(
This logic looks very nice.
As a minor from the previous review which still wasn't sorted, agent" and "ensemble-provider" are different things,
"provisioning-
I believe the above refers to the former, not to the latter.
[2]
+ for service_id, service_data in services.items(): data["service_ id"] = service_id data["token" ] = yield policy. get_token( service_ id) append( service_ data)
+ service_data = dict(service_data)
+ service_
+ service_
+ results.
Why is this using an arbitrary blob of data rather than a typed
value with proper fields? I think we've learned our lesson with the
machine_data.
[3]
+ matched_service = [data for data in service_data \ service] .pop() matched_ service[ "token" ], read=True, create=True), related_ service[ "token" ], read=True)])
+ if data["role"] == container].pop()
+ related_service = [data for data in service_data \
+ if data != matched_
+ returnValue(
+ [make_ace(
+ make_ace(
This was raised in the previous review for this branch as well: why
is the node a "container" rather than an individual node under the
relation-id node? Can't we have multiple nodes within those
containers?
[4]
+ # Unit agent can modify tweak any unitother unit settings.
Comment is a bit corrupted.
[5]
+ # Created by the unit agent, read by the provisioning agent.
Might be good to comment here that in the future it will be moved to
the machine agent.
[6]
+ elif path == "/topology": [make_ace( "anyone" , "world", read=True)])
+ # Only the admin cli can write to this.
+ returnValue(
Also part of the original review, IIRC. It feels bad to be exposing
this to _anyone_. We don't have to sort that out right now, but at
least a comment with a recommended future solution would be nice.
[7]
+ elif path == "/auth-tokens": [make_ace( "anyone" , "world", read=True, write=True)])
+ # Any connection that creates another user needs to modify this
+ returnValue(
_Anyone_ can write to the auth tokens!?