github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 }