github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/applicationofferuser_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  	jc "github.com/juju/testing/checkers"
    10  	"github.com/juju/utils/v3"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/core/crossmodel"
    14  	"github.com/juju/juju/core/permission"
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/testing/factory"
    17  )
    18  
    19  type ApplicationOfferUserSuite struct {
    20  	ConnSuite
    21  }
    22  
    23  var _ = gc.Suite(&ApplicationOfferUserSuite{})
    24  
    25  func (s *ApplicationOfferUserSuite) makeOffer(c *gc.C, access permission.Access) (*crossmodel.ApplicationOffer, names.UserTag) {
    26  	s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
    27  	offers := state.NewApplicationOffers(s.State)
    28  	offer, err := offers.AddOffer(crossmodel.AddApplicationOfferArgs{
    29  		OfferName:       "someoffer",
    30  		ApplicationName: "mysql",
    31  		Owner:           "test-admin",
    32  		HasRead:         []string{"everyone@external"},
    33  	})
    34  	c.Assert(err, jc.ErrorIsNil)
    35  
    36  	user := s.Factory.MakeUser(c,
    37  		&factory.UserParams{
    38  			Name:   "validusername",
    39  			Access: permission.ReadAccess,
    40  		})
    41  
    42  	// Initially no access.
    43  	_, err = s.State.GetOfferAccess(offer.OfferUUID, user.UserTag())
    44  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    45  
    46  	err = s.State.CreateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user.UserTag(), access)
    47  	c.Assert(err, jc.ErrorIsNil)
    48  	return offer, user.UserTag()
    49  }
    50  
    51  func (s *ApplicationOfferUserSuite) assertAddOffer(c *gc.C, wantedAccess permission.Access) string {
    52  	offer, user := s.makeOffer(c, wantedAccess)
    53  
    54  	access, err := s.State.GetOfferAccess(offer.OfferUUID, user)
    55  	c.Assert(err, jc.ErrorIsNil)
    56  	c.Assert(access, gc.Equals, wantedAccess)
    57  
    58  	// Creator of offer has admin.
    59  	access, err = s.State.GetOfferAccess(offer.OfferUUID, names.NewUserTag("test-admin"))
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	c.Assert(access, gc.Equals, permission.AdminAccess)
    62  
    63  	// Everyone has read.
    64  	access, err = s.State.GetOfferAccess(offer.OfferUUID, names.NewUserTag("everyone@external"))
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	c.Assert(access, gc.Equals, permission.ReadAccess)
    67  	return offer.OfferUUID
    68  }
    69  
    70  func (s *ApplicationOfferUserSuite) TestAddReadOnlyOfferUser(c *gc.C) {
    71  	s.assertAddOffer(c, permission.ReadAccess)
    72  }
    73  
    74  func (s *ApplicationOfferUserSuite) TestAddConsumeOfferUser(c *gc.C) {
    75  	s.assertAddOffer(c, permission.ConsumeAccess)
    76  }
    77  
    78  func (s *ApplicationOfferUserSuite) TestGetOfferAccess(c *gc.C) {
    79  	offerUUID := s.assertAddOffer(c, permission.ConsumeAccess)
    80  	users, err := s.State.GetOfferUsers(offerUUID)
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	c.Assert(users, jc.DeepEquals, map[string]permission.Access{
    83  		"everyone@external": permission.ReadAccess,
    84  		"test-admin":        permission.AdminAccess,
    85  		"validusername":     permission.ConsumeAccess,
    86  	})
    87  }
    88  
    89  func (s *ApplicationOfferUserSuite) TestAddAdminModelUser(c *gc.C) {
    90  	s.assertAddOffer(c, permission.AdminAccess)
    91  }
    92  
    93  func (s *ApplicationOfferUserSuite) TestUpdateOfferAccess(c *gc.C) {
    94  	offer, user := s.makeOffer(c, permission.AdminAccess)
    95  	err := s.State.UpdateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user, permission.ReadAccess)
    96  	c.Assert(err, jc.ErrorIsNil)
    97  
    98  	access, err := s.State.GetOfferAccess(offer.OfferUUID, user)
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	c.Assert(access, gc.Equals, permission.ReadAccess)
   101  }
   102  
   103  func (s *ApplicationOfferUserSuite) setupOfferRelation(c *gc.C, offerUUID, user string) *state.Relation {
   104  	// Make a relation to the offer.
   105  	wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   106  	wordpressEP, err := wordpress.Endpoint("db")
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	mysql, err := s.State.Application("mysql")
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	mysqlEP, err := mysql.Endpoint("server")
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	rel, err := s.State.AddRelation(wordpressEP, mysqlEP)
   113  	c.Assert(err, jc.ErrorIsNil)
   114  	_, err = s.State.AddOfferConnection(state.AddOfferConnectionParams{
   115  		SourceModelUUID: utils.MustNewUUID().String(),
   116  		OfferUUID:       offerUUID,
   117  		RelationKey:     rel.Tag().Id(),
   118  		RelationId:      rel.Id(),
   119  		Username:        user,
   120  	})
   121  	c.Assert(err, jc.ErrorIsNil)
   122  	return rel
   123  }
   124  
   125  func (s *ApplicationOfferUserSuite) TestUpdateOfferAccessSetsRelationSuspended(c *gc.C) {
   126  	offer, user := s.makeOffer(c, permission.ConsumeAccess)
   127  	rel := s.setupOfferRelation(c, offer.OfferUUID, user.Name())
   128  
   129  	// Downgrade consume access and check the relation is suspended.
   130  	err := s.State.UpdateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user, permission.ReadAccess)
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	err = rel.Refresh()
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	c.Assert(rel.Suspended(), jc.IsTrue)
   135  }
   136  
   137  func (s *ApplicationOfferUserSuite) TestUpdateOfferAccessSetsRelationSuspendedRace(c *gc.C) {
   138  	offer, user := s.makeOffer(c, permission.ConsumeAccess)
   139  	rel := s.setupOfferRelation(c, offer.OfferUUID, user.Name())
   140  	var rel2 *state.Relation
   141  
   142  	defer state.SetBeforeHooks(c, s.State, func() {
   143  		// Add another relation to the offered app.
   144  		curl := "local:quantal/quantal-wordpress-3"
   145  		wpch, err := s.State.Charm(curl)
   146  		c.Assert(err, jc.ErrorIsNil)
   147  		wordpress2 := s.AddTestingApplication(c, "wordpress2", wpch)
   148  		wordpressEP, err := wordpress2.Endpoint("db")
   149  		c.Assert(err, jc.ErrorIsNil)
   150  		mysql, err := s.State.Application("mysql")
   151  		c.Assert(err, jc.ErrorIsNil)
   152  		mysqlEP, err := mysql.Endpoint("server")
   153  		c.Assert(err, jc.ErrorIsNil)
   154  		rel2, err = s.State.AddRelation(wordpressEP, mysqlEP)
   155  		c.Assert(err, jc.ErrorIsNil)
   156  		_, err = s.State.AddOfferConnection(state.AddOfferConnectionParams{
   157  			SourceModelUUID: utils.MustNewUUID().String(),
   158  			OfferUUID:       offer.OfferUUID,
   159  			RelationKey:     rel2.Tag().Id(),
   160  			RelationId:      rel2.Id(),
   161  			Username:        user.Name(),
   162  		})
   163  		c.Assert(err, jc.ErrorIsNil)
   164  	}).Check()
   165  
   166  	// Downgrade consume access and check both relations are suspended.
   167  	err := s.State.UpdateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user, permission.ReadAccess)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	err = rel.Refresh()
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	c.Assert(rel.Suspended(), jc.IsTrue)
   172  	err = rel2.Refresh()
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	c.Assert(rel2.Suspended(), jc.IsTrue)
   175  }
   176  
   177  func (s *ApplicationOfferUserSuite) TestCreateOfferAccessNoUserFails(c *gc.C) {
   178  	app := s.Factory.MakeApplication(c, nil)
   179  	offers := state.NewApplicationOffers(s.State)
   180  	_, err := offers.AddOffer(crossmodel.AddApplicationOfferArgs{
   181  		OfferName:       "someoffer",
   182  		ApplicationName: app.Name(),
   183  		Owner:           "test-admin",
   184  	})
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	err = s.State.CreateOfferAccess(
   187  		names.NewApplicationOfferTag("f47ac10b-58cc-4372-a567-0e02b2c3d479"),
   188  		names.NewUserTag("validusername"), permission.ReadAccess)
   189  	c.Assert(err, gc.ErrorMatches, `user "validusername" does not exist locally: user "validusername" not found`)
   190  }
   191  
   192  func (s *ApplicationOfferUserSuite) TestRemoveOfferAccess(c *gc.C) {
   193  	offer, user := s.makeOffer(c, permission.ConsumeAccess)
   194  
   195  	err := s.State.RemoveOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user)
   196  	c.Assert(err, jc.ErrorIsNil)
   197  
   198  	_, err = s.State.GetOfferAccess(offer.OfferUUID, user)
   199  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   200  }
   201  
   202  func (s *ApplicationOfferUserSuite) TestRemoveOfferAccessNoUser(c *gc.C) {
   203  	offer, _ := s.makeOffer(c, permission.ConsumeAccess)
   204  	err := s.State.RemoveOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), names.NewUserTag("fred"))
   205  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   206  }
   207  
   208  func (s *ApplicationOfferUserSuite) TestRemoveOfferAccessSetsRelationSuspended(c *gc.C) {
   209  	offer, user := s.makeOffer(c, permission.ConsumeAccess)
   210  	rel := s.setupOfferRelation(c, offer.OfferUUID, user.Name())
   211  
   212  	// Remove any access and check the relation is suspended.
   213  	err := s.State.RemoveOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user)
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	err = rel.Refresh()
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	c.Assert(rel.Suspended(), jc.IsTrue)
   218  }