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  }