github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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) TestUserHasStoreAuth(c *C) { 295 var user0 *auth.UserState 296 // nil user 297 c.Check(user0.HasStoreAuth(), Equals, false) 298 299 as.state.Lock() 300 user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 301 as.state.Unlock() 302 c.Check(err, IsNil) 303 c.Check(user.HasStoreAuth(), Equals, true) 304 305 // no store auth 306 as.state.Lock() 307 user, err = auth.NewUser(as.state, "username", "email@test.com", "", nil) 308 as.state.Unlock() 309 c.Check(err, IsNil) 310 c.Check(user.HasStoreAuth(), Equals, false) 311 } 312 313 func (as *authSuite) TestUpdateUser(c *C) { 314 as.state.Lock() 315 user, _ := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 316 as.state.Unlock() 317 318 user.Username = "different" 319 user.StoreDischarges = []string{"updated-discharge"} 320 321 as.state.Lock() 322 err := auth.UpdateUser(as.state, user) 323 as.state.Unlock() 324 c.Check(err, IsNil) 325 326 as.state.Lock() 327 userFromState, err := auth.User(as.state, user.ID) 328 as.state.Unlock() 329 c.Check(err, IsNil) 330 c.Check(userFromState, DeepEquals, user) 331 } 332 333 func (as *authSuite) TestUpdateUserInvalid(c *C) { 334 as.state.Lock() 335 _, _ = auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 336 as.state.Unlock() 337 338 user := &auth.UserState{ 339 ID: 102, 340 Username: "username", 341 Macaroon: "macaroon", 342 } 343 344 as.state.Lock() 345 err := auth.UpdateUser(as.state, user) 346 as.state.Unlock() 347 c.Assert(err, Equals, auth.ErrInvalidUser) 348 } 349 350 func (as *authSuite) TestRemove(c *C) { 351 as.state.Lock() 352 user, err := auth.NewUser(as.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 353 as.state.Unlock() 354 c.Check(err, IsNil) 355 356 as.state.Lock() 357 _, err = auth.User(as.state, user.ID) 358 as.state.Unlock() 359 c.Check(err, IsNil) 360 361 as.state.Lock() 362 err = auth.RemoveUser(as.state, user.ID) 363 as.state.Unlock() 364 c.Assert(err, IsNil) 365 366 as.state.Lock() 367 _, err = auth.User(as.state, user.ID) 368 as.state.Unlock() 369 c.Check(err, Equals, auth.ErrInvalidUser) 370 371 as.state.Lock() 372 err = auth.RemoveUser(as.state, user.ID) 373 as.state.Unlock() 374 c.Assert(err, Equals, auth.ErrInvalidUser) 375 } 376 377 func (as *authSuite) TestUsers(c *C) { 378 as.state.Lock() 379 user1, err1 := auth.NewUser(as.state, "user1", "email1@test.com", "macaroon", []string{"discharge"}) 380 user2, err2 := auth.NewUser(as.state, "user2", "email2@test.com", "macaroon", []string{"discharge"}) 381 as.state.Unlock() 382 c.Check(err1, IsNil) 383 c.Check(err2, IsNil) 384 385 as.state.Lock() 386 users, err := auth.Users(as.state) 387 as.state.Unlock() 388 c.Check(err, IsNil) 389 c.Check(users, DeepEquals, []*auth.UserState{user1, user2}) 390 } 391 392 func (as *authSuite) TestEnsureContexts(c *C) { 393 ctx1 := auth.EnsureContextTODO() 394 ctx2 := auth.EnsureContextTODO() 395 396 c.Check(ctx1, Not(Equals), ctx2) 397 398 c.Check(auth.IsEnsureContext(ctx1), Equals, true) 399 c.Check(auth.IsEnsureContext(ctx2), Equals, true) 400 401 c.Check(auth.IsEnsureContext(context.TODO()), Equals, false) 402 }