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