github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/auth/auth_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2019 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package auth_test
    21  
    22  import (
    23  	"context"
    24  	"testing"
    25  
    26  	. "gopkg.in/check.v1"
    27  	"gopkg.in/macaroon.v1"
    28  
    29  	"github.com/snapcore/snapd/overlord/auth"
    30  	"github.com/snapcore/snapd/overlord/state"
    31  )
    32  
    33  // Hook up gocheck into the "go test" runner.
    34  func Test(t *testing.T) { TestingT(t) }
    35  
    36  type authSuite struct {
    37  	state *state.State
    38  }
    39  
    40  var _ = Suite(&authSuite{})
    41  
    42  func (as *authSuite) SetUpTest(c *C) {
    43  	as.state = state.New(nil)
    44  }
    45  
    46  func (s *authSuite) TestMacaroonSerialize(c *C) {
    47  	m, err := macaroon.New([]byte("secret"), "some-id", "location")
    48  	c.Check(err, IsNil)
    49  
    50  	serialized, err := auth.MacaroonSerialize(m)
    51  	c.Check(err, IsNil)
    52  
    53  	deserialized, err := auth.MacaroonDeserialize(serialized)
    54  	c.Check(err, IsNil)
    55  	c.Check(deserialized, DeepEquals, m)
    56  }
    57  
    58  func (s *authSuite) TestMacaroonSerializeDeserializeStoreMacaroon(c *C) {
    59  	// sample serialized macaroon using store server setup.
    60  	serialized := `MDAxNmxvY2F0aW9uIGxvY2F0aW9uCjAwMTdpZGVudGlmaWVyIHNvbWUgaWQKMDAwZmNpZCBjYXZlYXQKMDAxOWNpZCAzcmQgcGFydHkgY2F2ZWF0CjAwNTF2aWQgcyvpXSVlMnj9wYw5b-WPCLjTnO_8lVzBrRr8tJfu9tOhPORbsEOFyBwPOM_YiiXJ_qh-Pp8HY0HsUueCUY4dxONLIxPWTdMzCjAwMTJjbCByZW1vdGUuY29tCjAwMmZzaWduYXR1cmUgcm_Gdz75wUCWF9KGXZQEANhwfvBcLNt9xXGfAmxurPMK`
    61  
    62  	deserialized, err := auth.MacaroonDeserialize(serialized)
    63  	c.Check(err, IsNil)
    64  
    65  	// expected json serialization of the above macaroon
    66  	jsonData := []byte(`{"caveats":[{"cid":"caveat"},{"cid":"3rd party caveat","vid":"cyvpXSVlMnj9wYw5b-WPCLjTnO_8lVzBrRr8tJfu9tOhPORbsEOFyBwPOM_YiiXJ_qh-Pp8HY0HsUueCUY4dxONLIxPWTdMz","cl":"remote.com"}],"location":"location","identifier":"some id","signature":"726fc6773ef9c1409617d2865d940400d8707ef05c2cdb7dc5719f026c6eacf3"}`)
    67  
    68  	var expected macaroon.Macaroon
    69  	err = expected.UnmarshalJSON(jsonData)
    70  	c.Check(err, IsNil)
    71  	c.Check(deserialized, DeepEquals, &expected)
    72  
    73  	// reserializing the macaroon should give us the same original store serialization
    74  	reserialized, err := auth.MacaroonSerialize(deserialized)
    75  	c.Check(err, IsNil)
    76  	c.Check(reserialized, Equals, serialized)
    77  }
    78  
    79  func (s *authSuite) TestMacaroonDeserializeInvalidData(c *C) {
    80  	serialized := "invalid-macaroon-data"
    81  
    82  	deserialized, err := auth.MacaroonDeserialize(serialized)
    83  	c.Check(deserialized, IsNil)
    84  	c.Check(err, NotNil)
    85  }
    86  
    87  func (as *authSuite) TestNewUser(c *C) {
    88  	as.state.Lock()
    89  	user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
    90  	as.state.Unlock()
    91  	c.Check(err, IsNil)
    92  
    93  	// check snapd macaroon was generated for the local user
    94  	var authStateData auth.AuthState
    95  	as.state.Lock()
    96  	err = as.state.Get("auth", &authStateData)
    97  	as.state.Unlock()
    98  	c.Check(err, IsNil)
    99  	c.Check(authStateData.MacaroonKey, NotNil)
   100  	expectedMacaroon, err := macaroon.New(authStateData.MacaroonKey, "1", "snapd")
   101  	c.Check(err, IsNil)
   102  	expectedSerializedMacaroon, err := auth.MacaroonSerialize(expectedMacaroon)
   103  	c.Check(err, IsNil)
   104  
   105  	expected := &auth.UserState{
   106  		ID:              1,
   107  		Username:        "username",
   108  		Email:           "email@test.com",
   109  		Macaroon:        expectedSerializedMacaroon,
   110  		Discharges:      nil,
   111  		StoreMacaroon:   "macaroon",
   112  		StoreDischarges: []string{"discharge"},
   113  	}
   114  	c.Check(user, DeepEquals, expected)
   115  }
   116  
   117  func (as *authSuite) TestNewUserSortsDischarges(c *C) {
   118  	as.state.Lock()
   119  	user, err := auth.NewUser(as.state, "", "email@test.com", "macaroon", []string{"discharge2", "discharge1"})
   120  	c.Assert(err, IsNil)
   121  	as.state.Unlock()
   122  
   123  	expected := []string{"discharge1", "discharge2"}
   124  	c.Check(user.StoreDischarges, DeepEquals, expected)
   125  
   126  	as.state.Lock()
   127  	userFromState, err := auth.User(as.state, 1)
   128  	as.state.Unlock()
   129  	c.Check(err, IsNil)
   130  	c.Check(userFromState.StoreDischarges, DeepEquals, expected)
   131  }
   132  
   133  func (as *authSuite) TestNewUserAddsToExistent(c *C) {
   134  	as.state.Lock()
   135  	firstUser, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   136  	as.state.Unlock()
   137  	c.Check(err, IsNil)
   138  
   139  	// adding a new one
   140  	as.state.Lock()
   141  	user, err := auth.NewUser(as.state, "new_username", "new_email@test.com", "new_macaroon", []string{"new_discharge"})
   142  	as.state.Unlock()
   143  	c.Check(err, IsNil)
   144  	c.Check(user.ID, Equals, 2)
   145  	c.Check(user.Username, Equals, "new_username")
   146  	c.Check(user.Email, Equals, "new_email@test.com")
   147  
   148  	as.state.Lock()
   149  	userFromState, err := auth.User(as.state, 2)
   150  	as.state.Unlock()
   151  	c.Check(err, IsNil)
   152  	c.Check(userFromState.ID, Equals, 2)
   153  	c.Check(userFromState.Username, Equals, "new_username")
   154  	c.Check(userFromState.Email, Equals, "new_email@test.com")
   155  
   156  	// first user is still in the state
   157  	as.state.Lock()
   158  	userFromState, err = auth.User(as.state, 1)
   159  	as.state.Unlock()
   160  	c.Check(err, IsNil)
   161  	c.Check(userFromState, DeepEquals, firstUser)
   162  }
   163  
   164  func (as *authSuite) TestCheckMacaroonNoAuthData(c *C) {
   165  	as.state.Lock()
   166  	user, err := auth.CheckMacaroon(as.state, "macaroon", []string{"discharge"})
   167  	as.state.Unlock()
   168  
   169  	c.Check(err, Equals, auth.ErrInvalidAuth)
   170  	c.Check(user, IsNil)
   171  }
   172  
   173  func (as *authSuite) TestCheckMacaroonInvalidAuth(c *C) {
   174  	as.state.Lock()
   175  	user, err := auth.CheckMacaroon(as.state, "other-macaroon", []string{"discharge"})
   176  	as.state.Unlock()
   177  
   178  	c.Check(err, Equals, auth.ErrInvalidAuth)
   179  	c.Check(user, IsNil)
   180  
   181  	as.state.Lock()
   182  	_, err = auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   183  	as.state.Unlock()
   184  	c.Check(err, IsNil)
   185  
   186  	as.state.Lock()
   187  	user, err = auth.CheckMacaroon(as.state, "other-macaroon", []string{"discharge"})
   188  	as.state.Unlock()
   189  
   190  	c.Check(err, Equals, auth.ErrInvalidAuth)
   191  	c.Check(user, IsNil)
   192  }
   193  
   194  func (as *authSuite) TestCheckMacaroonValidUser(c *C) {
   195  	as.state.Lock()
   196  	expectedUser, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   197  	as.state.Unlock()
   198  	c.Check(err, IsNil)
   199  
   200  	as.state.Lock()
   201  	user, err := auth.CheckMacaroon(as.state, expectedUser.Macaroon, expectedUser.Discharges)
   202  	as.state.Unlock()
   203  
   204  	c.Check(err, IsNil)
   205  	c.Check(user, DeepEquals, expectedUser)
   206  }
   207  
   208  func (as *authSuite) TestCheckMacaroonValidUserOldStyle(c *C) {
   209  	// create a fake store-deserializable macaroon
   210  	m, err := macaroon.New([]byte("secret"), "some-id", "location")
   211  	c.Check(err, IsNil)
   212  	serializedMacaroon, err := auth.MacaroonSerialize(m)
   213  	c.Check(err, IsNil)
   214  
   215  	as.state.Lock()
   216  	expectedUser, err := auth.NewUser(as.state, "username", "email@test.com", serializedMacaroon, []string{"discharge"})
   217  	c.Check(err, IsNil)
   218  	// set user local macaroons with store macaroons
   219  	expectedUser.Macaroon = expectedUser.StoreMacaroon
   220  	expectedUser.Discharges = expectedUser.StoreDischarges
   221  	err = auth.UpdateUser(as.state, expectedUser)
   222  	c.Check(err, IsNil)
   223  	as.state.Unlock()
   224  
   225  	as.state.Lock()
   226  	user, err := auth.CheckMacaroon(as.state, expectedUser.Macaroon, expectedUser.Discharges)
   227  	as.state.Unlock()
   228  
   229  	c.Check(err, IsNil)
   230  	c.Check(user, DeepEquals, expectedUser)
   231  }
   232  
   233  func (as *authSuite) TestCheckMacaroonInvalidAuthMalformedMacaroon(c *C) {
   234  	var authStateData auth.AuthState
   235  	as.state.Lock()
   236  	// create a new user to ensure there is a MacaroonKey setup
   237  	_, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   238  	c.Check(err, IsNil)
   239  	// get AuthState to get signing MacaroonKey
   240  	err = as.state.Get("auth", &authStateData)
   241  	c.Check(err, IsNil)
   242  	as.state.Unlock()
   243  
   244  	// setup a macaroon for an invalid user
   245  	invalidMacaroon, err := macaroon.New(authStateData.MacaroonKey, "invalid", "snapd")
   246  	c.Check(err, IsNil)
   247  	serializedInvalidMacaroon, err := auth.MacaroonSerialize(invalidMacaroon)
   248  	c.Check(err, IsNil)
   249  
   250  	as.state.Lock()
   251  	user, err := auth.CheckMacaroon(as.state, serializedInvalidMacaroon, nil)
   252  	as.state.Unlock()
   253  
   254  	c.Check(err, Equals, auth.ErrInvalidAuth)
   255  	c.Check(user, IsNil)
   256  }
   257  
   258  func (as *authSuite) TestUserForNoAuthInState(c *C) {
   259  	as.state.Lock()
   260  	userFromState, err := auth.User(as.state, 42)
   261  	as.state.Unlock()
   262  	c.Check(err, Equals, auth.ErrInvalidUser)
   263  	c.Check(userFromState, IsNil)
   264  }
   265  
   266  func (as *authSuite) TestUserForNonExistent(c *C) {
   267  	as.state.Lock()
   268  	_, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   269  	as.state.Unlock()
   270  	c.Check(err, IsNil)
   271  
   272  	as.state.Lock()
   273  	userFromState, err := auth.User(as.state, 42)
   274  	c.Check(err, Equals, auth.ErrInvalidUser)
   275  	c.Check(err, ErrorMatches, "invalid user")
   276  	c.Check(userFromState, IsNil)
   277  }
   278  
   279  func (as *authSuite) TestUser(c *C) {
   280  	as.state.Lock()
   281  	user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   282  	as.state.Unlock()
   283  	c.Check(err, IsNil)
   284  
   285  	as.state.Lock()
   286  	userFromState, err := auth.User(as.state, 1)
   287  	as.state.Unlock()
   288  	c.Check(err, IsNil)
   289  	c.Check(userFromState, DeepEquals, user)
   290  
   291  	c.Check(user.HasStoreAuth(), Equals, true)
   292  }
   293  
   294  func (as *authSuite) TestUserByUsername(c *C) {
   295  	as.state.Lock()
   296  	user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   297  	as.state.Unlock()
   298  	c.Check(err, IsNil)
   299  
   300  	as.state.Lock()
   301  	userFromState, err := auth.UserByUsername(as.state, "username")
   302  	as.state.Unlock()
   303  	c.Check(err, IsNil)
   304  	c.Check(userFromState, DeepEquals, user)
   305  
   306  	as.state.Lock()
   307  	_, err = auth.UserByUsername(as.state, "otherusername")
   308  	as.state.Unlock()
   309  	c.Check(err, Equals, auth.ErrInvalidUser)
   310  }
   311  
   312  func (as *authSuite) TestUserHasStoreAuth(c *C) {
   313  	var user0 *auth.UserState
   314  	// nil user
   315  	c.Check(user0.HasStoreAuth(), Equals, false)
   316  
   317  	as.state.Lock()
   318  	user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   319  	as.state.Unlock()
   320  	c.Check(err, IsNil)
   321  	c.Check(user.HasStoreAuth(), Equals, true)
   322  
   323  	// no store auth
   324  	as.state.Lock()
   325  	user, err = auth.NewUser(as.state, "username", "email@test.com", "", nil)
   326  	as.state.Unlock()
   327  	c.Check(err, IsNil)
   328  	c.Check(user.HasStoreAuth(), Equals, false)
   329  }
   330  
   331  func (as *authSuite) TestUpdateUser(c *C) {
   332  	as.state.Lock()
   333  	user, _ := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   334  	as.state.Unlock()
   335  
   336  	user.Username = "different"
   337  	user.StoreDischarges = []string{"updated-discharge"}
   338  
   339  	as.state.Lock()
   340  	err := auth.UpdateUser(as.state, user)
   341  	as.state.Unlock()
   342  	c.Check(err, IsNil)
   343  
   344  	as.state.Lock()
   345  	userFromState, err := auth.User(as.state, user.ID)
   346  	as.state.Unlock()
   347  	c.Check(err, IsNil)
   348  	c.Check(userFromState, DeepEquals, user)
   349  }
   350  
   351  func (as *authSuite) TestUpdateUserInvalid(c *C) {
   352  	as.state.Lock()
   353  	_, _ = auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   354  	as.state.Unlock()
   355  
   356  	user := &auth.UserState{
   357  		ID:       102,
   358  		Username: "username",
   359  		Macaroon: "macaroon",
   360  	}
   361  
   362  	as.state.Lock()
   363  	err := auth.UpdateUser(as.state, user)
   364  	as.state.Unlock()
   365  	c.Assert(err, Equals, auth.ErrInvalidUser)
   366  }
   367  
   368  func (as *authSuite) TestRemove(c *C) {
   369  	as.state.Lock()
   370  	user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   371  	as.state.Unlock()
   372  	c.Check(err, IsNil)
   373  
   374  	as.state.Lock()
   375  	_, err = auth.User(as.state, user.ID)
   376  	as.state.Unlock()
   377  	c.Check(err, IsNil)
   378  
   379  	as.state.Lock()
   380  	u, err := auth.RemoveUser(as.state, user.ID)
   381  	as.state.Unlock()
   382  	c.Assert(err, IsNil)
   383  	c.Check(u, DeepEquals, &auth.UserState{
   384  		ID:       1,
   385  		Username: "username",
   386  		Email:    "email@test.com",
   387  	})
   388  
   389  	as.state.Lock()
   390  	_, err = auth.User(as.state, user.ID)
   391  	as.state.Unlock()
   392  	c.Check(err, Equals, auth.ErrInvalidUser)
   393  
   394  	as.state.Lock()
   395  	_, err = auth.RemoveUser(as.state, user.ID)
   396  	as.state.Unlock()
   397  	c.Assert(err, Equals, auth.ErrInvalidUser)
   398  }
   399  
   400  func (as *authSuite) TestRemoveByUsername(c *C) {
   401  	as.state.Lock()
   402  	user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   403  	as.state.Unlock()
   404  	c.Check(err, IsNil)
   405  
   406  	as.state.Lock()
   407  	_, err = auth.User(as.state, user.ID)
   408  	as.state.Unlock()
   409  	c.Check(err, IsNil)
   410  
   411  	as.state.Lock()
   412  	u, err := auth.RemoveUserByUsername(as.state, user.Username)
   413  	as.state.Unlock()
   414  	c.Assert(err, IsNil)
   415  	c.Check(u, DeepEquals, &auth.UserState{
   416  		ID:       1,
   417  		Username: "username",
   418  		Email:    "email@test.com",
   419  	})
   420  
   421  	as.state.Lock()
   422  	_, err = auth.User(as.state, user.ID)
   423  	as.state.Unlock()
   424  	c.Check(err, Equals, auth.ErrInvalidUser)
   425  
   426  	as.state.Lock()
   427  	_, err = auth.RemoveUserByUsername(as.state, user.Username)
   428  	as.state.Unlock()
   429  	c.Assert(err, Equals, auth.ErrInvalidUser)
   430  }
   431  
   432  func (as *authSuite) TestUsers(c *C) {
   433  	as.state.Lock()
   434  	user1, err1 := auth.NewUser(as.state, "user1", "email1@test.com", "macaroon", []string{"discharge"})
   435  	user2, err2 := auth.NewUser(as.state, "user2", "email2@test.com", "macaroon", []string{"discharge"})
   436  	as.state.Unlock()
   437  	c.Check(err1, IsNil)
   438  	c.Check(err2, IsNil)
   439  
   440  	as.state.Lock()
   441  	users, err := auth.Users(as.state)
   442  	as.state.Unlock()
   443  	c.Check(err, IsNil)
   444  	c.Check(users, DeepEquals, []*auth.UserState{user1, user2})
   445  }
   446  
   447  func (as *authSuite) TestEnsureContexts(c *C) {
   448  	ctx1 := auth.EnsureContextTODO()
   449  	ctx2 := auth.EnsureContextTODO()
   450  
   451  	c.Check(ctx1, Not(Equals), ctx2)
   452  
   453  	c.Check(auth.IsEnsureContext(ctx1), Equals, true)
   454  	c.Check(auth.IsEnsureContext(ctx2), Equals, true)
   455  
   456  	c.Check(auth.IsEnsureContext(context.TODO()), Equals, false)
   457  }