github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/daemon/api_users_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 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_test 21 22 import ( 23 "bytes" 24 "fmt" 25 "net/http" 26 "os/user" 27 "path/filepath" 28 "strings" 29 "time" 30 31 "gopkg.in/check.v1" 32 33 "github.com/snapcore/snapd/asserts" 34 "github.com/snapcore/snapd/asserts/assertstest" 35 "github.com/snapcore/snapd/client" 36 "github.com/snapcore/snapd/daemon" 37 "github.com/snapcore/snapd/osutil" 38 "github.com/snapcore/snapd/overlord/assertstate/assertstatetest" 39 "github.com/snapcore/snapd/overlord/auth" 40 "github.com/snapcore/snapd/overlord/configstate/config" 41 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 42 "github.com/snapcore/snapd/release" 43 "github.com/snapcore/snapd/store" 44 "github.com/snapcore/snapd/testutil" 45 ) 46 47 var _ = check.Suite(&userSuite{}) 48 49 type userSuite struct { 50 apiBaseSuite 51 52 userInfoResult *store.User 53 userInfoExpectedEmail string 54 55 loginUserStoreMacaroon string 56 loginUserDischarge string 57 58 mockUserHome string 59 trivialUserLookup func(username string) (*user.User, error) 60 } 61 62 func (s *userSuite) UserInfo(email string) (userinfo *store.User, err error) { 63 s.pokeStateLock() 64 65 if s.userInfoExpectedEmail != email { 66 panic(fmt.Sprintf("%q != %q", s.userInfoExpectedEmail, email)) 67 } 68 return s.userInfoResult, s.err 69 } 70 71 func (s *userSuite) LoginUser(username, password, otp string) (string, string, error) { 72 s.pokeStateLock() 73 74 return s.loginUserStoreMacaroon, s.loginUserDischarge, s.err 75 } 76 77 func (s *userSuite) SetUpTest(c *check.C) { 78 s.apiBaseSuite.SetUpTest(c) 79 80 s.AddCleanup(release.MockOnClassic(false)) 81 82 s.daemonWithStore(c, s) 83 84 s.mockUserHome = c.MkDir() 85 s.trivialUserLookup = mkUserLookup(s.mockUserHome) 86 s.AddCleanup(daemon.MockUserLookup(s.trivialUserLookup)) 87 88 s.AddCleanup(daemon.MockHasUserAdmin(true)) 89 90 // make sure we don't call these by accident 91 s.AddCleanup(daemon.MockOsutilAddUser(func(name string, opts *osutil.AddUserOptions) error { 92 c.Fatalf("unexpected add user %q call", name) 93 return fmt.Errorf("unexpected add user %q call", name) 94 })) 95 s.AddCleanup(daemon.MockOsutilDelUser(func(name string, opts *osutil.DelUserOptions) error { 96 c.Fatalf("unexpected del user %q call", name) 97 return fmt.Errorf("unexpected del user %q call", name) 98 })) 99 100 s.userInfoResult = nil 101 s.userInfoExpectedEmail = "" 102 103 s.loginUserStoreMacaroon = "" 104 s.loginUserDischarge = "" 105 } 106 107 func mkUserLookup(userHomeDir string) func(string) (*user.User, error) { 108 return func(username string) (*user.User, error) { 109 cur, err := user.Current() 110 cur.Username = username 111 cur.HomeDir = userHomeDir 112 return cur, err 113 } 114 } 115 116 func (s *userSuite) TestLoginUser(c *check.C) { 117 state := s.d.Overlord().State() 118 119 s.loginUserStoreMacaroon = "user-macaroon" 120 s.loginUserDischarge = "the-discharge-macaroon-serialized-data" 121 buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`) 122 req, err := http.NewRequest("POST", "/v2/login", buf) 123 c.Assert(err, check.IsNil) 124 125 rsp := s.syncReq(c, req, nil) 126 127 state.Lock() 128 user, err := auth.User(state, 1) 129 state.Unlock() 130 c.Check(err, check.IsNil) 131 132 expected := daemon.UserResponseData{ 133 ID: 1, 134 Email: "email@.com", 135 136 Macaroon: user.Macaroon, 137 Discharges: user.Discharges, 138 } 139 140 c.Check(rsp.Status, check.Equals, 200) 141 c.Assert(rsp.Result, check.FitsTypeOf, expected) 142 c.Check(rsp.Result, check.DeepEquals, expected) 143 144 c.Check(user.ID, check.Equals, 1) 145 c.Check(user.Username, check.Equals, "") 146 c.Check(user.Email, check.Equals, "email@.com") 147 c.Check(user.Discharges, check.IsNil) 148 c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon) 149 c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"}) 150 // snapd macaroon was setup too 151 snapdMacaroon, err := auth.MacaroonDeserialize(user.Macaroon) 152 c.Check(err, check.IsNil) 153 c.Check(snapdMacaroon.Id(), check.Equals, "1") 154 c.Check(snapdMacaroon.Location(), check.Equals, "snapd") 155 } 156 157 func (s *userSuite) TestLoginUserWithUsername(c *check.C) { 158 state := s.d.Overlord().State() 159 160 s.loginUserStoreMacaroon = "user-macaroon" 161 s.loginUserDischarge = "the-discharge-macaroon-serialized-data" 162 buf := bytes.NewBufferString(`{"username": "username", "email": "email@.com", "password": "password"}`) 163 req, err := http.NewRequest("POST", "/v2/login", buf) 164 c.Assert(err, check.IsNil) 165 166 rsp := s.syncReq(c, req, nil) 167 168 state.Lock() 169 user, err := auth.User(state, 1) 170 state.Unlock() 171 c.Check(err, check.IsNil) 172 173 expected := daemon.UserResponseData{ 174 ID: 1, 175 Username: "username", 176 Email: "email@.com", 177 Macaroon: user.Macaroon, 178 Discharges: user.Discharges, 179 } 180 c.Check(rsp.Status, check.Equals, 200) 181 c.Assert(rsp.Result, check.FitsTypeOf, expected) 182 c.Check(rsp.Result, check.DeepEquals, expected) 183 184 c.Check(user.ID, check.Equals, 1) 185 c.Check(user.Username, check.Equals, "username") 186 c.Check(user.Email, check.Equals, "email@.com") 187 c.Check(user.Discharges, check.IsNil) 188 c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon) 189 c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"}) 190 // snapd macaroon was setup too 191 snapdMacaroon, err := auth.MacaroonDeserialize(user.Macaroon) 192 c.Check(err, check.IsNil) 193 c.Check(snapdMacaroon.Id(), check.Equals, "1") 194 c.Check(snapdMacaroon.Location(), check.Equals, "snapd") 195 } 196 197 func (s *userSuite) TestLoginUserNoEmailWithExistentLocalUser(c *check.C) { 198 state := s.d.Overlord().State() 199 200 // setup local-only user 201 state.Lock() 202 localUser, err := auth.NewUser(state, "username", "email@test.com", "", nil) 203 state.Unlock() 204 c.Assert(err, check.IsNil) 205 206 s.loginUserStoreMacaroon = "user-macaroon" 207 s.loginUserDischarge = "the-discharge-macaroon-serialized-data" 208 buf := bytes.NewBufferString(`{"username": "username", "email": "", "password": "password"}`) 209 req, err := http.NewRequest("POST", "/v2/login", buf) 210 c.Assert(err, check.IsNil) 211 req.Header.Set("Authorization", fmt.Sprintf(`Macaroon root="%s"`, localUser.Macaroon)) 212 213 rsp := s.syncReq(c, req, localUser) 214 215 expected := daemon.UserResponseData{ 216 ID: 1, 217 Username: "username", 218 Email: "email@test.com", 219 220 Macaroon: localUser.Macaroon, 221 Discharges: localUser.Discharges, 222 } 223 c.Check(rsp.Status, check.Equals, 200) 224 c.Assert(rsp.Result, check.FitsTypeOf, expected) 225 c.Check(rsp.Result, check.DeepEquals, expected) 226 227 state.Lock() 228 user, err := auth.User(state, localUser.ID) 229 state.Unlock() 230 c.Check(err, check.IsNil) 231 c.Check(user.Username, check.Equals, "username") 232 c.Check(user.Email, check.Equals, localUser.Email) 233 c.Check(user.Macaroon, check.Equals, localUser.Macaroon) 234 c.Check(user.Discharges, check.IsNil) 235 c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon) 236 c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"}) 237 } 238 239 func (s *userSuite) TestLoginUserWithExistentLocalUser(c *check.C) { 240 state := s.d.Overlord().State() 241 242 // setup local-only user 243 state.Lock() 244 localUser, err := auth.NewUser(state, "username", "email@test.com", "", nil) 245 state.Unlock() 246 c.Assert(err, check.IsNil) 247 248 s.loginUserStoreMacaroon = "user-macaroon" 249 s.loginUserDischarge = "the-discharge-macaroon-serialized-data" 250 buf := bytes.NewBufferString(`{"username": "username", "email": "email@test.com", "password": "password"}`) 251 req, err := http.NewRequest("POST", "/v2/login", buf) 252 c.Assert(err, check.IsNil) 253 req.Header.Set("Authorization", fmt.Sprintf(`Macaroon root="%s"`, localUser.Macaroon)) 254 255 rsp := s.syncReq(c, req, localUser) 256 257 expected := daemon.UserResponseData{ 258 ID: 1, 259 Username: "username", 260 Email: "email@test.com", 261 262 Macaroon: localUser.Macaroon, 263 Discharges: localUser.Discharges, 264 } 265 c.Check(rsp.Status, check.Equals, 200) 266 c.Assert(rsp.Result, check.FitsTypeOf, expected) 267 c.Check(rsp.Result, check.DeepEquals, expected) 268 269 state.Lock() 270 user, err := auth.User(state, localUser.ID) 271 state.Unlock() 272 c.Check(err, check.IsNil) 273 c.Check(user.Username, check.Equals, "username") 274 c.Check(user.Email, check.Equals, localUser.Email) 275 c.Check(user.Macaroon, check.Equals, localUser.Macaroon) 276 c.Check(user.Discharges, check.IsNil) 277 c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon) 278 c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"}) 279 } 280 281 func (s *userSuite) TestLoginUserNewEmailWithExistentLocalUser(c *check.C) { 282 state := s.d.Overlord().State() 283 284 // setup local-only user 285 state.Lock() 286 localUser, err := auth.NewUser(state, "username", "email@test.com", "", nil) 287 state.Unlock() 288 c.Assert(err, check.IsNil) 289 290 s.loginUserStoreMacaroon = "user-macaroon" 291 s.loginUserDischarge = "the-discharge-macaroon-serialized-data" 292 // same local user, but using a new SSO account 293 buf := bytes.NewBufferString(`{"username": "username", "email": "new.email@test.com", "password": "password"}`) 294 req, err := http.NewRequest("POST", "/v2/login", buf) 295 c.Assert(err, check.IsNil) 296 req.Header.Set("Authorization", fmt.Sprintf(`Macaroon root="%s"`, localUser.Macaroon)) 297 298 rsp := s.syncReq(c, req, localUser) 299 300 expected := daemon.UserResponseData{ 301 ID: 1, 302 Username: "username", 303 Email: "new.email@test.com", 304 305 Macaroon: localUser.Macaroon, 306 Discharges: localUser.Discharges, 307 } 308 c.Check(rsp.Status, check.Equals, 200) 309 c.Assert(rsp.Result, check.FitsTypeOf, expected) 310 c.Check(rsp.Result, check.DeepEquals, expected) 311 312 state.Lock() 313 user, err := auth.User(state, localUser.ID) 314 state.Unlock() 315 c.Check(err, check.IsNil) 316 c.Check(user.Username, check.Equals, "username") 317 c.Check(user.Email, check.Equals, expected.Email) 318 c.Check(user.Macaroon, check.Equals, localUser.Macaroon) 319 c.Check(user.Discharges, check.IsNil) 320 c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon) 321 c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"}) 322 } 323 324 func (s *userSuite) TestLogoutUser(c *check.C) { 325 state := s.d.Overlord().State() 326 state.Lock() 327 user, err := auth.NewUser(state, "username", "email@test.com", "macaroon", []string{"discharge"}) 328 state.Unlock() 329 c.Assert(err, check.IsNil) 330 331 req, err := http.NewRequest("POST", "/v2/logout", nil) 332 c.Assert(err, check.IsNil) 333 req.Header.Set("Authorization", `Macaroon root="macaroon", discharge="discharge"`) 334 335 rsp := s.syncReq(c, req, user) 336 c.Check(rsp.Status, check.Equals, 200) 337 338 state.Lock() 339 _, err = auth.User(state, user.ID) 340 state.Unlock() 341 c.Check(err, check.Equals, auth.ErrInvalidUser) 342 } 343 344 func (s *userSuite) TestLoginUserBadRequest(c *check.C) { 345 buf := bytes.NewBufferString(`hello`) 346 req, err := http.NewRequest("POST", "/v2/login", buf) 347 c.Assert(err, check.IsNil) 348 349 rsp := s.errorReq(c, req, nil) 350 c.Check(rsp.Status, check.Equals, 400) 351 c.Check(rsp.Result, check.NotNil) 352 } 353 354 func (s *userSuite) TestLoginUserDeveloperAPIError(c *check.C) { 355 s.err = fmt.Errorf("error-from-login-user") 356 buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`) 357 req, err := http.NewRequest("POST", "/v2/login", buf) 358 c.Assert(err, check.IsNil) 359 360 rsp := s.errorReq(c, req, nil) 361 c.Check(rsp.Status, check.Equals, 401) 362 c.Check(rsp.Result.(*daemon.ErrorResult).Message, testutil.Contains, "error-from-login-user") 363 } 364 365 func (s *userSuite) TestLoginUserTwoFactorRequiredError(c *check.C) { 366 s.err = store.ErrAuthenticationNeeds2fa 367 buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`) 368 req, err := http.NewRequest("POST", "/v2/login", buf) 369 c.Assert(err, check.IsNil) 370 371 rsp := s.errorReq(c, req, nil) 372 c.Check(rsp.Status, check.Equals, 401) 373 c.Check(rsp.Result.(*daemon.ErrorResult).Kind, check.Equals, client.ErrorKindTwoFactorRequired) 374 } 375 376 func (s *userSuite) TestLoginUserTwoFactorFailedError(c *check.C) { 377 s.err = store.Err2faFailed 378 buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`) 379 req, err := http.NewRequest("POST", "/v2/login", buf) 380 c.Assert(err, check.IsNil) 381 382 rsp := s.errorReq(c, req, nil) 383 c.Check(rsp.Status, check.Equals, 401) 384 c.Check(rsp.Result.(*daemon.ErrorResult).Kind, check.Equals, client.ErrorKindTwoFactorFailed) 385 } 386 387 func (s *userSuite) TestLoginUserInvalidCredentialsError(c *check.C) { 388 s.err = store.ErrInvalidCredentials 389 buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`) 390 req, err := http.NewRequest("POST", "/v2/login", buf) 391 c.Assert(err, check.IsNil) 392 393 rsp := s.errorReq(c, req, nil) 394 c.Check(rsp.Status, check.Equals, 401) 395 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Equals, "invalid credentials") 396 } 397 398 func (s *userSuite) TestUsersOnlyRoot(c *check.C) { 399 for _, cmd := range daemon.APICommands() { 400 if strings.Contains(cmd.Path, "user") { 401 c.Check(cmd.RootOnly, check.Equals, true, check.Commentf(cmd.Path)) 402 } 403 } 404 } 405 406 func (s *userSuite) TestPostCreateUserNoSSHKeys(c *check.C) { 407 s.userInfoExpectedEmail = "popper@lse.ac.uk" 408 s.userInfoResult = &store.User{ 409 Username: "karl", 410 OpenIDIdentifier: "xxyyzz", 411 } 412 buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail)) 413 req, err := http.NewRequest("POST", "/v2/create-user", buf) 414 c.Assert(err, check.IsNil) 415 416 rsp := s.errorReq(c, req, nil) 417 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user for "popper@lse.ac.uk": no ssh keys found`) 418 } 419 420 func (s *userSuite) TestPostCreateUser(c *check.C) { 421 s.testCreateUser(c, true) 422 } 423 424 func (s *userSuite) TestPostUserCreate(c *check.C) { 425 s.testCreateUser(c, false) 426 } 427 428 func (s *userSuite) testCreateUser(c *check.C, oldWay bool) { 429 expectedUsername := "karl" 430 s.userInfoExpectedEmail = "popper@lse.ac.uk" 431 s.userInfoResult = &store.User{ 432 Username: expectedUsername, 433 SSHKeys: []string{"ssh1", "ssh2"}, 434 OpenIDIdentifier: "xxyyzz", 435 } 436 defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error { 437 c.Check(username, check.Equals, expectedUsername) 438 c.Check(opts.SSHKeys, check.DeepEquals, []string{"ssh1", "ssh2"}) 439 c.Check(opts.Gecos, check.Equals, "popper@lse.ac.uk,xxyyzz") 440 c.Check(opts.Sudoer, check.Equals, false) 441 return nil 442 })() 443 444 var req *http.Request 445 var expected interface{} 446 expectedItem := daemon.UserResponseData{ 447 Username: expectedUsername, 448 SSHKeys: []string{"ssh1", "ssh2"}, 449 } 450 451 if oldWay { 452 var err error 453 buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail)) 454 req, err = http.NewRequest("POST", "/v2/create-user", buf) 455 c.Assert(err, check.IsNil) 456 expected = &expectedItem 457 } else { 458 var err error 459 buf := bytes.NewBufferString(fmt.Sprintf(`{"action":"create","email": "%s"}`, s.userInfoExpectedEmail)) 460 req, err = http.NewRequest("POST", "/v2/users", buf) 461 c.Assert(err, check.IsNil) 462 expected = []daemon.UserResponseData{expectedItem} 463 } 464 465 rsp := s.syncReq(c, req, nil) 466 c.Check(rsp.Result, check.FitsTypeOf, expected) 467 c.Check(rsp.Result, check.DeepEquals, expected) 468 469 // user was setup in state 470 state := s.d.Overlord().State() 471 state.Lock() 472 user, err := auth.User(state, 1) 473 state.Unlock() 474 c.Check(err, check.IsNil) 475 c.Check(user.Username, check.Equals, expectedUsername) 476 c.Check(user.Email, check.Equals, s.userInfoExpectedEmail) 477 c.Check(user.Macaroon, check.NotNil) 478 // auth saved to user home dir 479 outfile := filepath.Join(s.mockUserHome, ".snap", "auth.json") 480 c.Check(osutil.FileExists(outfile), check.Equals, true) 481 c.Check(outfile, testutil.FileEquals, 482 fmt.Sprintf(`{"id":%d,"username":"%s","email":"%s","macaroon":"%s"}`, 483 1, expectedUsername, s.userInfoExpectedEmail, user.Macaroon)) 484 } 485 486 func (s *userSuite) TestNoUserAdminCreateUser(c *check.C) { s.testNoUserAdmin(c, "/v2/create-user") } 487 func (s *userSuite) TestNoUserAdminPostUser(c *check.C) { s.testNoUserAdmin(c, "/v2/users") } 488 func (s *userSuite) testNoUserAdmin(c *check.C, endpoint string) { 489 defer daemon.MockHasUserAdmin(false)() 490 491 buf := bytes.NewBufferString("{}") 492 req, err := http.NewRequest("POST", endpoint, buf) 493 c.Assert(err, check.IsNil) 494 495 rsp := s.errorReq(c, req, nil) 496 497 const noUserAdmin = "system user administration via snapd is not allowed on this system" 498 switch endpoint { 499 case "/v2/users": 500 c.Check(rsp, check.DeepEquals, daemon.MethodNotAllowed(noUserAdmin)) 501 case "/v2/create-user": 502 c.Check(rsp, check.DeepEquals, daemon.Forbidden(noUserAdmin)) 503 default: 504 c.Fatalf("unknown endpoint %q", endpoint) 505 } 506 } 507 508 func (s *userSuite) TestPostUserBadBody(c *check.C) { 509 buf := bytes.NewBufferString(`42`) 510 req, err := http.NewRequest("POST", "/v2/users", buf) 511 c.Assert(err, check.IsNil) 512 513 rsp := s.errorReq(c, req, nil) 514 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, "cannot decode user action data from request body: .*") 515 } 516 517 func (s *userSuite) TestPostUserBadAfterBody(c *check.C) { 518 buf := bytes.NewBufferString(`{}42`) 519 req, err := http.NewRequest("POST", "/v2/users", buf) 520 c.Assert(err, check.IsNil) 521 522 rsp := s.errorReq(c, req, nil) 523 c.Check(rsp, check.DeepEquals, daemon.BadRequest("spurious content after user action")) 524 } 525 526 func (s *userSuite) TestPostUserNoAction(c *check.C) { 527 buf := bytes.NewBufferString("{}") 528 req, err := http.NewRequest("POST", "/v2/users", buf) 529 c.Assert(err, check.IsNil) 530 531 rsp := s.errorReq(c, req, nil) 532 c.Check(rsp, check.DeepEquals, daemon.BadRequest("missing user action")) 533 } 534 535 func (s *userSuite) TestPostUserBadAction(c *check.C) { 536 buf := bytes.NewBufferString(`{"action":"patatas"}`) 537 req, err := http.NewRequest("POST", "/v2/users", buf) 538 c.Assert(err, check.IsNil) 539 540 rsp := s.errorReq(c, req, nil) 541 c.Check(rsp, check.DeepEquals, daemon.BadRequest(`unsupported user action "patatas"`)) 542 } 543 544 func (s *userSuite) TestPostUserActionRemoveNoUsername(c *check.C) { 545 buf := bytes.NewBufferString(`{"action":"remove"}`) 546 req, err := http.NewRequest("POST", "/v2/users", buf) 547 c.Assert(err, check.IsNil) 548 549 rsp := s.errorReq(c, req, nil) 550 c.Check(rsp, check.DeepEquals, daemon.BadRequest("need a username to remove")) 551 } 552 553 func (s *userSuite) TestPostUserActionRemoveDelUserErr(c *check.C) { 554 st := s.d.Overlord().State() 555 st.Lock() 556 _, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"}) 557 st.Unlock() 558 c.Check(err, check.IsNil) 559 560 called := 0 561 defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error { 562 called++ 563 c.Check(username, check.Equals, "some-user") 564 return fmt.Errorf("wat") 565 })() 566 567 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 568 req, err := http.NewRequest("POST", "/v2/users", buf) 569 c.Assert(err, check.IsNil) 570 571 rsp := s.errorReq(c, req, nil) 572 c.Check(rsp.Status, check.Equals, 500) 573 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Equals, "wat") 574 c.Check(called, check.Equals, 1) 575 } 576 577 func (s *userSuite) TestPostUserActionRemoveStateErr(c *check.C) { 578 st := s.d.Overlord().State() 579 st.Lock() 580 st.Set("auth", 42) // breaks auth 581 st.Unlock() 582 called := 0 583 defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error { 584 called++ 585 c.Check(username, check.Equals, "some-user") 586 return nil 587 })() 588 589 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 590 req, err := http.NewRequest("POST", "/v2/users", buf) 591 c.Assert(err, check.IsNil) 592 593 rsp := s.errorReq(c, req, nil) 594 c.Check(rsp.Status, check.Equals, 500) 595 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `internal error: could not unmarshal state entry "auth": .*`) 596 c.Check(called, check.Equals, 0) 597 } 598 599 func (s *userSuite) TestPostUserActionRemoveNoUserInState(c *check.C) { 600 called := 0 601 defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error { 602 called++ 603 c.Check(username, check.Equals, "some-user") 604 return nil 605 }) 606 607 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 608 req, err := http.NewRequest("POST", "/v2/users", buf) 609 c.Assert(err, check.IsNil) 610 611 rsp := s.errorReq(c, req, nil) 612 c.Check(rsp, check.DeepEquals, daemon.BadRequest(`user "some-user" is not known`)) 613 c.Check(called, check.Equals, 0) 614 } 615 616 func (s *userSuite) TestPostUserActionRemove(c *check.C) { 617 st := s.d.Overlord().State() 618 st.Lock() 619 user, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"}) 620 st.Unlock() 621 c.Check(err, check.IsNil) 622 623 called := 0 624 defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error { 625 called++ 626 c.Check(username, check.Equals, "some-user") 627 return nil 628 })() 629 630 buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`) 631 req, err := http.NewRequest("POST", "/v2/users", buf) 632 c.Assert(err, check.IsNil) 633 rsp := s.syncReq(c, req, nil) 634 c.Check(rsp.Status, check.Equals, 200) 635 expected := []daemon.UserResponseData{ 636 {ID: user.ID, Username: user.Username, Email: user.Email}, 637 } 638 c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{ 639 "removed": expected, 640 }) 641 c.Check(called, check.Equals, 1) 642 643 // and the user is removed from state 644 st.Lock() 645 _, err = auth.User(st, user.ID) 646 st.Unlock() 647 c.Check(err, check.Equals, auth.ErrInvalidUser) 648 } 649 650 func (s *userSuite) setupSigner(accountID string, signerPrivKey asserts.PrivateKey) *assertstest.SigningDB { 651 st := s.d.Overlord().State() 652 653 signerSigning := s.Brands.Register(accountID, signerPrivKey, map[string]interface{}{ 654 "account-id": accountID, 655 "verification": "verified", 656 }) 657 acctNKey := s.Brands.AccountsAndKeys(accountID) 658 659 assertstest.AddMany(s.StoreSigning, acctNKey...) 660 assertstatetest.AddMany(st, acctNKey...) 661 662 return signerSigning 663 } 664 665 var ( 666 partnerPrivKey, _ = assertstest.GenerateKey(752) 667 unknownPrivKey, _ = assertstest.GenerateKey(752) 668 ) 669 670 func (s *userSuite) makeSystemUsers(c *check.C, systemUsers []map[string]interface{}) { 671 st := s.d.Overlord().State() 672 st.Lock() 673 defer st.Unlock() 674 675 assertstatetest.AddMany(st, s.StoreSigning.StoreAccountKey("")) 676 677 s.setupSigner("my-brand", brandPrivKey) 678 s.setupSigner("partner", partnerPrivKey) 679 s.setupSigner("unknown", unknownPrivKey) 680 681 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 682 "architecture": "amd64", 683 "gadget": "pc", 684 "kernel": "pc-kernel", 685 "required-snaps": []interface{}{"required-snap1"}, 686 "system-user-authority": []interface{}{"my-brand", "partner"}, 687 }) 688 // now add model related stuff to the system 689 assertstatetest.AddMany(st, model) 690 // and a serial 691 deviceKey, _ := assertstest.GenerateKey(752) 692 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 693 c.Assert(err, check.IsNil) 694 serial, err := s.Brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 695 "authority-id": "my-brand", 696 "brand-id": "my-brand", 697 "model": "my-model", 698 "serial": "serialserial", 699 "device-key": string(encDevKey), 700 "device-key-sha3-384": deviceKey.PublicKey().ID(), 701 "timestamp": time.Now().Format(time.RFC3339), 702 }, nil, "") 703 c.Assert(err, check.IsNil) 704 assertstatetest.AddMany(st, serial) 705 706 for _, suMap := range systemUsers { 707 su, err := s.Brands.Signing(suMap["authority-id"].(string)).Sign(asserts.SystemUserType, suMap, nil, "") 708 c.Assert(err, check.IsNil) 709 su = su.(*asserts.SystemUser) 710 // now add system-user assertion to the system 711 assertstatetest.AddMany(st, su) 712 } 713 // create fake device 714 err = devicestatetest.SetDevice(st, &auth.DeviceState{ 715 Brand: "my-brand", 716 Model: "my-model", 717 Serial: "serialserial", 718 }) 719 c.Assert(err, check.IsNil) 720 } 721 722 var goodUser = map[string]interface{}{ 723 "authority-id": "my-brand", 724 "brand-id": "my-brand", 725 "email": "foo@bar.com", 726 "series": []interface{}{"16", "18"}, 727 "models": []interface{}{"my-model", "other-model"}, 728 "name": "Boring Guy", 729 "username": "guy", 730 "password": "$6$salt$hash", 731 "since": time.Now().Format(time.RFC3339), 732 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 733 } 734 735 var partnerUser = map[string]interface{}{ 736 "authority-id": "partner", 737 "brand-id": "my-brand", 738 "email": "p@partner.com", 739 "series": []interface{}{"16", "18"}, 740 "models": []interface{}{"my-model"}, 741 "name": "Partner Guy", 742 "username": "partnerguy", 743 "password": "$6$salt$hash", 744 "since": time.Now().Format(time.RFC3339), 745 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 746 } 747 748 var serialUser = map[string]interface{}{ 749 "format": "1", 750 "authority-id": "my-brand", 751 "brand-id": "my-brand", 752 "email": "serial@bar.com", 753 "series": []interface{}{"16", "18"}, 754 "models": []interface{}{"my-model"}, 755 "serials": []interface{}{"serialserial"}, 756 "name": "Serial Guy", 757 "username": "goodserialguy", 758 "password": "$6$salt$hash", 759 "since": time.Now().Format(time.RFC3339), 760 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 761 } 762 763 var badUser = map[string]interface{}{ 764 // bad user (not valid for this model) 765 "authority-id": "my-brand", 766 "brand-id": "my-brand", 767 "email": "foobar@bar.com", 768 "series": []interface{}{"16", "18"}, 769 "models": []interface{}{"non-of-the-models-i-have"}, 770 "name": "Random Gal", 771 "username": "gal", 772 "password": "$6$salt$hash", 773 "since": time.Now().Format(time.RFC3339), 774 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 775 } 776 777 var badUserNoMatchingSerial = map[string]interface{}{ 778 "format": "1", 779 "authority-id": "my-brand", 780 "brand-id": "my-brand", 781 "email": "noserial@bar.com", 782 "series": []interface{}{"16", "18"}, 783 "models": []interface{}{"my-model"}, 784 "serials": []interface{}{"different-serialserial"}, 785 "name": "No Serial Guy", 786 "username": "noserial", 787 "password": "$6$salt$hash", 788 "since": time.Now().Format(time.RFC3339), 789 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 790 } 791 792 var unknownUser = map[string]interface{}{ 793 "authority-id": "unknown", 794 "brand-id": "my-brand", 795 "email": "x@partner.com", 796 "series": []interface{}{"16", "18"}, 797 "models": []interface{}{"my-model"}, 798 "name": "XGuy", 799 "username": "xguy", 800 "password": "$6$salt$hash", 801 "since": time.Now().Format(time.RFC3339), 802 "until": time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339), 803 } 804 805 func (s *userSuite) TestGetUserDetailsFromAssertionHappy(c *check.C) { 806 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 807 808 st := s.d.Overlord().State() 809 810 st.Lock() 811 model, err := s.d.Overlord().DeviceManager().Model() 812 st.Unlock() 813 c.Assert(err, check.IsNil) 814 815 // ensure that if we query the details from the assert DB we get 816 // the expected user 817 username, opts, err := daemon.GetUserDetailsFromAssertion(st, model, nil, "foo@bar.com") 818 c.Check(username, check.Equals, "guy") 819 c.Check(opts, check.DeepEquals, &osutil.AddUserOptions{ 820 Gecos: "foo@bar.com,Boring Guy", 821 Password: "$6$salt$hash", 822 }) 823 c.Check(err, check.IsNil) 824 } 825 826 // FIXME: These tests all look similar, with small deltas. Would be 827 // nice to transform them into a table that is just the deltas, and 828 // run on a loop. 829 func (s *userSuite) TestPostCreateUserFromAssertion(c *check.C) { 830 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 831 832 // mock the calls that create the user 833 defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error { 834 c.Check(username, check.Equals, "guy") 835 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 836 c.Check(opts.Sudoer, check.Equals, false) 837 c.Check(opts.Password, check.Equals, "$6$salt$hash") 838 c.Check(opts.ForcePasswordChange, check.Equals, false) 839 return nil 840 })() 841 842 // do it! 843 buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`) 844 req, err := http.NewRequest("POST", "/v2/create-user", buf) 845 c.Assert(err, check.IsNil) 846 847 rsp := s.syncReq(c, req, nil) 848 849 expected := &daemon.UserResponseData{ 850 Username: "guy", 851 } 852 853 c.Check(rsp.Result, check.FitsTypeOf, expected) 854 c.Check(rsp.Result, check.DeepEquals, expected) 855 856 // ensure the user was added to the state 857 st := s.d.Overlord().State() 858 st.Lock() 859 users, err := auth.Users(st) 860 c.Assert(err, check.IsNil) 861 st.Unlock() 862 c.Check(users, check.HasLen, 1) 863 } 864 865 func (s *userSuite) TestPostCreateUserFromAssertionWithForcePasswordChange(c *check.C) { 866 user := make(map[string]interface{}) 867 for k, v := range goodUser { 868 user[k] = v 869 } 870 user["force-password-change"] = "true" 871 lusers := []map[string]interface{}{user} 872 s.makeSystemUsers(c, lusers) 873 874 // mock the calls that create the user 875 defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error { 876 c.Check(username, check.Equals, "guy") 877 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 878 c.Check(opts.Sudoer, check.Equals, false) 879 c.Check(opts.Password, check.Equals, "$6$salt$hash") 880 c.Check(opts.ForcePasswordChange, check.Equals, true) 881 return nil 882 })() 883 884 // do it! 885 buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`) 886 req, err := http.NewRequest("POST", "/v2/create-user", buf) 887 c.Assert(err, check.IsNil) 888 889 rsp := s.syncReq(c, req, nil) 890 891 expected := &daemon.UserResponseData{ 892 Username: "guy", 893 } 894 895 c.Check(rsp.Result, check.FitsTypeOf, expected) 896 c.Check(rsp.Result, check.DeepEquals, expected) 897 898 // ensure the user was added to the state 899 st := s.d.Overlord().State() 900 st.Lock() 901 users, err := auth.Users(st) 902 c.Assert(err, check.IsNil) 903 st.Unlock() 904 c.Check(users, check.HasLen, 1) 905 } 906 907 func (s *userSuite) TestPostCreateUserFromAssertionAllKnown(c *check.C) { 908 expectSudoer := false 909 s.testPostCreateUserFromAssertion(c, `{"known":true}`, expectSudoer) 910 } 911 912 func (s *userSuite) TestPostCreateUserFromAssertionAllAutomatic(c *check.C) { 913 // automatic implies "sudoder" 914 expectSudoer := true 915 s.testPostCreateUserFromAssertion(c, `{"automatic":true}`, expectSudoer) 916 } 917 918 func (s *userSuite) testPostCreateUserFromAssertion(c *check.C, postData string, expectSudoer bool) { 919 s.makeSystemUsers(c, []map[string]interface{}{goodUser, partnerUser, serialUser, badUser, badUserNoMatchingSerial, unknownUser}) 920 created := map[string]bool{} 921 // mock the calls that create the user 922 defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error { 923 switch username { 924 case "guy": 925 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 926 case "partnerguy": 927 c.Check(opts.Gecos, check.Equals, "p@partner.com,Partner Guy") 928 case "goodserialguy": 929 c.Check(opts.Gecos, check.Equals, "serial@bar.com,Serial Guy") 930 default: 931 c.Logf("unexpected username %q", username) 932 c.Fail() 933 } 934 c.Check(opts.Sudoer, check.Equals, expectSudoer) 935 c.Check(opts.Password, check.Equals, "$6$salt$hash") 936 created[username] = true 937 return nil 938 })() 939 // make sure we report them as non-existing until created 940 defer daemon.MockUserLookup(func(username string) (*user.User, error) { 941 if created[username] { 942 return s.trivialUserLookup(username) 943 } 944 return nil, fmt.Errorf("not created yet") 945 })() 946 947 // do it! 948 buf := bytes.NewBufferString(postData) 949 req, err := http.NewRequest("POST", "/v2/create-user", buf) 950 c.Assert(err, check.IsNil) 951 952 rsp := s.syncReq(c, req, nil) 953 954 // note that we get a list here instead of a single 955 // userResponseData item 956 c.Check(rsp.Result, check.FitsTypeOf, []daemon.UserResponseData{}) 957 seen := map[string]bool{} 958 for _, u := range rsp.Result.([]daemon.UserResponseData) { 959 seen[u.Username] = true 960 c.Check(u, check.DeepEquals, daemon.UserResponseData{Username: u.Username}) 961 } 962 c.Check(seen, check.DeepEquals, map[string]bool{ 963 "guy": true, 964 "partnerguy": true, 965 "goodserialguy": true, 966 }) 967 968 // ensure the user was added to the state 969 st := s.d.Overlord().State() 970 st.Lock() 971 users, err := auth.Users(st) 972 c.Assert(err, check.IsNil) 973 st.Unlock() 974 c.Check(users, check.HasLen, 3) 975 } 976 977 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownClassicErrors(c *check.C) { 978 restore := release.MockOnClassic(true) 979 defer restore() 980 981 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 982 983 // do it! 984 buf := bytes.NewBufferString(`{"known":true}`) 985 req, err := http.NewRequest("POST", "/v2/create-user", buf) 986 c.Assert(err, check.IsNil) 987 988 rsp := s.errorReq(c, req, nil) 989 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user: device is a classic system`) 990 } 991 992 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwnedErrors(c *check.C) { 993 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 994 995 st := s.d.Overlord().State() 996 st.Lock() 997 _, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"}) 998 st.Unlock() 999 c.Check(err, check.IsNil) 1000 1001 // do it! 1002 buf := bytes.NewBufferString(`{"known":true}`) 1003 req, err := http.NewRequest("POST", "/v2/create-user", buf) 1004 c.Assert(err, check.IsNil) 1005 1006 rsp := s.errorReq(c, req, nil) 1007 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user: device already managed`) 1008 } 1009 1010 func (s *userSuite) TestPostCreateUserAutomaticManagedDoesNotActOrError(c *check.C) { 1011 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 1012 1013 st := s.d.Overlord().State() 1014 st.Lock() 1015 _, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"}) 1016 st.Unlock() 1017 c.Check(err, check.IsNil) 1018 1019 // do it! 1020 buf := bytes.NewBufferString(`{"automatic":true}`) 1021 req, err := http.NewRequest("POST", "/v2/create-user", buf) 1022 c.Assert(err, check.IsNil) 1023 1024 rsp := s.syncReq(c, req, nil) 1025 1026 // expecting an empty reply 1027 expected := []daemon.UserResponseData{} 1028 c.Check(rsp.Result, check.FitsTypeOf, expected) 1029 c.Check(rsp.Result, check.DeepEquals, expected) 1030 } 1031 1032 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownNoModelError(c *check.C) { 1033 restore := release.MockOnClassic(false) 1034 defer restore() 1035 1036 st := s.d.Overlord().State() 1037 // have not model yet 1038 st.Lock() 1039 err := devicestatetest.SetDevice(st, &auth.DeviceState{}) 1040 st.Unlock() 1041 c.Assert(err, check.IsNil) 1042 1043 // do it! 1044 buf := bytes.NewBufferString(`{"known":true}`) 1045 req, err := http.NewRequest("POST", "/v2/create-user", buf) 1046 c.Assert(err, check.IsNil) 1047 1048 rsp := s.errorReq(c, req, nil) 1049 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user: cannot get model assertion: no state entry for key`) 1050 } 1051 1052 func (s *userSuite) TestPostCreateUserFromAssertionNoModel(c *check.C) { 1053 restore := release.MockOnClassic(false) 1054 defer restore() 1055 1056 s.makeSystemUsers(c, []map[string]interface{}{serialUser}) 1057 model := s.Brands.Model("my-brand", "other-model", map[string]interface{}{ 1058 "architecture": "amd64", 1059 "gadget": "pc", 1060 "kernel": "pc-kernel", 1061 "system-user-authority": []interface{}{"my-brand", "partner"}, 1062 }) 1063 1064 st := s.d.Overlord().State() 1065 st.Lock() 1066 assertstatetest.AddMany(st, model) 1067 err := devicestatetest.SetDevice(st, &auth.DeviceState{ 1068 Brand: "my-brand", 1069 Model: "my-model", 1070 Serial: "other-serial-assertion", 1071 }) 1072 st.Unlock() 1073 c.Assert(err, check.IsNil) 1074 1075 // do it! 1076 buf := bytes.NewBufferString(`{"email":"serial@bar.com", "known":true}`) 1077 req, err := http.NewRequest("POST", "/v2/create-user", buf) 1078 c.Assert(err, check.IsNil) 1079 1080 rsp := s.errorReq(c, req, nil) 1081 c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot add system-user "serial@bar.com": bound to serial assertion but device not yet registered`) 1082 } 1083 1084 func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwned(c *check.C) { 1085 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 1086 1087 st := s.d.Overlord().State() 1088 st.Lock() 1089 _, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"}) 1090 st.Unlock() 1091 c.Check(err, check.IsNil) 1092 1093 // mock the calls that create the user 1094 created := map[string]bool{} 1095 defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error { 1096 c.Check(username, check.Equals, "guy") 1097 c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy") 1098 c.Check(opts.Sudoer, check.Equals, false) 1099 c.Check(opts.Password, check.Equals, "$6$salt$hash") 1100 created[username] = true 1101 return nil 1102 })() 1103 // make sure we report them as non-existing until created 1104 defer daemon.MockUserLookup(func(username string) (*user.User, error) { 1105 if created[username] { 1106 return s.trivialUserLookup(username) 1107 } 1108 return nil, fmt.Errorf("not created yet") 1109 })() 1110 1111 // do it! 1112 buf := bytes.NewBufferString(`{"known":true,"force-managed":true}`) 1113 req, err := http.NewRequest("POST", "/v2/create-user", buf) 1114 c.Assert(err, check.IsNil) 1115 1116 rsp := s.syncReq(c, req, nil) 1117 1118 // note that we get a list here instead of a single 1119 // userResponseData item 1120 expected := []daemon.UserResponseData{ 1121 {Username: "guy"}, 1122 } 1123 c.Check(rsp.Result, check.FitsTypeOf, expected) 1124 c.Check(rsp.Result, check.DeepEquals, expected) 1125 } 1126 1127 func (s *userSuite) TestPostCreateUserAutomaticDisabled(c *check.C) { 1128 s.makeSystemUsers(c, []map[string]interface{}{goodUser}) 1129 1130 // disable automatic user creation 1131 st := s.d.Overlord().State() 1132 st.Lock() 1133 tr := config.NewTransaction(st) 1134 err := tr.Set("core", "users.create.automatic", false) 1135 tr.Commit() 1136 st.Unlock() 1137 c.Assert(err, check.IsNil) 1138 1139 defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error { 1140 // we should not reach here 1141 panic("no user should be created") 1142 })() 1143 // make sure we report them as non-existing until created 1144 defer daemon.MockUserLookup(func(username string) (*user.User, error) { 1145 // this error would simply be interpreted as need to create 1146 return nil, fmt.Errorf("not created yet") 1147 })() 1148 1149 // do it! 1150 buf := bytes.NewBufferString(`{"automatic": true}`) 1151 req, err := http.NewRequest("POST", "/v2/create-user", buf) 1152 c.Assert(err, check.IsNil) 1153 1154 rsp := s.syncReq(c, req, nil) 1155 1156 // empty result 1157 expected := []daemon.UserResponseData{} 1158 c.Check(rsp.Result, check.FitsTypeOf, expected) 1159 c.Check(rsp.Result, check.DeepEquals, expected) 1160 1161 // ensure no user was added to the state 1162 st.Lock() 1163 users, err := auth.Users(st) 1164 c.Assert(err, check.IsNil) 1165 st.Unlock() 1166 c.Check(users, check.HasLen, 0) 1167 } 1168 1169 func (s *userSuite) TestUsersEmpty(c *check.C) { 1170 req, err := http.NewRequest("GET", "/v2/users", nil) 1171 c.Assert(err, check.IsNil) 1172 1173 rsp := s.syncReq(c, req, nil) 1174 1175 expected := []daemon.UserResponseData{} 1176 c.Check(rsp.Result, check.FitsTypeOf, expected) 1177 c.Check(rsp.Result, check.DeepEquals, expected) 1178 } 1179 1180 func (s *userSuite) TestUsersHasUser(c *check.C) { 1181 st := s.d.Overlord().State() 1182 st.Lock() 1183 u, err := auth.NewUser(st, "someuser", "mymail@test.com", "macaroon", []string{"discharge"}) 1184 st.Unlock() 1185 c.Assert(err, check.IsNil) 1186 1187 req, err := http.NewRequest("GET", "/v2/users", nil) 1188 c.Assert(err, check.IsNil) 1189 1190 rsp := s.syncReq(c, req, nil) 1191 1192 expected := []daemon.UserResponseData{ 1193 {ID: u.ID, Username: u.Username, Email: u.Email}, 1194 } 1195 c.Check(rsp.Result, check.FitsTypeOf, expected) 1196 c.Check(rsp.Result, check.DeepEquals, expected) 1197 }