github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/admin_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver_test 5 6 import ( 7 "fmt" 8 "net" 9 "net/url" 10 "strconv" 11 "sync" 12 "time" 13 14 "github.com/juju/errors" 15 "github.com/juju/loggo" 16 jc "github.com/juju/testing/checkers" 17 "github.com/juju/utils" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/names.v2" 20 "gopkg.in/macaroon-bakery.v1/httpbakery" 21 22 "github.com/juju/juju/api" 23 apimachiner "github.com/juju/juju/api/machiner" 24 apitesting "github.com/juju/juju/api/testing" 25 "github.com/juju/juju/apiserver" 26 "github.com/juju/juju/apiserver/common" 27 "github.com/juju/juju/apiserver/controller" 28 "github.com/juju/juju/apiserver/params" 29 "github.com/juju/juju/constraints" 30 jujutesting "github.com/juju/juju/juju/testing" 31 "github.com/juju/juju/network" 32 "github.com/juju/juju/permission" 33 "github.com/juju/juju/rpc" 34 "github.com/juju/juju/state" 35 coretesting "github.com/juju/juju/testing" 36 "github.com/juju/juju/testing/factory" 37 ) 38 39 type baseLoginSuite struct { 40 jujutesting.JujuConnSuite 41 setAdminAPI func(*apiserver.Server) 42 } 43 44 type loginSuite struct { 45 baseLoginSuite 46 } 47 48 var _ = gc.Suite(&loginSuite{ 49 baseLoginSuite{ 50 setAdminAPI: func(srv *apiserver.Server) { 51 apiserver.SetAdminAPIVersions(srv, 3) 52 }, 53 }, 54 }) 55 56 func (s *baseLoginSuite) SetUpTest(c *gc.C) { 57 s.JujuConnSuite.SetUpTest(c) 58 loggo.GetLogger("juju.apiserver").SetLogLevel(loggo.TRACE) 59 } 60 61 func (s *baseLoginSuite) newMachineAndServer(c *gc.C) (*api.Info, *apiserver.Server) { 62 machine, password := s.Factory.MakeMachineReturningPassword( 63 c, &factory.MachineParams{Nonce: "fake_nonce"}) 64 info, srv := newServer(c, s.State) 65 info.Tag = machine.Tag() 66 info.Password = password 67 info.Nonce = "fake_nonce" 68 return info, srv 69 } 70 71 func (s *loginSuite) TestLoginWithInvalidTag(c *gc.C) { 72 info := s.APIInfo(c) 73 info.Tag = nil 74 info.Password = "" 75 st := s.openAPIWithoutLogin(c, info) 76 77 request := ¶ms.LoginRequest{ 78 AuthTag: "bar", 79 Credentials: "password", 80 } 81 82 var response params.LoginResult 83 err := st.APICall("Admin", 3, "", "Login", request, &response) 84 c.Assert(err, gc.ErrorMatches, `.*"bar" is not a valid tag.*`) 85 } 86 87 func (s *loginSuite) TestBadLogin(c *gc.C) { 88 // Start our own server so we can control when the first login 89 // happens. Otherwise in JujuConnSuite.SetUpTest api.Open is 90 // called with user-admin permissions automatically. 91 info, srv := newServer(c, s.State) 92 defer assertStop(c, srv) 93 info.ModelTag = s.State.ModelTag() 94 95 adminUser := s.AdminUserTag(c) 96 97 for i, t := range []struct { 98 tag names.Tag 99 password string 100 err error 101 code string 102 }{{ 103 tag: adminUser, 104 password: "wrong password", 105 err: &rpc.RequestError{ 106 Message: "invalid entity name or password", 107 Code: "unauthorized access", 108 }, 109 code: params.CodeUnauthorized, 110 }, { 111 tag: names.NewUserTag("unknown"), 112 password: "password", 113 err: &rpc.RequestError{ 114 Message: "invalid entity name or password", 115 Code: "unauthorized access", 116 }, 117 code: params.CodeUnauthorized, 118 }} { 119 c.Logf("test %d; entity %q; password %q", i, t.tag, t.password) 120 func() { 121 // Open the API without logging in, so we can perform 122 // operations on the connection before calling Login. 123 st := s.openAPIWithoutLogin(c, info) 124 125 _, err := apimachiner.NewState(st).Machine(names.NewMachineTag("0")) 126 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 127 Message: `unknown object type "Machiner"`, 128 Code: "not implemented", 129 }) 130 131 // Since these are user login tests, the nonce is empty. 132 err = st.Login(t.tag, t.password, "", nil) 133 c.Assert(errors.Cause(err), gc.DeepEquals, t.err) 134 c.Assert(params.ErrCode(err), gc.Equals, t.code) 135 136 _, err = apimachiner.NewState(st).Machine(names.NewMachineTag("0")) 137 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 138 Message: `unknown object type "Machiner"`, 139 Code: "not implemented", 140 }) 141 }() 142 } 143 } 144 145 func (s *loginSuite) TestLoginAsDeactivatedUser(c *gc.C) { 146 info, srv := newServer(c, s.State) 147 defer assertStop(c, srv) 148 info.ModelTag = s.State.ModelTag() 149 150 st := s.openAPIWithoutLogin(c, info) 151 password := "password" 152 u := s.Factory.MakeUser(c, &factory.UserParams{Password: password, Disabled: true}) 153 154 _, err := st.Client().Status([]string{}) 155 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 156 Message: `unknown object type "Client"`, 157 Code: "not implemented", 158 }) 159 160 // Since these are user login tests, the nonce is empty. 161 err = st.Login(u.Tag(), password, "", nil) 162 assertInvalidEntityPassword(c, err) 163 164 _, err = st.Client().Status([]string{}) 165 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 166 Message: `unknown object type "Client"`, 167 Code: "not implemented", 168 }) 169 } 170 171 func (s *baseLoginSuite) runLoginSetsLogIdentifier(c *gc.C) { 172 info, srv := newServer(c, s.State) 173 defer assertStop(c, srv) 174 175 machine, password := s.Factory.MakeMachineReturningPassword( 176 c, &factory.MachineParams{Nonce: "fake_nonce"}) 177 178 info.Tag = machine.Tag() 179 info.Password = password 180 info.Nonce = "fake_nonce" 181 182 apiConn, err := api.Open(info, fastDialOpts) 183 c.Assert(err, jc.ErrorIsNil) 184 defer apiConn.Close() 185 186 apiMachine, err := apimachiner.NewState(apiConn).Machine(machine.MachineTag()) 187 c.Assert(err, jc.ErrorIsNil) 188 c.Assert(apiMachine.Tag(), gc.Equals, machine.Tag()) 189 } 190 191 func (s *loginSuite) TestLoginAddrs(c *gc.C) { 192 info, srv := s.newMachineAndServer(c) 193 defer assertStop(c, srv) 194 195 err := s.State.SetAPIHostPorts(nil) 196 c.Assert(err, jc.ErrorIsNil) 197 198 // Initially just the address we connect with is returned, 199 // despite there being no APIHostPorts in state. 200 connectedAddr, hostPorts := s.loginHostPorts(c, info) 201 connectedAddrHost, connectedAddrPortString, err := net.SplitHostPort(connectedAddr) 202 c.Assert(err, jc.ErrorIsNil) 203 connectedAddrPort, err := strconv.Atoi(connectedAddrPortString) 204 c.Assert(err, jc.ErrorIsNil) 205 connectedAddrHostPorts := [][]network.HostPort{ 206 network.NewHostPorts(connectedAddrPort, connectedAddrHost), 207 } 208 c.Assert(hostPorts, gc.DeepEquals, connectedAddrHostPorts) 209 210 // After storing APIHostPorts in state, Login should store 211 // all of them and the address we connected with. 212 server1Addresses := []network.Address{{ 213 Value: "server-1", 214 Type: network.HostName, 215 Scope: network.ScopePublic, 216 }, { 217 Value: "10.0.0.1", 218 Type: network.IPv4Address, 219 Scope: network.ScopeCloudLocal, 220 }} 221 server2Addresses := []network.Address{{ 222 Value: "::1", 223 Type: network.IPv6Address, 224 Scope: network.ScopeMachineLocal, 225 }} 226 stateAPIHostPorts := [][]network.HostPort{ 227 network.AddressesWithPort(server1Addresses, 123), 228 network.AddressesWithPort(server2Addresses, 456), 229 } 230 err = s.State.SetAPIHostPorts(stateAPIHostPorts) 231 c.Assert(err, jc.ErrorIsNil) 232 _, hostPorts = s.loginHostPorts(c, info) 233 // Now that we connected, we add the other stateAPIHostPorts. However, 234 // the one we connected to comes first. 235 stateAPIHostPorts = append(connectedAddrHostPorts, stateAPIHostPorts...) 236 c.Assert(hostPorts, gc.DeepEquals, stateAPIHostPorts) 237 } 238 239 func (s *baseLoginSuite) loginHostPorts(c *gc.C, info *api.Info) (connectedAddr string, hostPorts [][]network.HostPort) { 240 st, err := api.Open(info, fastDialOpts) 241 c.Assert(err, jc.ErrorIsNil) 242 defer st.Close() 243 return st.Addr(), st.APIHostPorts() 244 } 245 246 func startNLogins(c *gc.C, n int, info *api.Info) (chan error, *sync.WaitGroup) { 247 errResults := make(chan error, 100) 248 var doneWG sync.WaitGroup 249 var startedWG sync.WaitGroup 250 c.Logf("starting %d concurrent logins to %v", n, info.Addrs) 251 for i := 0; i < n; i++ { 252 i := i 253 c.Logf("starting login request %d", i) 254 startedWG.Add(1) 255 doneWG.Add(1) 256 go func() { 257 c.Logf("started login %d", i) 258 startedWG.Done() 259 st, err := api.Open(info, fastDialOpts) 260 errResults <- err 261 if err == nil { 262 st.Close() 263 } 264 doneWG.Done() 265 c.Logf("finished login %d: %v", i, err) 266 }() 267 } 268 startedWG.Wait() 269 return errResults, &doneWG 270 } 271 272 func (s *loginSuite) TestDelayLogins(c *gc.C) { 273 info, srv := s.newMachineAndServer(c) 274 defer assertStop(c, srv) 275 delayChan, cleanup := apiserver.DelayLogins() 276 defer cleanup() 277 278 // numConcurrentLogins is how many logins will fire off simultaneously. 279 // It doesn't really matter, as long as it is less than LoginRateLimit 280 const numConcurrentLogins = 5 281 c.Assert(numConcurrentLogins, jc.LessThan, apiserver.LoginRateLimit) 282 // Trigger a bunch of login requests 283 errResults, wg := startNLogins(c, numConcurrentLogins, info) 284 select { 285 case err := <-errResults: 286 c.Fatalf("we should not have gotten any logins yet: %v", err) 287 case <-time.After(coretesting.ShortWait): 288 } 289 // Allow one login to proceed 290 c.Logf("letting one login through") 291 select { 292 case delayChan <- struct{}{}: 293 default: 294 c.Fatalf("we should have been able to unblock a login") 295 } 296 select { 297 case err := <-errResults: 298 c.Check(err, jc.ErrorIsNil) 299 case <-time.After(coretesting.LongWait): 300 c.Fatalf("timed out while waiting for Login to finish") 301 } 302 c.Logf("checking no other logins succeeded") 303 // It should have only let 1 login through 304 select { 305 case err := <-errResults: 306 c.Fatalf("we should not have gotten more logins: %v", err) 307 case <-time.After(coretesting.ShortWait): 308 } 309 // Now allow the rest of the logins to proceed 310 c.Logf("letting %d logins through", numConcurrentLogins-1) 311 for i := 0; i < numConcurrentLogins-1; i++ { 312 delayChan <- struct{}{} 313 } 314 c.Logf("waiting for Logins to finish") 315 wg.Wait() 316 close(errResults) 317 successCount := 0 318 for err := range errResults { 319 c.Check(err, jc.ErrorIsNil) 320 if err == nil { 321 successCount += 1 322 } 323 } 324 // All the logins should succeed, they were just delayed after 325 // connecting. 326 c.Check(successCount, gc.Equals, numConcurrentLogins-1) 327 c.Logf("done") 328 } 329 330 func (s *loginSuite) TestLoginRateLimited(c *gc.C) { 331 info, srv := s.newMachineAndServer(c) 332 defer assertStop(c, srv) 333 delayChan, cleanup := apiserver.DelayLogins() 334 defer cleanup() 335 336 // Start enough concurrent Login requests so that we max out our 337 // LoginRateLimit. Do one extra so we know we are in overload 338 errResults, wg := startNLogins(c, apiserver.LoginRateLimit+1, info) 339 select { 340 case err := <-errResults: 341 c.Check(err, jc.Satisfies, params.IsCodeTryAgain) 342 case <-time.After(coretesting.LongWait): 343 c.Fatalf("timed out waiting for login to get rejected.") 344 } 345 346 // Let one request through, we should see that it succeeds without 347 // error, and then be able to start a new request, but it will block 348 delayChan <- struct{}{} 349 select { 350 case err := <-errResults: 351 c.Check(err, jc.ErrorIsNil) 352 case <-time.After(coretesting.LongWait): 353 c.Fatalf("timed out expecting one login to succeed") 354 } 355 chOne := make(chan error, 1) 356 wg.Add(1) 357 go func() { 358 st, err := api.Open(info, fastDialOpts) 359 chOne <- err 360 if err == nil { 361 st.Close() 362 } 363 wg.Done() 364 }() 365 select { 366 case err := <-chOne: 367 c.Fatalf("the open request should not have completed: %v", err) 368 case <-time.After(coretesting.ShortWait): 369 } 370 // Let all the logins finish. We started with LoginRateLimit, let one 371 // proceed, but we issued another one, so there should be 372 // LoginRateLimit logins pending. 373 for i := 0; i < apiserver.LoginRateLimit; i++ { 374 delayChan <- struct{}{} 375 } 376 wg.Wait() 377 close(errResults) 378 for err := range errResults { 379 c.Check(err, jc.ErrorIsNil) 380 } 381 } 382 383 func (s *loginSuite) TestUsersLoginWhileRateLimited(c *gc.C) { 384 info, srv := s.newMachineAndServer(c) 385 defer assertStop(c, srv) 386 delayChan, cleanup := apiserver.DelayLogins() 387 defer cleanup() 388 389 // Start enough concurrent Login requests so that we max out our 390 // LoginRateLimit. Do one extra so we know we are in overload 391 machineResults, machineWG := startNLogins(c, apiserver.LoginRateLimit+1, info) 392 select { 393 case err := <-machineResults: 394 c.Check(err, jc.Satisfies, params.IsCodeTryAgain) 395 case <-time.After(coretesting.LongWait): 396 c.Fatalf("timed out waiting for login to get rejected.") 397 } 398 399 userInfo := *info 400 userInfo.Tag = s.AdminUserTag(c) 401 userInfo.Password = "dummy-secret" 402 userResults, userWG := startNLogins(c, apiserver.LoginRateLimit+1, &userInfo) 403 // all of them should have started, and none of them in TryAgain state 404 select { 405 case err := <-userResults: 406 c.Fatalf("we should not have gotten any logins yet: %v", err) 407 case <-time.After(coretesting.ShortWait): 408 } 409 totalLogins := apiserver.LoginRateLimit*2 + 1 410 for i := 0; i < totalLogins; i++ { 411 delayChan <- struct{}{} 412 } 413 machineWG.Wait() 414 close(machineResults) 415 userWG.Wait() 416 close(userResults) 417 machineCount := 0 418 for err := range machineResults { 419 machineCount += 1 420 c.Check(err, jc.ErrorIsNil) 421 } 422 c.Check(machineCount, gc.Equals, apiserver.LoginRateLimit) 423 userCount := 0 424 for err := range userResults { 425 userCount += 1 426 c.Check(err, jc.ErrorIsNil) 427 } 428 c.Check(userCount, gc.Equals, apiserver.LoginRateLimit+1) 429 } 430 431 func (s *loginSuite) TestUsersAreNotRateLimited(c *gc.C) { 432 info, srv := newServer(c, s.State) 433 defer assertStop(c, srv) 434 435 info.Tag = s.AdminUserTag(c) 436 info.Password = "dummy-secret" 437 info.ModelTag = s.State.ModelTag() 438 439 delayChan, cleanup := apiserver.DelayLogins() 440 defer cleanup() 441 // We can login more than LoginRateLimit users 442 nLogins := apiserver.LoginRateLimit * 2 443 errResults, wg := startNLogins(c, nLogins, info) 444 select { 445 case err := <-errResults: 446 c.Fatalf("we should not have gotten any logins yet: %v", err) 447 case <-time.After(coretesting.ShortWait): 448 } 449 c.Logf("letting %d logins complete", nLogins) 450 for i := 0; i < nLogins; i++ { 451 delayChan <- struct{}{} 452 } 453 c.Logf("waiting for original requests to finish") 454 wg.Wait() 455 close(errResults) 456 for err := range errResults { 457 c.Check(err, jc.ErrorIsNil) 458 } 459 } 460 461 func (s *loginSuite) TestNonModelUserLoginFails(c *gc.C) { 462 info, srv := newServer(c, s.State) 463 defer assertStop(c, srv) 464 info.ModelTag = s.State.ModelTag() 465 user := s.Factory.MakeUser(c, &factory.UserParams{Password: "dummy-password", NoModelUser: true}) 466 ctag := names.NewControllerTag(s.State.ControllerUUID()) 467 err := s.State.RemoveUserAccess(user.UserTag(), ctag) 468 c.Assert(err, jc.ErrorIsNil) 469 info.Password = "dummy-password" 470 info.Tag = user.UserTag() 471 _, err = api.Open(info, fastDialOpts) 472 assertInvalidEntityPassword(c, err) 473 } 474 475 func (s *loginSuite) TestLoginValidationSuccess(c *gc.C) { 476 validator := func(params.LoginRequest) error { 477 return nil 478 } 479 checker := func(c *gc.C, loginErr error, st api.Connection) { 480 c.Assert(loginErr, gc.IsNil) 481 482 // Ensure an API call that would be restricted during 483 // upgrades works after a normal login. 484 err := st.APICall("Client", 1, "", "ModelSet", params.ModelSet{}, nil) 485 c.Assert(err, jc.ErrorIsNil) 486 } 487 s.checkLoginWithValidator(c, validator, checker) 488 } 489 490 func (s *loginSuite) TestLoginValidationFail(c *gc.C) { 491 validator := func(params.LoginRequest) error { 492 return errors.New("Login not allowed") 493 } 494 checker := func(c *gc.C, loginErr error, _ api.Connection) { 495 // error is wrapped in API server 496 c.Assert(loginErr, gc.ErrorMatches, "Login not allowed") 497 } 498 s.checkLoginWithValidator(c, validator, checker) 499 } 500 501 func (s *loginSuite) TestLoginValidationDuringUpgrade(c *gc.C) { 502 validator := func(params.LoginRequest) error { 503 return params.UpgradeInProgressError 504 } 505 checker := func(c *gc.C, loginErr error, st api.Connection) { 506 c.Assert(loginErr, gc.IsNil) 507 508 var statusResult params.FullStatus 509 err := st.APICall("Client", 1, "", "FullStatus", params.StatusParams{}, &statusResult) 510 c.Assert(err, jc.ErrorIsNil) 511 512 err = st.APICall("Client", 1, "", "ModelSet", params.ModelSet{}, nil) 513 c.Assert(err, jc.Satisfies, params.IsCodeUpgradeInProgress) 514 } 515 s.checkLoginWithValidator(c, validator, checker) 516 } 517 518 func (s *loginSuite) TestFailedLoginDuringMaintenance(c *gc.C) { 519 cfg := defaultServerConfig(c) 520 cfg.Validator = func(params.LoginRequest) error { 521 return errors.New("something") 522 } 523 info, srv := newServerWithConfig(c, s.State, cfg) 524 defer assertStop(c, srv) 525 info.ModelTag = s.State.ModelTag() 526 527 checkLogin := func(tag names.Tag) { 528 st := s.openAPIWithoutLogin(c, info) 529 err := st.Login(tag, "dummy-secret", "nonce", nil) 530 c.Assert(err, gc.ErrorMatches, "something") 531 } 532 checkLogin(names.NewUserTag("definitelywontexist")) 533 checkLogin(names.NewMachineTag("99999")) 534 } 535 536 type validationChecker func(c *gc.C, err error, st api.Connection) 537 538 func (s *baseLoginSuite) checkLoginWithValidator(c *gc.C, validator apiserver.LoginValidator, checker validationChecker) { 539 cfg := defaultServerConfig(c) 540 cfg.Validator = validator 541 info, srv := newServerWithConfig(c, s.State, cfg) 542 defer assertStop(c, srv) 543 info.ModelTag = s.State.ModelTag() 544 545 st := s.openAPIWithoutLogin(c, info) 546 547 // Ensure not already logged in. 548 _, err := apimachiner.NewState(st).Machine(names.NewMachineTag("0")) 549 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 550 Message: `unknown object type "Machiner"`, 551 Code: "not implemented", 552 }) 553 554 adminUser := s.AdminUserTag(c) 555 // Since these are user login tests, the nonce is empty. 556 err = st.Login(adminUser, "dummy-secret", "", nil) 557 558 checker(c, err, st) 559 } 560 561 func (s *baseLoginSuite) openAPIWithoutLogin(c *gc.C, info0 *api.Info) api.Connection { 562 info := *info0 563 info.Tag = nil 564 info.Password = "" 565 info.SkipLogin = true 566 st, err := api.Open(&info, fastDialOpts) 567 c.Assert(err, jc.ErrorIsNil) 568 s.AddCleanup(func(*gc.C) { st.Close() }) 569 return st 570 } 571 572 func (s *loginSuite) TestControllerModel(c *gc.C) { 573 info, srv := newServer(c, s.State) 574 defer assertStop(c, srv) 575 576 info.ModelTag = s.State.ModelTag() 577 st := s.openAPIWithoutLogin(c, info) 578 579 adminUser := s.AdminUserTag(c) 580 err := st.Login(adminUser, "dummy-secret", "", nil) 581 c.Assert(err, jc.ErrorIsNil) 582 583 s.assertRemoteModel(c, st, s.State.ModelTag()) 584 } 585 586 func (s *loginSuite) TestControllerModelBadCreds(c *gc.C) { 587 info, srv := newServer(c, s.State) 588 defer assertStop(c, srv) 589 590 info.ModelTag = s.State.ModelTag() 591 st := s.openAPIWithoutLogin(c, info) 592 593 adminUser := s.AdminUserTag(c) 594 err := st.Login(adminUser, "bad-password", "", nil) 595 assertInvalidEntityPassword(c, err) 596 } 597 598 func (s *loginSuite) TestNonExistentModel(c *gc.C) { 599 info, srv := newServer(c, s.State) 600 defer assertStop(c, srv) 601 602 uuid, err := utils.NewUUID() 603 c.Assert(err, jc.ErrorIsNil) 604 info.ModelTag = names.NewModelTag(uuid.String()) 605 st := s.openAPIWithoutLogin(c, info) 606 607 adminUser := s.AdminUserTag(c) 608 err = st.Login(adminUser, "dummy-secret", "", nil) 609 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 610 Message: fmt.Sprintf("unknown model: %q", uuid), 611 Code: "model not found", 612 }) 613 } 614 615 func (s *loginSuite) TestInvalidModel(c *gc.C) { 616 info, srv := newServer(c, s.State) 617 defer assertStop(c, srv) 618 info.ModelTag = names.NewModelTag("rubbish") 619 620 st := s.openAPIWithoutLogin(c, info) 621 622 adminUser := s.AdminUserTag(c) 623 err := st.Login(adminUser, "dummy-secret", "", nil) 624 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 625 Message: `unknown model: "rubbish"`, 626 Code: "model not found", 627 }) 628 } 629 630 func (s *loginSuite) TestOtherModel(c *gc.C) { 631 info, srv := newServer(c, s.State) 632 defer assertStop(c, srv) 633 634 envOwner := s.Factory.MakeUser(c, nil) 635 envState := s.Factory.MakeModel(c, &factory.ModelParams{ 636 Owner: envOwner.UserTag(), 637 }) 638 defer envState.Close() 639 info.ModelTag = envState.ModelTag() 640 st := s.openAPIWithoutLogin(c, info) 641 642 err := st.Login(envOwner.UserTag(), "password", "", nil) 643 c.Assert(err, jc.ErrorIsNil) 644 s.assertRemoteModel(c, st, envState.ModelTag()) 645 } 646 647 func (s *loginSuite) TestMachineLoginOtherModel(c *gc.C) { 648 // User credentials are checked against a global user list. 649 // Machine credentials are checked against environment specific 650 // machines, so this makes sure that the credential checking is 651 // using the correct state connection. 652 info, srv := newServer(c, s.State) 653 defer assertStop(c, srv) 654 655 envOwner := s.Factory.MakeUser(c, nil) 656 envState := s.Factory.MakeModel(c, &factory.ModelParams{ 657 Owner: envOwner.UserTag(), 658 ConfigAttrs: map[string]interface{}{ 659 "controller": false, 660 }, 661 }) 662 defer envState.Close() 663 664 f2 := factory.NewFactory(envState) 665 machine, password := f2.MakeMachineReturningPassword(c, &factory.MachineParams{ 666 Nonce: "nonce", 667 }) 668 669 info.ModelTag = envState.ModelTag() 670 st := s.openAPIWithoutLogin(c, info) 671 672 err := st.Login(machine.Tag(), password, "nonce", nil) 673 c.Assert(err, jc.ErrorIsNil) 674 } 675 676 func (s *loginSuite) TestMachineLoginOtherModelNotProvisioned(c *gc.C) { 677 info, srv := newServer(c, s.State) 678 defer assertStop(c, srv) 679 680 envOwner := s.Factory.MakeUser(c, nil) 681 envState := s.Factory.MakeModel(c, &factory.ModelParams{ 682 Owner: envOwner.UserTag(), 683 ConfigAttrs: map[string]interface{}{ 684 "controller": false, 685 }, 686 }) 687 defer envState.Close() 688 689 f2 := factory.NewFactory(envState) 690 machine, password := f2.MakeUnprovisionedMachineReturningPassword(c, &factory.MachineParams{}) 691 692 info.ModelTag = envState.ModelTag() 693 st := s.openAPIWithoutLogin(c, info) 694 695 // If the agent attempts Login before the provisioner has recorded 696 // the machine's nonce in state, then the agent should get back an 697 // error with code "not provisioned". 698 err := st.Login(machine.Tag(), password, "nonce", nil) 699 c.Assert(err, gc.ErrorMatches, `machine 0 not provisioned \(not provisioned\)`) 700 c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned) 701 } 702 703 func (s *loginSuite) TestOtherEnvironmentFromController(c *gc.C) { 704 info, srv := newServer(c, s.State) 705 defer assertStop(c, srv) 706 707 machine, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 708 Jobs: []state.MachineJob{state.JobManageModel}, 709 }) 710 711 envState := s.Factory.MakeModel(c, nil) 712 defer envState.Close() 713 info.ModelTag = envState.ModelTag() 714 st := s.openAPIWithoutLogin(c, info) 715 716 err := st.Login(machine.Tag(), password, "nonce", nil) 717 c.Assert(err, jc.ErrorIsNil) 718 } 719 720 func (s *loginSuite) TestOtherEnvironmentFromControllerOtherNotProvisioned(c *gc.C) { 721 info, srv := newServer(c, s.State) 722 defer assertStop(c, srv) 723 724 managerMachine, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 725 Jobs: []state.MachineJob{state.JobManageModel}, 726 }) 727 728 // Create a hosted model with an unprovisioned machine that has the 729 // same tag as the manager machine. 730 hostedModelState := s.Factory.MakeModel(c, nil) 731 defer hostedModelState.Close() 732 f2 := factory.NewFactory(hostedModelState) 733 workloadMachine, _ := f2.MakeUnprovisionedMachineReturningPassword(c, &factory.MachineParams{}) 734 c.Assert(managerMachine.Tag(), gc.Equals, workloadMachine.Tag()) 735 736 info.ModelTag = hostedModelState.ModelTag() 737 st := s.openAPIWithoutLogin(c, info) 738 739 // The fact that the machine with the same tag in the hosted 740 // model is unprovisioned should not cause the login to fail 741 // with "not provisioned", because the passwords don't match. 742 err := st.Login(managerMachine.Tag(), password, "nonce", nil) 743 c.Assert(err, jc.ErrorIsNil) 744 } 745 746 func (s *loginSuite) TestOtherEnvironmentWhenNotController(c *gc.C) { 747 info, srv := newServer(c, s.State) 748 defer assertStop(c, srv) 749 750 machine, password := s.Factory.MakeMachineReturningPassword(c, nil) 751 752 envState := s.Factory.MakeModel(c, nil) 753 defer envState.Close() 754 info.ModelTag = envState.ModelTag() 755 st := s.openAPIWithoutLogin(c, info) 756 757 err := st.Login(machine.Tag(), password, "nonce", nil) 758 assertPermissionDenied(c, err) 759 } 760 761 func (s *loginSuite) loginLocalUser(c *gc.C, info *api.Info) (*state.User, params.LoginResult) { 762 password := "shhh..." 763 user := s.Factory.MakeUser(c, &factory.UserParams{ 764 Password: password, 765 }) 766 conn := s.openAPIWithoutLogin(c, info) 767 768 var result params.LoginResult 769 request := ¶ms.LoginRequest{ 770 AuthTag: user.Tag().String(), 771 Credentials: password, 772 } 773 err := conn.APICall("Admin", 3, "", "Login", request, &result) 774 c.Assert(err, jc.ErrorIsNil) 775 c.Assert(result.UserInfo, gc.NotNil) 776 return user, result 777 } 778 779 func (s *loginSuite) TestLoginResultLocalUser(c *gc.C) { 780 info, srv := newServer(c, s.State) 781 defer assertStop(c, srv) 782 info.ModelTag = s.State.ModelTag() 783 784 user, result := s.loginLocalUser(c, info) 785 c.Check(result.UserInfo.Identity, gc.Equals, user.Tag().String()) 786 c.Check(result.UserInfo.ControllerAccess, gc.Equals, "login") 787 c.Check(result.UserInfo.ModelAccess, gc.Equals, "admin") 788 } 789 790 func (s *loginSuite) TestLoginResultLocalUserEveryoneCreateOnlyNonLocal(c *gc.C) { 791 info, srv := newServer(c, s.State) 792 defer assertStop(c, srv) 793 info.ModelTag = s.State.ModelTag() 794 795 setEveryoneAccess(c, s.State, s.AdminUserTag(c), permission.AddModelAccess) 796 797 user, result := s.loginLocalUser(c, info) 798 c.Check(result.UserInfo.Identity, gc.Equals, user.Tag().String()) 799 c.Check(result.UserInfo.ControllerAccess, gc.Equals, "login") 800 c.Check(result.UserInfo.ModelAccess, gc.Equals, "admin") 801 } 802 803 func (s *loginSuite) assertRemoteModel(c *gc.C, api api.Connection, expected names.ModelTag) { 804 // Look at what the api thinks it has. 805 tag, ok := api.ModelTag() 806 c.Assert(ok, jc.IsTrue) 807 c.Assert(tag, gc.Equals, expected) 808 // Look at what the api Client thinks it has. 809 client := api.Client() 810 811 // ModelUUID looks at the env tag on the api state connection. 812 uuid, ok := client.ModelUUID() 813 c.Assert(ok, jc.IsTrue) 814 c.Assert(uuid, gc.Equals, expected.Id()) 815 816 // The code below is to verify that the API connection is operating on 817 // the expected model. We make a change in state on that model, and 818 // then check that it is picked up by a call to the API. 819 820 st, err := s.State.ForModel(tag) 821 c.Assert(err, jc.ErrorIsNil) 822 defer st.Close() 823 824 expectedCons := constraints.MustParse("mem=8G") 825 err = st.SetModelConstraints(expectedCons) 826 c.Assert(err, jc.ErrorIsNil) 827 828 cons, err := client.GetModelConstraints() 829 c.Assert(err, jc.ErrorIsNil) 830 c.Assert(cons, jc.DeepEquals, expectedCons) 831 } 832 833 func (s *loginSuite) TestLoginUpdatesLastLoginAndConnection(c *gc.C) { 834 // Since the login and connection times truncate time to the second, 835 // we need to make sure our start time is just before now. 836 startTime := time.Now().Add(-time.Second) 837 838 password := "shhh..." 839 user := s.Factory.MakeUser(c, &factory.UserParams{ 840 Password: password, 841 }) 842 843 info := s.APIInfo(c) 844 info.Tag = user.Tag() 845 info.Password = password 846 apiState, err := api.Open(info, api.DialOpts{}) 847 c.Assert(err, jc.ErrorIsNil) 848 defer apiState.Close() 849 850 // The user now has last login updated. 851 err = user.Refresh() 852 c.Assert(err, jc.ErrorIsNil) 853 lastLogin, err := user.LastLogin() 854 c.Assert(err, jc.ErrorIsNil) 855 c.Assert(lastLogin, gc.NotNil) 856 c.Assert(lastLogin.After(startTime), jc.IsTrue) 857 858 // The env user is also updated. 859 modelUser, err := s.State.UserAccess(user.UserTag(), s.State.ModelTag()) 860 c.Assert(err, jc.ErrorIsNil) 861 when, err := s.State.LastModelConnection(modelUser.UserTag) 862 c.Assert(err, jc.ErrorIsNil) 863 c.Assert(when, gc.NotNil) 864 c.Assert(when.After(startTime), jc.IsTrue) 865 } 866 867 var _ = gc.Suite(&macaroonLoginSuite{}) 868 869 type macaroonLoginSuite struct { 870 apitesting.MacaroonSuite 871 } 872 873 func (s *macaroonLoginSuite) TestLoginToController(c *gc.C) { 874 // Note that currently we cannot use macaroon auth 875 // to log into the controller rather than an environment 876 // because there's no place to store the fact that 877 // a given external user is allowed access to the controller. 878 s.DischargerLogin = func() string { 879 return "test@somewhere" 880 } 881 info := s.APIInfo(c) 882 883 // Zero the environment tag so that we log into the controller 884 // not the environment. 885 info.ModelTag = names.ModelTag{} 886 887 client, err := api.Open(info, api.DialOpts{}) 888 assertInvalidEntityPassword(c, err) 889 c.Assert(client, gc.Equals, nil) 890 } 891 892 func (s *macaroonLoginSuite) login(c *gc.C, info *api.Info) (params.LoginResult, error) { 893 info.SkipLogin = true 894 895 cookieJar := apitesting.NewClearableCookieJar() 896 897 client := s.OpenAPI(c, info, cookieJar) 898 defer client.Close() 899 900 var ( 901 // Remote users start with an empty login request. 902 request params.LoginRequest 903 result params.LoginResult 904 ) 905 err := client.APICall("Admin", 3, "", "Login", &request, &result) 906 c.Assert(err, jc.ErrorIsNil) 907 908 cookieURL := &url.URL{ 909 Scheme: "https", 910 Host: "localhost", 911 Path: "/", 912 } 913 914 bakeryClient := httpbakery.NewClient() 915 916 err = bakeryClient.HandleError(cookieURL, &httpbakery.Error{ 917 Message: result.DischargeRequiredReason, 918 Code: httpbakery.ErrDischargeRequired, 919 Info: &httpbakery.ErrorInfo{ 920 Macaroon: result.DischargeRequired, 921 MacaroonPath: "/", 922 }, 923 }) 924 c.Assert(err, jc.ErrorIsNil) 925 // Add the macaroons that have been saved by HandleError to our login request. 926 request.Macaroons = httpbakery.MacaroonsForURL(bakeryClient.Client.Jar, cookieURL) 927 928 err = client.APICall("Admin", 3, "", "Login", &request, &result) 929 return result, err 930 } 931 932 func (s *macaroonLoginSuite) TestRemoteUserLoginToControllerNoAccess(c *gc.C) { 933 s.DischargerLogin = func() string { 934 return "test@somewhere" 935 } 936 info := s.APIInfo(c) 937 // Log in to the controller, not the model. 938 info.ModelTag = names.ModelTag{} 939 940 _, err := s.login(c, info) 941 assertInvalidEntityPassword(c, err) 942 } 943 944 func (s *macaroonLoginSuite) TestRemoteUserLoginToControllerLoginAccess(c *gc.C) { 945 setEveryoneAccess(c, s.State, s.AdminUserTag(c), permission.LoginAccess) 946 const remoteUser = "test@somewhere" 947 var remoteUserTag = names.NewUserTag(remoteUser) 948 949 s.DischargerLogin = func() string { 950 return remoteUser 951 } 952 info := s.APIInfo(c) 953 // Log in to the controller, not the model. 954 info.ModelTag = names.ModelTag{} 955 956 result, err := s.login(c, info) 957 c.Check(err, jc.ErrorIsNil) 958 c.Assert(result.UserInfo, gc.NotNil) 959 c.Check(result.UserInfo.Identity, gc.Equals, remoteUserTag.String()) 960 c.Check(result.UserInfo.ControllerAccess, gc.Equals, "login") 961 c.Check(result.UserInfo.ModelAccess, gc.Equals, "") 962 } 963 964 func (s *macaroonLoginSuite) TestRemoteUserLoginToControllerAddModelAccess(c *gc.C) { 965 setEveryoneAccess(c, s.State, s.AdminUserTag(c), permission.AddModelAccess) 966 const remoteUser = "test@somewhere" 967 var remoteUserTag = names.NewUserTag(remoteUser) 968 969 s.DischargerLogin = func() string { 970 return remoteUser 971 } 972 info := s.APIInfo(c) 973 // Log in to the controller, not the model. 974 info.ModelTag = names.ModelTag{} 975 976 result, err := s.login(c, info) 977 c.Check(err, jc.ErrorIsNil) 978 c.Assert(result.UserInfo, gc.NotNil) 979 c.Check(result.UserInfo.Identity, gc.Equals, remoteUserTag.String()) 980 c.Check(result.UserInfo.ControllerAccess, gc.Equals, "add-model") 981 c.Check(result.UserInfo.ModelAccess, gc.Equals, "") 982 } 983 984 func (s *macaroonLoginSuite) TestRemoteUserLoginToModelNoExplicitAccess(c *gc.C) { 985 // If we have a remote user which the controller knows nothing about, 986 // and the macaroon is discharged successfully, and the user is attempting 987 // to log into a model, that is permission denied. 988 setEveryoneAccess(c, s.State, s.AdminUserTag(c), permission.LoginAccess) 989 s.DischargerLogin = func() string { 990 return "test@somewhere" 991 } 992 info := s.APIInfo(c) 993 994 _, err := s.login(c, info) 995 assertPermissionDenied(c, err) 996 } 997 998 func (s *macaroonLoginSuite) TestRemoteUserLoginToModelWithExplicitAccess(c *gc.C) { 999 s.testRemoteUserLoginToModelWithExplicitAccess(c, false) 1000 } 1001 1002 func (s *macaroonLoginSuite) TestRemoteUserLoginToModelWithExplicitAccessAndAllowModelAccess(c *gc.C) { 1003 s.testRemoteUserLoginToModelWithExplicitAccess(c, true) 1004 } 1005 1006 func (s *macaroonLoginSuite) testRemoteUserLoginToModelWithExplicitAccess(c *gc.C, allowModelAccess bool) { 1007 cfg := defaultServerConfig(c) 1008 cfg.AllowModelAccess = allowModelAccess 1009 1010 info, srv := newServerWithConfig(c, s.State, cfg) 1011 defer assertStop(c, srv) 1012 info.ModelTag = s.State.ModelTag() 1013 1014 // If we have a remote user which has explict model access, but neither 1015 // controller access nor 'everyone' access, the user will have access 1016 // only if the AllowModelAccess configuration flag is true. 1017 const remoteUser = "test@somewhere" 1018 s.Factory.MakeModelUser(c, &factory.ModelUserParams{ 1019 User: remoteUser, 1020 1021 Access: permission.WriteAccess, 1022 }) 1023 s.DischargerLogin = func() string { 1024 return remoteUser 1025 } 1026 1027 _, err := s.login(c, info) 1028 if allowModelAccess { 1029 c.Assert(err, jc.ErrorIsNil) 1030 } else { 1031 assertPermissionDenied(c, err) 1032 } 1033 } 1034 1035 func (s *macaroonLoginSuite) TestRemoteUserLoginToModelWithControllerAccess(c *gc.C) { 1036 const remoteUser = "test@somewhere" 1037 var remoteUserTag = names.NewUserTag(remoteUser) 1038 s.Factory.MakeModelUser(c, &factory.ModelUserParams{ 1039 User: remoteUser, 1040 Access: permission.WriteAccess, 1041 }) 1042 s.AddControllerUser(c, remoteUser, permission.AddModelAccess) 1043 1044 s.DischargerLogin = func() string { 1045 return remoteUser 1046 } 1047 info := s.APIInfo(c) 1048 1049 result, err := s.login(c, info) 1050 c.Check(err, jc.ErrorIsNil) 1051 c.Assert(result.UserInfo, gc.NotNil) 1052 c.Check(result.UserInfo.Identity, gc.Equals, remoteUserTag.String()) 1053 c.Check(result.UserInfo.ControllerAccess, gc.Equals, "add-model") 1054 c.Check(result.UserInfo.ModelAccess, gc.Equals, "write") 1055 } 1056 1057 func (s *macaroonLoginSuite) TestLoginToEnvironmentSuccess(c *gc.C) { 1058 const remoteUser = "test@somewhere" 1059 s.AddModelUser(c, remoteUser) 1060 s.AddControllerUser(c, remoteUser, permission.LoginAccess) 1061 s.DischargerLogin = func() string { 1062 return "test@somewhere" 1063 } 1064 loggo.GetLogger("juju.apiserver").SetLogLevel(loggo.TRACE) 1065 client, err := api.Open(s.APIInfo(c), api.DialOpts{}) 1066 c.Assert(err, jc.ErrorIsNil) 1067 defer client.Close() 1068 1069 // The auth tag has been correctly returned by the server. 1070 c.Assert(client.AuthTag(), gc.Equals, names.NewUserTag(remoteUser)) 1071 } 1072 1073 func (s *macaroonLoginSuite) TestFailedToObtainDischargeLogin(c *gc.C) { 1074 s.DischargerLogin = func() string { 1075 return "" 1076 } 1077 client, err := api.Open(s.APIInfo(c), api.DialOpts{}) 1078 c.Assert(err, gc.ErrorMatches, `cannot get discharge from "https://.*": third party refused discharge: cannot discharge: login denied by discharger`) 1079 c.Assert(client, gc.Equals, nil) 1080 } 1081 1082 func (s *macaroonLoginSuite) TestUnknownUserLogin(c *gc.C) { 1083 s.DischargerLogin = func() string { 1084 return "testUnknown@somewhere" 1085 } 1086 client, err := api.Open(s.APIInfo(c), api.DialOpts{}) 1087 assertInvalidEntityPassword(c, err) 1088 c.Assert(client, gc.Equals, nil) 1089 } 1090 1091 func assertInvalidEntityPassword(c *gc.C, err error) { 1092 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 1093 Message: "invalid entity name or password", 1094 Code: "unauthorized access", 1095 }) 1096 } 1097 1098 func assertPermissionDenied(c *gc.C, err error) { 1099 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 1100 Message: "permission denied", 1101 Code: "unauthorized access", 1102 }) 1103 } 1104 1105 func setEveryoneAccess(c *gc.C, st *state.State, adminUser names.UserTag, access permission.Access) { 1106 err := controller.ChangeControllerAccess( 1107 st, adminUser, names.NewUserTag(common.EveryoneTagName), 1108 params.GrantControllerAccess, access) 1109 c.Assert(err, jc.ErrorIsNil) 1110 } 1111 1112 var _ = gc.Suite(&migrationSuite{ 1113 baseLoginSuite{ 1114 setAdminAPI: func(srv *apiserver.Server) { 1115 apiserver.SetAdminAPIVersions(srv, 3) 1116 }, 1117 }, 1118 }) 1119 1120 type migrationSuite struct { 1121 baseLoginSuite 1122 } 1123 1124 func (s *migrationSuite) TestImportingModel(c *gc.C) { 1125 m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 1126 Nonce: "nonce", 1127 }) 1128 model, err := s.State.Model() 1129 c.Assert(err, jc.ErrorIsNil) 1130 err = model.SetMigrationMode(state.MigrationModeImporting) 1131 c.Assert(err, jc.ErrorIsNil) 1132 1133 // Users should be able to log in but RPC requests should fail. 1134 info := s.APIInfo(c) 1135 userConn := s.OpenAPIAs(c, info.Tag, info.Password) 1136 defer userConn.Close() 1137 _, err = userConn.Client().Status(nil) 1138 c.Check(err, gc.ErrorMatches, "migration in progress, model is importing") 1139 1140 // Machines should be able to use the API. 1141 machineConn := s.OpenAPIAsMachine(c, m.Tag(), password, "nonce") 1142 defer machineConn.Close() 1143 _, err = apimachiner.NewState(machineConn).Machine(m.MachineTag()) 1144 c.Check(err, jc.ErrorIsNil) 1145 }