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