github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/client/perm_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package client_test 5 6 import ( 7 "strings" 8 9 "github.com/juju/names" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 "gopkg.in/juju/charm.v4" 13 14 "github.com/juju/juju/api" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/constraints" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/version" 19 ) 20 21 type permSuite struct { 22 baseSuite 23 } 24 25 var _ = gc.Suite(&permSuite{}) 26 27 // Most (if not all) of the permission tests below aim to test 28 // end-to-end operations execution through the API, but do not care 29 // about the results. They only test that a call is succeeds or fails 30 // (usually due to "permission denied"). There are separate test cases 31 // testing each individual API call data flow later on. 32 33 // allowed returns the set of allowed entities given an allow list and a 34 // deny list. If an allow list is specified, only those entities are 35 // allowed; otherwise those in deny are disallowed. 36 func allowed(all, allow, deny []names.Tag) map[names.Tag]bool { 37 p := make(map[names.Tag]bool) 38 if allow != nil { 39 for _, e := range allow { 40 p[e] = true 41 } 42 return p 43 } 44 loop: 45 for _, e0 := range all { 46 for _, e1 := range deny { 47 if e1 == e0 { 48 continue loop 49 } 50 } 51 p[e0] = true 52 } 53 return p 54 } 55 56 func (s *permSuite) TestOperationPerm(c *gc.C) { 57 var ( 58 userAdmin = s.AdminUserTag(c) 59 userOther = names.NewLocalUserTag("other") 60 ) 61 entities := s.setUpScenario(c) 62 for i, t := range []struct { 63 about string 64 // op performs the operation to be tested using the given state 65 // connection. It returns a function that should be used to 66 // undo any changes made by the operation. 67 op func(c *gc.C, st *api.State, mst *state.State) (reset func(), err error) 68 allow []names.Tag 69 deny []names.Tag 70 }{{ 71 about: "Client.Status", 72 op: opClientStatus, 73 allow: []names.Tag{userAdmin, userOther}, 74 }, { 75 about: "Client.ServiceSet", 76 op: opClientServiceSet, 77 allow: []names.Tag{userAdmin, userOther}, 78 }, { 79 about: "Client.ServiceSetYAML", 80 op: opClientServiceSetYAML, 81 allow: []names.Tag{userAdmin, userOther}, 82 }, { 83 about: "Client.ServiceGet", 84 op: opClientServiceGet, 85 allow: []names.Tag{userAdmin, userOther}, 86 }, { 87 about: "Client.Resolved", 88 op: opClientResolved, 89 allow: []names.Tag{userAdmin, userOther}, 90 }, { 91 about: "Client.ServiceExpose", 92 op: opClientServiceExpose, 93 allow: []names.Tag{userAdmin, userOther}, 94 }, { 95 about: "Client.ServiceUnexpose", 96 op: opClientServiceUnexpose, 97 allow: []names.Tag{userAdmin, userOther}, 98 }, { 99 about: "Client.ServiceDeploy", 100 op: opClientServiceDeploy, 101 allow: []names.Tag{userAdmin, userOther}, 102 }, { 103 about: "Client.ServiceDeployWithNetworks", 104 op: opClientServiceDeployWithNetworks, 105 allow: []names.Tag{userAdmin, userOther}, 106 }, { 107 about: "Client.ServiceUpdate", 108 op: opClientServiceUpdate, 109 allow: []names.Tag{userAdmin, userOther}, 110 }, { 111 about: "Client.ServiceSetCharm", 112 op: opClientServiceSetCharm, 113 allow: []names.Tag{userAdmin, userOther}, 114 }, { 115 about: "Client.GetAnnotations", 116 op: opClientGetAnnotations, 117 allow: []names.Tag{userAdmin, userOther}, 118 }, { 119 about: "Client.SetAnnotations", 120 op: opClientSetAnnotations, 121 allow: []names.Tag{userAdmin, userOther}, 122 }, { 123 about: "Client.AddServiceUnits", 124 op: opClientAddServiceUnits, 125 allow: []names.Tag{userAdmin, userOther}, 126 }, { 127 about: "Client.DestroyServiceUnits", 128 op: opClientDestroyServiceUnits, 129 allow: []names.Tag{userAdmin, userOther}, 130 }, { 131 about: "Client.ServiceDestroy", 132 op: opClientServiceDestroy, 133 allow: []names.Tag{userAdmin, userOther}, 134 }, { 135 about: "Client.GetServiceConstraints", 136 op: opClientGetServiceConstraints, 137 allow: []names.Tag{userAdmin, userOther}, 138 }, { 139 about: "Client.SetServiceConstraints", 140 op: opClientSetServiceConstraints, 141 allow: []names.Tag{userAdmin, userOther}, 142 }, { 143 about: "Client.SetEnvironmentConstraints", 144 op: opClientSetEnvironmentConstraints, 145 allow: []names.Tag{userAdmin, userOther}, 146 }, { 147 about: "Client.EnvironmentGet", 148 op: opClientEnvironmentGet, 149 allow: []names.Tag{userAdmin, userOther}, 150 }, { 151 about: "Client.EnvironmentSet", 152 op: opClientEnvironmentSet, 153 allow: []names.Tag{userAdmin, userOther}, 154 }, { 155 about: "Client.SetEnvironAgentVersion", 156 op: opClientSetEnvironAgentVersion, 157 allow: []names.Tag{userAdmin, userOther}, 158 }, { 159 about: "Client.WatchAll", 160 op: opClientWatchAll, 161 allow: []names.Tag{userAdmin, userOther}, 162 }, { 163 about: "Client.CharmInfo", 164 op: opClientCharmInfo, 165 allow: []names.Tag{userAdmin, userOther}, 166 }, { 167 about: "Client.AddRelation", 168 op: opClientAddRelation, 169 allow: []names.Tag{userAdmin, userOther}, 170 }, { 171 about: "Client.DestroyRelation", 172 op: opClientDestroyRelation, 173 allow: []names.Tag{userAdmin, userOther}, 174 }} { 175 allow := allowed(entities, t.allow, t.deny) 176 for j, e := range entities { 177 c.Logf("\n------\ntest %d,%d; %s; entity %q", i, j, t.about, e) 178 st := s.openAs(c, e) 179 reset, err := t.op(c, st, s.State) 180 if allow[e] { 181 c.Check(err, jc.ErrorIsNil) 182 } else { 183 c.Check(err, gc.ErrorMatches, "permission denied") 184 c.Check(err, jc.Satisfies, params.IsCodeUnauthorized) 185 } 186 reset() 187 st.Close() 188 } 189 } 190 } 191 192 func opClientCharmInfo(c *gc.C, st *api.State, mst *state.State) (func(), error) { 193 info, err := st.Client().CharmInfo("local:quantal/wordpress-3") 194 if err != nil { 195 c.Check(info, gc.IsNil) 196 return func() {}, err 197 } 198 c.Assert(info.URL, gc.Equals, "local:quantal/wordpress-3") 199 c.Assert(info.Meta.Name, gc.Equals, "wordpress") 200 c.Assert(info.Revision, gc.Equals, 3) 201 c.Assert(info.Actions, jc.DeepEquals, &charm.Actions{ 202 ActionSpecs: map[string]charm.ActionSpec{ 203 "fakeaction": { 204 Description: "No description", 205 Params: map[string]interface{}{ 206 "type": "object", 207 "description": "No description", 208 "properties": map[string]interface{}{}, 209 "title": "fakeaction", 210 }, 211 }, 212 }, 213 }) 214 return func() {}, nil 215 } 216 217 func opClientAddRelation(c *gc.C, st *api.State, mst *state.State) (func(), error) { 218 _, err := st.Client().AddRelation("nosuch1", "nosuch2") 219 if params.IsCodeNotFound(err) { 220 err = nil 221 } 222 return func() {}, err 223 } 224 225 func opClientDestroyRelation(c *gc.C, st *api.State, mst *state.State) (func(), error) { 226 err := st.Client().DestroyRelation("nosuch1", "nosuch2") 227 if params.IsCodeNotFound(err) { 228 err = nil 229 } 230 return func() {}, err 231 } 232 233 func opClientStatus(c *gc.C, st *api.State, mst *state.State) (func(), error) { 234 status, err := st.Client().Status(nil) 235 if err != nil { 236 c.Check(status, gc.IsNil) 237 return func() {}, err 238 } 239 c.Assert(status, jc.DeepEquals, scenarioStatus) 240 return func() {}, nil 241 } 242 243 func resetBlogTitle(c *gc.C, st *api.State) func() { 244 return func() { 245 err := st.Client().ServiceSet("wordpress", map[string]string{ 246 "blog-title": "", 247 }) 248 c.Assert(err, jc.ErrorIsNil) 249 } 250 } 251 252 func opClientServiceSet(c *gc.C, st *api.State, mst *state.State) (func(), error) { 253 err := st.Client().ServiceSet("wordpress", map[string]string{ 254 "blog-title": "foo", 255 }) 256 if err != nil { 257 return func() {}, err 258 } 259 return resetBlogTitle(c, st), nil 260 } 261 262 func opClientServiceSetYAML(c *gc.C, st *api.State, mst *state.State) (func(), error) { 263 err := st.Client().ServiceSetYAML("wordpress", `"wordpress": {"blog-title": "foo"}`) 264 if err != nil { 265 return func() {}, err 266 } 267 return resetBlogTitle(c, st), nil 268 } 269 270 func opClientServiceGet(c *gc.C, st *api.State, mst *state.State) (func(), error) { 271 _, err := st.Client().ServiceGet("wordpress") 272 if err != nil { 273 return func() {}, err 274 } 275 return func() {}, nil 276 } 277 278 func opClientServiceExpose(c *gc.C, st *api.State, mst *state.State) (func(), error) { 279 err := st.Client().ServiceExpose("wordpress") 280 if err != nil { 281 return func() {}, err 282 } 283 return func() { 284 svc, err := mst.Service("wordpress") 285 c.Assert(err, jc.ErrorIsNil) 286 svc.ClearExposed() 287 }, nil 288 } 289 290 func opClientServiceUnexpose(c *gc.C, st *api.State, mst *state.State) (func(), error) { 291 err := st.Client().ServiceUnexpose("wordpress") 292 if err != nil { 293 return func() {}, err 294 } 295 return func() {}, nil 296 } 297 298 func opClientResolved(c *gc.C, st *api.State, _ *state.State) (func(), error) { 299 err := st.Client().Resolved("wordpress/1", false) 300 // There are several scenarios in which this test is called, one is 301 // that the user is not authorized. In that case we want to exit now, 302 // letting the error percolate out so the caller knows that the 303 // permission error was correctly generated. 304 if err != nil && params.IsCodeUnauthorized(err) { 305 return func() {}, err 306 } 307 // Otherwise, the user was authorized, but we expect an error anyway 308 // because the unit is not in an error state when we tried to resolve 309 // the error. Therefore, since it is complaining it means that the 310 // call to Resolved worked, so we're happy. 311 c.Assert(err, gc.NotNil) 312 c.Assert(err.Error(), gc.Equals, `unit "wordpress/1" is not in an error state`) 313 return func() {}, nil 314 } 315 316 func opClientGetAnnotations(c *gc.C, st *api.State, mst *state.State) (func(), error) { 317 ann, err := st.Client().GetAnnotations("service-wordpress") 318 if err != nil { 319 return func() {}, err 320 } 321 c.Assert(ann, gc.DeepEquals, make(map[string]string)) 322 return func() {}, nil 323 } 324 325 func opClientSetAnnotations(c *gc.C, st *api.State, mst *state.State) (func(), error) { 326 pairs := map[string]string{"key1": "value1", "key2": "value2"} 327 err := st.Client().SetAnnotations("service-wordpress", pairs) 328 if err != nil { 329 return func() {}, err 330 } 331 return func() { 332 pairs := map[string]string{"key1": "", "key2": ""} 333 st.Client().SetAnnotations("service-wordpress", pairs) 334 }, nil 335 } 336 337 func opClientServiceDeploy(c *gc.C, st *api.State, mst *state.State) (func(), error) { 338 err := st.Client().ServiceDeploy("mad:bad/url-1", "x", 1, "", constraints.Value{}, "") 339 if err.Error() == `charm URL has invalid schema: "mad:bad/url-1"` { 340 err = nil 341 } 342 return func() {}, err 343 } 344 345 func opClientServiceDeployWithNetworks(c *gc.C, st *api.State, mst *state.State) (func(), error) { 346 err := st.Client().ServiceDeployWithNetworks("mad:bad/url-1", "x", 1, "", constraints.Value{}, "", nil, nil) 347 if err.Error() == `charm URL has invalid schema: "mad:bad/url-1"` { 348 err = nil 349 } 350 return func() {}, err 351 } 352 353 func opClientServiceUpdate(c *gc.C, st *api.State, mst *state.State) (func(), error) { 354 args := params.ServiceUpdate{ 355 ServiceName: "no-such-charm", 356 CharmUrl: "cs:quantal/wordpress-42", 357 ForceCharmUrl: true, 358 SettingsStrings: map[string]string{"blog-title": "foo"}, 359 SettingsYAML: `"wordpress": {"blog-title": "foo"}`, 360 } 361 err := st.Client().ServiceUpdate(args) 362 if params.IsCodeNotFound(err) { 363 err = nil 364 } 365 return func() {}, err 366 } 367 368 func opClientServiceSetCharm(c *gc.C, st *api.State, mst *state.State) (func(), error) { 369 err := st.Client().ServiceSetCharm("nosuch", "local:quantal/wordpress", false) 370 if params.IsCodeNotFound(err) { 371 err = nil 372 } 373 return func() {}, err 374 } 375 376 func opClientAddServiceUnits(c *gc.C, st *api.State, mst *state.State) (func(), error) { 377 _, err := st.Client().AddServiceUnits("nosuch", 1, "") 378 if params.IsCodeNotFound(err) { 379 err = nil 380 } 381 return func() {}, err 382 } 383 384 func opClientDestroyServiceUnits(c *gc.C, st *api.State, mst *state.State) (func(), error) { 385 err := st.Client().DestroyServiceUnits("wordpress/99") 386 if err != nil && strings.HasPrefix(err.Error(), "no units were destroyed") { 387 err = nil 388 } 389 return func() {}, err 390 } 391 392 func opClientServiceDestroy(c *gc.C, st *api.State, mst *state.State) (func(), error) { 393 err := st.Client().ServiceDestroy("non-existent") 394 if params.IsCodeNotFound(err) { 395 err = nil 396 } 397 return func() {}, err 398 } 399 400 func opClientGetServiceConstraints(c *gc.C, st *api.State, mst *state.State) (func(), error) { 401 _, err := st.Client().GetServiceConstraints("wordpress") 402 return func() {}, err 403 } 404 405 func opClientSetServiceConstraints(c *gc.C, st *api.State, mst *state.State) (func(), error) { 406 nullConstraints := constraints.Value{} 407 err := st.Client().SetServiceConstraints("wordpress", nullConstraints) 408 if err != nil { 409 return func() {}, err 410 } 411 return func() {}, nil 412 } 413 414 func opClientSetEnvironmentConstraints(c *gc.C, st *api.State, mst *state.State) (func(), error) { 415 nullConstraints := constraints.Value{} 416 err := st.Client().SetEnvironmentConstraints(nullConstraints) 417 if err != nil { 418 return func() {}, err 419 } 420 return func() {}, nil 421 } 422 423 func opClientEnvironmentGet(c *gc.C, st *api.State, mst *state.State) (func(), error) { 424 _, err := st.Client().EnvironmentGet() 425 if err != nil { 426 return func() {}, err 427 } 428 return func() {}, nil 429 } 430 431 func opClientEnvironmentSet(c *gc.C, st *api.State, mst *state.State) (func(), error) { 432 args := map[string]interface{}{"some-key": "some-value"} 433 err := st.Client().EnvironmentSet(args) 434 if err != nil { 435 return func() {}, err 436 } 437 return func() { 438 args["some-key"] = nil 439 st.Client().EnvironmentSet(args) 440 }, nil 441 } 442 443 func opClientSetEnvironAgentVersion(c *gc.C, st *api.State, mst *state.State) (func(), error) { 444 attrs, err := st.Client().EnvironmentGet() 445 if err != nil { 446 return func() {}, err 447 } 448 err = st.Client().SetEnvironAgentVersion(version.Current.Number) 449 if err != nil { 450 return func() {}, err 451 } 452 453 return func() { 454 oldAgentVersion, found := attrs["agent-version"] 455 if found { 456 versionString := oldAgentVersion.(string) 457 st.Client().SetEnvironAgentVersion(version.MustParse(versionString)) 458 } 459 }, nil 460 } 461 462 func opClientWatchAll(c *gc.C, st *api.State, mst *state.State) (func(), error) { 463 watcher, err := st.Client().WatchAll() 464 if err == nil { 465 watcher.Stop() 466 } 467 return func() {}, err 468 }