github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/daemon/api_users_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2018 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 daemon 21 22 import ( 23 "bytes" 24 "fmt" 25 "net/http" 26 "os/user" 27 "path/filepath" 28 "time" 29 30 "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/asserts" 33 "github.com/snapcore/snapd/asserts/assertstest" 34 "github.com/snapcore/snapd/osutil" 35 "github.com/snapcore/snapd/overlord/assertstate/assertstatetest" 36 "github.com/snapcore/snapd/overlord/auth" 37 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 38 "github.com/snapcore/snapd/release" 39 "github.com/snapcore/snapd/store" 40 "github.com/snapcore/snapd/testutil" 41 ) 42 43 var _ = check.Suite(&userSuite{}) 44 45 // TODO: also pull login/logout tests into this. 46 // TODO: move to daemon_test package 47 48 type userSuite struct { 49 apiBaseSuite 50 51 mockUserHome string 52 restoreClassic func() 53 oldUserAdmin bool 54 } 55 56 func (s *userSuite) SetUpTest(c *check.C) { 57 s.apiBaseSuite.SetUpTest(c) 58 59 s.restoreClassic = release.MockOnClassic(false) 60 61 s.daemon(c) 62 s.mockUserHome = c.MkDir() 63 userLookup = mkUserLookup(s.mockUserHome) 64 s.oldUserAdmin = hasUserAdmin 65 hasUserAdmin = true 66 67 // make sure we don't call these by accident 68 osutilAddUser = func(name string, opts *osutil.AddUserOptions) error { 69 c.Fatalf("unexpected add user %q call", name) 70 return fmt.Errorf("unexpected add user %q call", name) 71 } 72 osutilDelUser = func(name string, opts *osutil.DelUserOptions) error { 73 c.Fatalf("unexpected del user %q call", name) 74 return fmt.Errorf("unexpected del user %q call", name) 75 } 76 } 77 78 func (s *userSuite) TearDownTest(c *check.C) { 79 s.apiBaseSuite.TearDownTest(c) 80 81 userLookup = user.Lookup 82 osutilAddUser = osutil.AddUser 83 osutilDelUser = osutil.DelUser 84 85 s.restoreClassic() 86 hasUserAdmin = s.oldUserAdmin 87 } 88 89 func mkUserLookup(userHomeDir string) func(string) (*user.User, error) { 90 return func(username string) (*user.User, error) { 91 cur, err := user.Current() 92 cur.Username = username 93 cur.HomeDir = userHomeDir 94 return cur, err 95 } 96 } 97 98 func (s *userSuite) TestPostCreateUserNoSSHKeys(c *check.C) { 99 s.userInfoExpectedEmail = "popper@lse.ac.uk" 100 s.userInfoResult = &store.User{ 101 Username: "karl", 102 OpenIDIdentifier: "xxyyzz", 103 } 104 buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail)) 105 req, err := http.NewRequest("POST", "/v2/create-user", buf) 106 c.Assert(err, check.IsNil) 107 108 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 109 110 c.Check(rsp.Type, check.Equals, ResponseTypeError) 111 c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user for "popper@lse.ac.uk": no ssh keys found`) 112 } 113 114 func (s *userSuite) TestPostCreateUser(c *check.C) { 115 s.testCreateUser(c, true) 116 } 117 118 func (s *userSuite) TestPostUserCreate(c *check.C) { 119 s.testCreateUser(c, false) 120 } 121 122 func (s *userSuite) testCreateUser(c *check.C, oldWay bool) { 123 expectedUsername := "karl" 124 s.userInfoExpectedEmail = "popper@lse.ac.uk" 125 s.userInfoResult = &store.User{ 126 Username: expectedUsername, 127 SSHKeys: []string{"ssh1", "ssh2"}, 128 OpenIDIdentifier: "xxyyzz", 129 } 130 osutilAddUser = func(username string, opts *osutil.AddUserOptions) error { 131 c.Check(username, check.Equals, expectedUsername) 132 c.Check(opts.SSHKeys, check.DeepEquals, []string{"ssh1", "ssh2"}) 133 c.Check(opts.Gecos, check.Equals, "popper@lse.ac.uk,xxyyzz") 134 c.Check(opts.Sudoer, check.Equals, false) 135 return nil 136 } 137 138 var rsp *resp 139 var expected interface{} 140 expectedItem := userResponseData{ 141 Username: expectedUsername, 142 SSHKeys: []string{"ssh1", "ssh2"}, 143 } 144 145 if oldWay { 146 buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail)) 147 req, err := http.NewRequest("POST", "/v2/create-user", buf) 148 c.Assert(err, check.IsNil) 149 150 rsp = postCreateUser(createUserCmd, req, nil).(*resp) 151 expected = &expectedItem 152 } else { 153 buf := bytes.NewBufferString(fmt.Sprintf(`{"action":"create","email": "%s"}`, s.userInfoExpectedEmail)) 154 req, err := http.NewRequest("POST", "/v2/users", buf) 155 c.Assert(err, check.IsNil) 156 157 rsp = postUsers(usersCmd, req, nil).(*resp) 158 expected = []userResponseData{expectedItem} 159 } 160 161 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 162 c.Check(rsp.Result, check.FitsTypeOf, expected) 163 c.Check(rsp.Result, check.DeepEquals, expected) 164 165 // user was setup in state 166 state := s.d.overlord.State() 167 state.Lock() 168 user, err := auth.User(state, 1) 169 state.Unlock() 170 c.Check(err, check.IsNil) 171 c.Check(user.Username, check.Equals, expectedUsername) 172 c.Check(user.Email, check.Equals, s.userInfoExpectedEmail) 173 c.Check(user.Macaroon, check.NotNil) 174 // auth saved to user home dir 175 outfile := filepath.Join(s.mockUserHome, ".snap", "auth.json") 176 c.Check(osutil.FileExists(outfile), check.Equals, true) 177 c.Check(outfile, testutil.FileEquals, 178 fmt.Sprintf(`{"id":%d,"username":"%s","email":"%s","macaroon":"%s"}`, 179 1, expectedUsername, s.userInfoExpectedEmail, user.Macaroon)) 180 } 181 182 func (s *userSuite) TestNoUserAdminCreateUser(c *check.C) { s.testNoUserAdmin(c, "/v2/create-user") } 183 func (s *userSuite) TestNoUserAdminPostUser(c *check.C) { s.testNoUserAdmin(c, "/v2/users") } 184 func (s *userSuite) testNoUserAdmin(c *check.C, endpoint string) { 185 hasUserAdmin = false 186 187 buf := bytes.NewBufferString("{}") 188 req, err := http.NewRequest("POST", endpoint, buf) 189 c.Assert(err, check.IsNil) 190 191 switch endpoint { 192 case "/v2/users": 193 rsp := postUsers(usersCmd, req, nil).(*resp) 194 c.Check(rsp, check.DeepEquals, MethodNotAllowed(noUserAdmin)) 195 case "/v2/create-user": 196 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 197 c.Check(rsp, check.DeepEquals, Forbidden(noUserAdmin)) 198 default: 199 c.Fatalf("unknown endpoint %q", endpoint) 200 } 201 } 202 203 func (s *userSuite) TestPostUserBadBody(c *check.C) { 204 buf := bytes.NewBufferString(`42`) 205 req, err := http.NewRequest("POST", "/v2/users", buf) 206 c.Assert(err, check.IsNil) 207 208 rsp := postUsers(usersCmd, req, nil).(*resp) 209 c.Check(rsp.Type, check.Equals, ResponseTypeError) 210 c.Check(rsp.Result.(*errorResult).Message, check.Matches, "cannot decode user action data from request body: .*") 211 } 212 213 func (s *userSuite) TestPostUserBadAfterBody(c *check.C) { 214 buf := bytes.NewBufferString(`{}42`) 215 req, err := http.NewRequest("POST", "/v2/users", buf) 216 c.Assert(err, check.IsNil) 217 218 rsp := postUsers(usersCmd, req, nil).(*resp) 219 c.Check(rsp, check.DeepEquals, BadRequest("spurious content after user action")) 220 } 221 222 func (s *userSuite) TestPostUserNoAction(c *check.C) { 223 buf := bytes.NewBufferString("{}") 224 req, err := http.NewRequest("POST", "/v2/users", buf) 225 c.Assert(err, check.IsNil) 226 227 rsp := postUsers(usersCmd, req, nil).(*resp) 228 c.Check(rsp, check.DeepEquals, BadRequest("missing user action")) 229 } 230 231 func (s *userSuite) TestPostUserBadAction(c *check.C) { 232 buf := bytes.NewBufferString(`{"action":"patatas"}`) 233 req, err := http.NewRequest("POST", "/v2/users", buf) 234 c.Assert(err, check.IsNil) 235 236 rsp := postUsers(usersCmd, req, nil).(*resp) 237 c.Check(rsp, check.DeepEquals, BadRequest(`unsupported user action "patatas"`)) 238 } 239 240 func (s *userSuite) TestPostUserActionRemoveNoUsername(c *check.C) { 241 buf := bytes.NewBufferString(`{"action":"remove"}`) 242 req, err := http.NewRequest("POST", "/v2/users", buf) 243 c.Assert(err, check.IsNil) 244 245 rsp := postUsers(usersCmd, req, nil).(*resp) 246 c.Check(rsp, check.DeepEquals, BadRequest("need a username to remove")) 247 } 248 249 func (s *userSuite) TestPostUserActionRemoveDelUserErr(c *check.C) { 250 st := s.d.overlord.State() 251 st.Lock() 252 _, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"}) 253 st.Unlock() 254 c.Check(err, check.IsNil) 255 256 called := 0 257 osutilDelUser = func(username string, opts *osutil.DelUserOptions) error { 258 called++ 259 c.Check(username, check.Equals, "some-user") 260 return fmt.Errorf("wat") 261 } 262 263 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 264 req, err := http.NewRequest("POST", "/v2/users", buf) 265 c.Assert(err, check.IsNil) 266 267 rsp := postUsers(usersCmd, req, nil).(*resp) 268 c.Check(rsp.Status, check.Equals, 500) 269 c.Check(rsp.Result.(*errorResult).Message, check.Equals, "wat") 270 c.Check(called, check.Equals, 1) 271 } 272 273 func (s *userSuite) TestPostUserActionRemoveStateErr(c *check.C) { 274 st := s.d.overlord.State() 275 st.Lock() 276 st.Set("auth", 42) // breaks auth 277 st.Unlock() 278 called := 0 279 osutilDelUser = func(username string, opts *osutil.DelUserOptions) error { 280 called++ 281 c.Check(username, check.Equals, "some-user") 282 return nil 283 } 284 285 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 286 req, err := http.NewRequest("POST", "/v2/users", buf) 287 c.Assert(err, check.IsNil) 288 289 rsp := postUsers(usersCmd, req, nil).(*resp) 290 c.Check(rsp.Status, check.Equals, 500) 291 c.Check(rsp.Result.(*errorResult).Message, check.Matches, `internal error: could not unmarshal state entry "auth": .*`) 292 c.Check(called, check.Equals, 0) 293 } 294 295 func (s *userSuite) TestPostUserActionRemoveNoUserInState(c *check.C) { 296 called := 0 297 osutilDelUser = func(username string, opts *osutil.DelUserOptions) error { 298 called++ 299 c.Check(username, check.Equals, "some-user") 300 return nil 301 } 302 303 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 304 req, err := http.NewRequest("POST", "/v2/users", buf) 305 c.Assert(err, check.IsNil) 306 307 rsp := postUsers(usersCmd, req, nil).(*resp) 308 c.Check(rsp, check.DeepEquals, BadRequest(`user "some-user" is not known`)) 309 c.Check(called, check.Equals, 0) 310 } 311 312 func (s *userSuite) TestPostUserActionRemove(c *check.C) { 313 st := s.d.overlord.State() 314 st.Lock() 315 user, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"}) 316 st.Unlock() 317 c.Check(err, check.IsNil) 318 319 called := 0 320 osutilDelUser = func(username string, opts *osutil.DelUserOptions) error { 321 called++ 322 c.Check(username, check.Equals, "some-user") 323 return nil 324 } 325 326 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 327 req, err := http.NewRequest("POST", "/v2/users", buf) 328 c.Assert(err, check.IsNil) 329 rsp := postUsers(usersCmd, req, nil).(*resp) 330 c.Check(rsp.Status, check.Equals, 200) 331 expected := []userResponseData{ 332 {ID: user.ID, Username: user.Username, Email: user.Email}, 333 } 334 c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{ 335 "removed": expected, 336 }) 337 c.Check(called, check.Equals, 1) 338 339 // and the user is removed from state 340 st.Lock() 341 _, err = auth.User(st, user.ID) 342 st.Unlock() 343 c.Check(err, check.Equals, auth.ErrInvalidUser) 344 } 345 346 func (s *userSuite) setupSigner(accountID string, signerPrivKey asserts.PrivateKey) *assertstest.SigningDB { 347 st := s.d.overlord.State() 348 349 signerSigning := s.brands.Register(accountID, signerPrivKey, map[string]interface{}{ 350 "account-id": accountID, 351 "verification": "verified", 352 }) 353 acctNKey := s.brands.AccountsAndKeys(accountID) 354 355 assertstest.AddMany(s.storeSigning, acctNKey...) 356 assertstatetest.AddMany(st, acctNKey...) 357 358 return signerSigning 359 } 360 361 var ( 362 brandPrivKey, _ = assertstest.GenerateKey(752) 363 partnerPrivKey, _ = assertstest.GenerateKey(752) 364 unknownPrivKey, _ = assertstest.GenerateKey(752) 365 ) 366 367 func (s *userSuite) makeSystemUsers(c *check.C, systemUsers []map[string]interface{}) { 368 st := s.d.overlord.State() 369 st.Lock() 370 defer st.Unlock() 371 372 assertstatetest.AddMany(st, s.storeSigning.StoreAccountKey("")) 373 374 s.setupSigner("my-brand", brandPrivKey) 375 s.setupSigner("partner", partnerPrivKey) 376 s.setupSigner("unknown", unknownPrivKey) 377 378 model := s.brands.Model("my-brand", "my-model", map[string]interface{}{ 379 "architecture": "amd64", 380 "gadget": "pc", 381 "kernel": "pc-kernel", 382 "required-snaps": []interface{}{"required-snap1"}, 383 "system-user-authority": []interface{}{"my-brand", "partner"}, 384 }) 385 // now add model related stuff to the system 386 assertstatetest.AddMany(st, model) 387 // and a serial 388 deviceKey, _ := assertstest.GenerateKey(752) 389 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 390 c.Assert(err, check.IsNil) 391 serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 392 "authority-id": "my-brand", 393 "brand-id": "my-brand", 394 "model": "my-model", 395 "serial": "serialserial", 396 "device-key": string(encDevKey), 397 "device-key-sha3-384": deviceKey.PublicKey().ID(), 398 "timestamp": time.Now().Format(time.RFC3339), 399 }, nil, "") 400 c.Assert(err, check.IsNil) 401 assertstatetest.AddMany(st, serial) 402 403 for _, suMap := range systemUsers { 404 su, err := s.brands.Signing(suMap["authority-id"].(string)).Sign(asserts.SystemUserType, suMap, nil, "") 405 c.Assert(err, check.IsNil) 406 su = su.(*asserts.SystemUser) 407 // now add system-user assertion to the system 408 assertstatetest.AddMany(st, su) 409 } 410 // create fake device 411 err = devicestatetest.SetDevice(st, &auth.DeviceState{ 412 Brand: "my-brand", 413 Model: "my-model", 414 Serial: "serialserial", 415 }) 416 c.Assert(err, check.IsNil) 417 } 418 419 var goodUser = map[string]interface{}{ 420 "authority-id": "my-brand", 421 "brand-id": "my-brand", 422 "email": "foo@bar.com", 423 "series": []interface{}{"16", "18"}, 424 "models": []interface{}{"my-model", "other-model"}, 425 "name": "Boring Guy", 426 "username": "guy", 427 "password": "$6$salt$hash", 428 "since": time.Now().Format(time.RFC3339), 429 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 430 } 431 432 var partnerUser = map[string]interface{}{ 433 "authority-id": "partner", 434 "brand-id": "my-brand", 435 "email": "p@partner.com", 436 "series": []interface{}{"16", "18"}, 437 "models": []interface{}{"my-model"}, 438 "name": "Partner Guy", 439 "username": "partnerguy", 440 "password": "$6$salt$hash", 441 "since": time.Now().Format(time.RFC3339), 442 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 443 } 444 445 var serialUser = map[string]interface{}{ 446 "format": "1", 447 "authority-id": "my-brand", 448 "brand-id": "my-brand", 449 "email": "serial@bar.com", 450 "series": []interface{}{"16", "18"}, 451 "models": []interface{}{"my-model"}, 452 "serials": []interface{}{"serialserial"}, 453 "name": "Serial Guy", 454 "username": "goodserialguy", 455 "password": "$6$salt$hash", 456 "since": time.Now().Format(time.RFC3339), 457 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 458 } 459 460 var badUser = map[string]interface{}{ 461 // bad user (not valid for this model) 462 "authority-id": "my-brand", 463 "brand-id": "my-brand", 464 "email": "foobar@bar.com", 465 "series": []interface{}{"16", "18"}, 466 "models": []interface{}{"non-of-the-models-i-have"}, 467 "name": "Random Gal", 468 "username": "gal", 469 "password": "$6$salt$hash", 470 "since": time.Now().Format(time.RFC3339), 471 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 472 } 473 474 var badUserNoMatchingSerial = map[string]interface{}{ 475 "format": "1", 476 "authority-id": "my-brand", 477 "brand-id": "my-brand", 478 "email": "noserial@bar.com", 479 "series": []interface{}{"16", "18"}, 480 "models": []interface{}{"my-model"}, 481 "serials": []interface{}{"different-serialserial"}, 482 "name": "No Serial Guy", 483 "username": "noserial", 484 "password": "$6$salt$hash", 485 "since": time.Now().Format(time.RFC3339), 486 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 487 } 488 489 var unknownUser = map[string]interface{}{ 490 "authority-id": "unknown", 491 "brand-id": "my-brand", 492 "email": "x@partner.com", 493 "series": []interface{}{"16", "18"}, 494 "models": []interface{}{"my-model"}, 495 "name": "XGuy", 496 "username": "xguy", 497 "password": "$6$salt$hash", 498 "since": time.Now().Format(time.RFC3339), 499 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 500 } 501 502 func (s *userSuite) TestGetUserDetailsFromAssertionHappy(c *check.C) { 503 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 504 505 st := s.d.overlord.State() 506 507 st.Lock() 508 model, err := s.d.overlord.DeviceManager().Model() 509 st.Unlock() 510 c.Assert(err, check.IsNil) 511 512 // ensure that if we query the details from the assert DB we get 513 // the expected user 514 username, opts, err := getUserDetailsFromAssertion(st, model, nil, "foo@bar.com") 515 c.Check(username, check.Equals, "guy") 516 c.Check(opts, check.DeepEquals, &osutil.AddUserOptions{ 517 Gecos: "foo@bar.com,Boring Guy", 518 Password: "$6$salt$hash", 519 }) 520 c.Check(err, check.IsNil) 521 } 522 523 // FIXME: These tests all look similar, with small deltas. Would be 524 // nice to transform them into a table that is just the deltas, and 525 // run on a loop. 526 func (s *userSuite) TestPostCreateUserFromAssertion(c *check.C) { 527 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 528 529 // mock the calls that create the user 530 osutilAddUser = func(username string, opts *osutil.AddUserOptions) error { 531 c.Check(username, check.Equals, "guy") 532 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 533 c.Check(opts.Sudoer, check.Equals, false) 534 c.Check(opts.Password, check.Equals, "$6$salt$hash") 535 c.Check(opts.ForcePasswordChange, check.Equals, false) 536 return nil 537 } 538 539 defer func() { 540 osutilAddUser = osutil.AddUser 541 }() 542 543 // do it! 544 buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`) 545 req, err := http.NewRequest("POST", "/v2/create-user", buf) 546 c.Assert(err, check.IsNil) 547 548 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 549 550 expected := &userResponseData{ 551 Username: "guy", 552 } 553 554 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 555 c.Check(rsp.Result, check.FitsTypeOf, expected) 556 c.Check(rsp.Result, check.DeepEquals, expected) 557 558 // ensure the user was added to the state 559 st := s.d.overlord.State() 560 st.Lock() 561 users, err := auth.Users(st) 562 c.Assert(err, check.IsNil) 563 st.Unlock() 564 c.Check(users, check.HasLen, 1) 565 } 566 567 func (s *userSuite) TestPostCreateUserFromAssertionWithForcePasswordChange(c *check.C) { 568 user := make(map[string]interface{}) 569 for k, v := range goodUser { 570 user[k] = v 571 } 572 user["force-password-change"] = "true" 573 lusers := []map[string]interface{}{user} 574 s.makeSystemUsers(c, lusers) 575 576 // mock the calls that create the user 577 osutilAddUser = func(username string, opts *osutil.AddUserOptions) error { 578 c.Check(username, check.Equals, "guy") 579 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 580 c.Check(opts.Sudoer, check.Equals, false) 581 c.Check(opts.Password, check.Equals, "$6$salt$hash") 582 c.Check(opts.ForcePasswordChange, check.Equals, true) 583 return nil 584 } 585 586 defer func() { 587 osutilAddUser = osutil.AddUser 588 }() 589 590 // do it! 591 buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`) 592 req, err := http.NewRequest("POST", "/v2/create-user", buf) 593 c.Assert(err, check.IsNil) 594 595 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 596 597 expected := &userResponseData{ 598 Username: "guy", 599 } 600 601 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 602 c.Check(rsp.Result, check.FitsTypeOf, expected) 603 c.Check(rsp.Result, check.DeepEquals, expected) 604 605 // ensure the user was added to the state 606 st := s.d.overlord.State() 607 st.Lock() 608 users, err := auth.Users(st) 609 c.Assert(err, check.IsNil) 610 st.Unlock() 611 c.Check(users, check.HasLen, 1) 612 } 613 614 func (s *userSuite) TestPostCreateUserFromAssertionAllKnown(c *check.C) { 615 s.makeSystemUsers(c, []map[string]interface{}{goodUser, partnerUser, serialUser, badUser, badUserNoMatchingSerial, unknownUser}) 616 created := map[string]bool{} 617 // mock the calls that create the user 618 osutilAddUser = func(username string, opts *osutil.AddUserOptions) error { 619 switch username { 620 case "guy": 621 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 622 case "partnerguy": 623 c.Check(opts.Gecos, check.Equals, "p@partner.com,Partner Guy") 624 case "goodserialguy": 625 c.Check(opts.Gecos, check.Equals, "serial@bar.com,Serial Guy") 626 default: 627 c.Logf("unexpected username %q", username) 628 c.Fail() 629 } 630 c.Check(opts.Sudoer, check.Equals, false) 631 c.Check(opts.Password, check.Equals, "$6$salt$hash") 632 created[username] = true 633 return nil 634 } 635 oldLookup := userLookup 636 // make sure we report them as non-existing until created 637 userLookup = func(username string) (*user.User, error) { 638 if created[username] { 639 return oldLookup(username) 640 } 641 return nil, fmt.Errorf("not created yet") 642 } 643 644 // do it! 645 buf := bytes.NewBufferString(`{"known":true}`) 646 req, err := http.NewRequest("POST", "/v2/create-user", buf) 647 c.Assert(err, check.IsNil) 648 649 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 650 651 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 652 // note that we get a list here instead of a single 653 // userResponseData item 654 c.Check(rsp.Result, check.FitsTypeOf, []userResponseData{}) 655 seen := map[string]bool{} 656 for _, u := range rsp.Result.([]userResponseData) { 657 seen[u.Username] = true 658 c.Check(u, check.DeepEquals, userResponseData{Username: u.Username}) 659 } 660 c.Check(seen, check.DeepEquals, map[string]bool{ 661 "guy": true, 662 "partnerguy": true, 663 "goodserialguy": true, 664 }) 665 666 // ensure the user was added to the state 667 st := s.d.overlord.State() 668 st.Lock() 669 users, err := auth.Users(st) 670 c.Assert(err, check.IsNil) 671 st.Unlock() 672 c.Check(users, check.HasLen, 3) 673 } 674 675 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownClassicErrors(c *check.C) { 676 restore := release.MockOnClassic(true) 677 defer restore() 678 679 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 680 681 // do it! 682 buf := bytes.NewBufferString(`{"known":true}`) 683 req, err := http.NewRequest("POST", "/v2/create-user", buf) 684 c.Assert(err, check.IsNil) 685 686 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 687 688 c.Check(rsp.Type, check.Equals, ResponseTypeError) 689 c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user: device is a classic system`) 690 } 691 692 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwnedErrors(c *check.C) { 693 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 694 695 st := s.d.overlord.State() 696 st.Lock() 697 _, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"}) 698 st.Unlock() 699 c.Check(err, check.IsNil) 700 701 // do it! 702 buf := bytes.NewBufferString(`{"known":true}`) 703 req, err := http.NewRequest("POST", "/v2/create-user", buf) 704 c.Assert(err, check.IsNil) 705 706 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 707 708 c.Check(rsp.Type, check.Equals, ResponseTypeError) 709 c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user: device already managed`) 710 } 711 712 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownNoModelError(c *check.C) { 713 restore := release.MockOnClassic(false) 714 defer restore() 715 716 st := s.d.overlord.State() 717 // have not model yet 718 st.Lock() 719 err := devicestatetest.SetDevice(st, &auth.DeviceState{}) 720 st.Unlock() 721 c.Assert(err, check.IsNil) 722 723 // do it! 724 buf := bytes.NewBufferString(`{"known":true}`) 725 req, err := http.NewRequest("POST", "/v2/create-user", buf) 726 c.Assert(err, check.IsNil) 727 728 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 729 730 c.Check(rsp.Type, check.Equals, ResponseTypeError) 731 c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user: cannot get model assertion: no state entry for key`) 732 } 733 734 func (s *userSuite) TestPostCreateUserFromAssertionNoModel(c *check.C) { 735 restore := release.MockOnClassic(false) 736 defer restore() 737 738 model := s.brands.Model("my-brand", "other-model", map[string]interface{}{ 739 "architecture": "amd64", 740 "gadget": "pc", 741 "kernel": "pc-kernel", 742 "system-user-authority": []interface{}{"my-brand", "partner"}, 743 }) 744 s.makeSystemUsers(c, []map[string]interface{}{serialUser}) 745 746 st := s.d.overlord.State() 747 st.Lock() 748 assertstatetest.AddMany(st, model) 749 err := devicestatetest.SetDevice(st, &auth.DeviceState{ 750 Brand: "my-brand", 751 Model: "my-model", 752 Serial: "other-serial-assertion", 753 }) 754 st.Unlock() 755 c.Assert(err, check.IsNil) 756 757 // do it! 758 buf := bytes.NewBufferString(`{"email":"serial@bar.com", "known":true}`) 759 req, err := http.NewRequest("POST", "/v2/create-user", buf) 760 c.Assert(err, check.IsNil) 761 762 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 763 764 c.Check(rsp.Type, check.Equals, ResponseTypeError) 765 c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot add system-user "serial@bar.com": bound to serial assertion but device not yet registered`) 766 } 767 768 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwned(c *check.C) { 769 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 770 771 st := s.d.overlord.State() 772 st.Lock() 773 _, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"}) 774 st.Unlock() 775 c.Check(err, check.IsNil) 776 777 // mock the calls that create the user 778 created := map[string]bool{} 779 osutilAddUser = func(username string, opts *osutil.AddUserOptions) error { 780 c.Check(username, check.Equals, "guy") 781 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 782 c.Check(opts.Sudoer, check.Equals, false) 783 c.Check(opts.Password, check.Equals, "$6$salt$hash") 784 created[username] = true 785 return nil 786 } 787 oldLookup := userLookup 788 // make sure we report them as non-existing until created 789 userLookup = func(username string) (*user.User, error) { 790 if created[username] { 791 return oldLookup(username) 792 } 793 return nil, fmt.Errorf("not created yet") 794 } 795 796 // do it! 797 buf := bytes.NewBufferString(`{"known":true,"force-managed":true}`) 798 req, err := http.NewRequest("POST", "/v2/create-user", buf) 799 c.Assert(err, check.IsNil) 800 801 rsp := postCreateUser(createUserCmd, req, nil).(*resp) 802 803 // note that we get a list here instead of a single 804 // userResponseData item 805 expected := []userResponseData{ 806 {Username: "guy"}, 807 } 808 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 809 c.Check(rsp.Result, check.FitsTypeOf, expected) 810 c.Check(rsp.Result, check.DeepEquals, expected) 811 } 812 813 func (s *userSuite) TestUsersEmpty(c *check.C) { 814 req, err := http.NewRequest("GET", "/v2/users", nil) 815 c.Assert(err, check.IsNil) 816 817 rsp := getUsers(usersCmd, req, nil).(*resp) 818 819 expected := []userResponseData{} 820 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 821 c.Check(rsp.Result, check.FitsTypeOf, expected) 822 c.Check(rsp.Result, check.DeepEquals, expected) 823 } 824 825 func (s *userSuite) TestUsersHasUser(c *check.C) { 826 st := s.d.overlord.State() 827 st.Lock() 828 u, err := auth.NewUser(st, "someuser", "mymail@test.com", "macaroon", []string{"discharge"}) 829 st.Unlock() 830 c.Assert(err, check.IsNil) 831 832 req, err := http.NewRequest("GET", "/v2/users", nil) 833 c.Assert(err, check.IsNil) 834 835 rsp := getUsers(usersCmd, req, nil).(*resp) 836 837 expected := []userResponseData{ 838 {ID: u.ID, Username: u.Username, Email: u.Email}, 839 } 840 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 841 c.Check(rsp.Result, check.FitsTypeOf, expected) 842 c.Check(rsp.Result, check.DeepEquals, expected) 843 } 844 845 func (s *userSuite) TestSysInfoIsManaged(c *check.C) { 846 st := s.d.overlord.State() 847 st.Lock() 848 _, err := auth.NewUser(st, "someuser", "mymail@test.com", "macaroon", []string{"discharge"}) 849 st.Unlock() 850 c.Assert(err, check.IsNil) 851 852 req, err := http.NewRequest("GET", "/v2/system-info", nil) 853 c.Assert(err, check.IsNil) 854 855 rsp := sysInfo(sysInfoCmd, req, nil).(*resp) 856 857 c.Check(rsp.Type, check.Equals, ResponseTypeSync) 858 c.Check(rsp.Result.(map[string]interface{})["managed"], check.Equals, true) 859 } 860 861 func (s *userSuite) TestSysInfoWorksDegraded(c *check.C) { 862 s.d.SetDegradedMode(fmt.Errorf("some error")) 863 864 req, err := http.NewRequest("GET", "/v2/system-info", nil) 865 c.Assert(err, check.IsNil) 866 867 rsp := sysInfo(sysInfoCmd, req, nil).(*resp) 868 c.Check(rsp.Status, check.Equals, 200) 869 }