github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/admin.go (about) 1 // Copyright 2013, 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/utils/clock" 12 "gopkg.in/juju/names.v2" 13 14 "github.com/juju/juju/apiserver/authentication" 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/apiserver/observer" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/apiserver/presence" 19 "github.com/juju/juju/permission" 20 "github.com/juju/juju/rpc" 21 "github.com/juju/juju/rpc/rpcreflect" 22 "github.com/juju/juju/state" 23 statepresence "github.com/juju/juju/state/presence" 24 jujuversion "github.com/juju/juju/version" 25 ) 26 27 type adminAPIFactory func(*Server, *apiHandler, observer.Observer) interface{} 28 29 // admin is the only object that unlogged-in clients can access. It holds any 30 // methods that are needed to log in. 31 type admin struct { 32 srv *Server 33 root *apiHandler 34 apiObserver observer.Observer 35 36 mu sync.Mutex 37 loggedIn bool 38 } 39 40 var AboutToRestoreError = errors.New("restore preparation in progress") 41 var RestoreInProgressError = errors.New("restore in progress") 42 var MaintenanceNoLoginError = errors.New("login failed - maintenance in progress") 43 var errAlreadyLoggedIn = errors.New("already logged in") 44 45 // login is the internal version of the Login API call. 46 func (a *admin) login(req params.LoginRequest, loginVersion int) (params.LoginResult, error) { 47 var fail params.LoginResult 48 49 a.mu.Lock() 50 defer a.mu.Unlock() 51 if a.loggedIn { 52 // This can only happen if Login is called concurrently. 53 return fail, errAlreadyLoggedIn 54 } 55 56 // apiRoot is the API root exposed to the client after authentication. 57 var apiRoot rpc.Root = newAPIRoot(a.root.state, a.root.resources, a.root) 58 59 // Use the login validation function, if one was specified. 60 if a.srv.validator != nil { 61 err := a.srv.validator(req) 62 switch err { 63 case params.UpgradeInProgressError: 64 apiRoot = restrictRoot(apiRoot, upgradeMethodsOnly) 65 case AboutToRestoreError: 66 apiRoot = restrictRoot(apiRoot, aboutToRestoreMethodsOnly) 67 case RestoreInProgressError: 68 apiRoot = restrictAll(apiRoot, restoreInProgressError) 69 case nil: 70 // in this case no need to wrap authed api so we do nothing 71 default: 72 return fail, errors.Trace(err) 73 } 74 } 75 76 isUser := true 77 kind := names.UserTagKind 78 if req.AuthTag != "" { 79 var err error 80 kind, err = names.TagKind(req.AuthTag) 81 if err != nil || kind != names.UserTagKind { 82 isUser = false 83 // Users are not rate limited, all other entities are. 84 if !a.srv.limiter.Acquire() { 85 logger.Debugf("rate limiting for agent %s", req.AuthTag) 86 return fail, common.ErrTryAgain 87 } 88 defer a.srv.limiter.Release() 89 } 90 } 91 92 controllerOnlyLogin := a.root.modelUUID == "" 93 controllerMachineLogin := false 94 95 entity, lastConnection, err := a.checkCreds(req, isUser) 96 if err != nil { 97 if err, ok := errors.Cause(err).(*common.DischargeRequiredError); ok { 98 loginResult := params.LoginResult{ 99 DischargeRequired: err.Macaroon, 100 DischargeRequiredReason: err.Error(), 101 } 102 logger.Infof("login failed with discharge-required error: %v", err) 103 return loginResult, nil 104 } 105 if a.maintenanceInProgress() { 106 // An upgrade, restore or similar operation is in 107 // progress. It is possible for logins to fail until this 108 // is complete due to incomplete or updating data. Mask 109 // transitory and potentially confusing errors from failed 110 // logins with a more helpful one. 111 return fail, MaintenanceNoLoginError 112 } 113 // Here we have a special case. The machine agents that manage 114 // models in the controller model need to be able to 115 // open API connections to other models. In those cases, we 116 // need to look in the controller database to check the creds 117 // against the machine if and only if the entity tag is a machine tag, 118 // and the machine exists in the controller model, and the 119 // machine has the manage state job. If all those parts are valid, we 120 // can then check the credentials against the controller model 121 // machine. 122 if kind != names.MachineTagKind { 123 return fail, errors.Trace(err) 124 } 125 if errors.Cause(err) != common.ErrBadCreds { 126 return fail, err 127 } 128 entity, err = a.checkControllerMachineCreds(req) 129 if err != nil { 130 return fail, errors.Trace(err) 131 } 132 // If we are here, then the entity will refer to a controller 133 // machine in the controller model, and we don't need a pinger 134 // for it as we already have one running in the machine agent api 135 // worker for the controller model. 136 controllerMachineLogin = true 137 } 138 a.root.entity = entity 139 a.apiObserver.Login(entity.Tag(), a.root.state.ModelTag(), controllerMachineLogin, req.UserData) 140 141 // We have authenticated the user; enable the appropriate API 142 // to serve to them. 143 a.loggedIn = true 144 145 if !controllerMachineLogin { 146 if err := startPingerIfAgent(a.srv.pingClock, a.root, entity); err != nil { 147 return fail, errors.Trace(err) 148 } 149 } 150 151 var maybeUserInfo *params.AuthUserInfo 152 // Send back user info if user 153 if isUser { 154 userTag := entity.Tag().(names.UserTag) 155 maybeUserInfo, err = a.checkUserPermissions(userTag, controllerOnlyLogin) 156 if err != nil { 157 return fail, errors.Trace(err) 158 } 159 maybeUserInfo.LastConnection = lastConnection 160 } else { 161 if controllerOnlyLogin { 162 logger.Debugf("controller login: %s", entity.Tag()) 163 } else { 164 logger.Debugf("model login: %s for %s", entity.Tag(), a.root.state.ModelTag().Id()) 165 } 166 } 167 168 // Fetch the API server addresses from state. 169 hostPorts, err := a.root.state.APIHostPorts() 170 if err != nil { 171 return fail, errors.Trace(err) 172 } 173 174 model, err := a.root.state.Model() 175 if err != nil { 176 return fail, errors.Trace(err) 177 } 178 179 if isUser && model.MigrationMode() == state.MigrationModeImporting { 180 apiRoot = restrictAll(apiRoot, errors.New("migration in progress, model is importing")) 181 } 182 183 loginResult := params.LoginResult{ 184 Servers: params.FromNetworkHostsPorts(hostPorts), 185 ControllerTag: model.ControllerTag().String(), 186 UserInfo: maybeUserInfo, 187 ServerVersion: jujuversion.Current.String(), 188 } 189 190 if controllerOnlyLogin { 191 loginResult.Facades = filterFacades(isControllerFacade) 192 apiRoot = restrictRoot(apiRoot, controllerFacadesOnly) 193 } else { 194 loginResult.ModelTag = model.Tag().String() 195 loginResult.Facades = filterFacades(isModelFacade) 196 apiRoot = restrictRoot(apiRoot, modelFacadesOnly) 197 } 198 199 a.root.rpcConn.ServeRoot(apiRoot, serverError) 200 201 return loginResult, nil 202 } 203 204 func (a *admin) checkUserPermissions(userTag names.UserTag, controllerOnlyLogin bool) (*params.AuthUserInfo, error) { 205 206 modelAccess := permission.NoAccess 207 if !controllerOnlyLogin { 208 // Only grab modelUser permissions if this is not a controller only 209 // login. In all situations, if the model user is not found, they have 210 // no authorisation to access this model. 211 modelUser, err := a.root.state.UserAccess(userTag, a.root.state.ModelTag()) 212 if err != nil { 213 return nil, errors.Wrap(err, common.ErrPerm) 214 } 215 modelAccess = modelUser.Access 216 } 217 218 // TODO(perrito666) remove the following section about everyone group 219 // when groups are implemented, this accounts only for the lack of a local 220 // ControllerUser when logging in from an external user that has not been granted 221 // permissions on the controller but there are permissions for the special 222 // everyone group. 223 everyoneGroupAccess := permission.NoAccess 224 if !userTag.IsLocal() { 225 everyoneTag := names.NewUserTag(common.EveryoneTagName) 226 everyoneGroupUser, err := state.ControllerAccess(a.root.state, everyoneTag) 227 if err != nil && !errors.IsNotFound(err) { 228 return nil, errors.Annotatef(err, "obtaining ControllerUser for everyone group") 229 } 230 everyoneGroupAccess = everyoneGroupUser.Access 231 } 232 233 controllerAccess := permission.NoAccess 234 if controllerUser, err := state.ControllerAccess(a.root.state, userTag); err == nil { 235 controllerAccess = controllerUser.Access 236 } else if errors.IsNotFound(err) { 237 controllerAccess = everyoneGroupAccess 238 } else { 239 return nil, errors.Annotatef(err, "obtaining ControllerUser for logged in user %s", userTag.Id()) 240 } 241 // It is possible that the everyoneGroup permissions are more capable than an 242 // individuals. If they are, use them. 243 if everyoneGroupAccess.GreaterControllerAccessThan(controllerAccess) { 244 controllerAccess = everyoneGroupAccess 245 } 246 if controllerOnlyLogin || !a.srv.allowModelAccess { 247 // We're either explicitly logging into the controller or 248 // we must check that the user has access to the controller 249 // even though they're logging into a model. 250 if controllerAccess == permission.NoAccess { 251 return nil, errors.Trace(common.ErrPerm) 252 } 253 } 254 if controllerOnlyLogin { 255 logger.Debugf("controller login: user %s has %q access", userTag.Id(), controllerAccess) 256 } else { 257 logger.Debugf("model login: user %s has %q for controller; %q for model %s", 258 userTag.Id(), controllerAccess, modelAccess, a.root.state.ModelTag().Id()) 259 } 260 return ¶ms.AuthUserInfo{ 261 Identity: userTag.String(), 262 ControllerAccess: string(controllerAccess), 263 ModelAccess: string(modelAccess), 264 }, nil 265 } 266 267 func filterFacades(allowFacade func(name string) bool) []params.FacadeVersions { 268 allFacades := DescribeFacades() 269 out := make([]params.FacadeVersions, 0, len(allFacades)) 270 for _, facade := range allFacades { 271 if allowFacade(facade.Name) { 272 out = append(out, facade) 273 } 274 } 275 return out 276 } 277 278 func (a *admin) checkCreds(req params.LoginRequest, lookForModelUser bool) (state.Entity, *time.Time, error) { 279 return doCheckCreds(a.root.state, req, lookForModelUser, a.authenticator()) 280 } 281 282 func (a *admin) checkControllerMachineCreds(req params.LoginRequest) (state.Entity, error) { 283 return checkControllerMachineCreds(a.srv.state, req, a.authenticator()) 284 } 285 286 func (a *admin) authenticator() authentication.EntityAuthenticator { 287 return a.srv.authCtxt.authenticator(a.root.serverHost) 288 } 289 290 func (a *admin) maintenanceInProgress() bool { 291 if a.srv.validator == nil { 292 return false 293 } 294 // jujud's login validator will return an error for any user tag 295 // if jujud is upgrading or restoring. The tag of the entity 296 // trying to log in can't be used because jujud's login validator 297 // will always return nil for the local machine agent and here we 298 // need to know if maintenance is in progress irrespective of the 299 // the authenticating entity. 300 // 301 // TODO(mjs): 2014-09-29 bug 1375110 302 // This needs improving but I don't have the cycles right now. 303 req := params.LoginRequest{ 304 AuthTag: names.NewUserTag("arbitrary").String(), 305 } 306 return a.srv.validator(req) != nil 307 } 308 309 var doCheckCreds = checkCreds 310 311 // checkCreds validates the entities credentials in the current model. 312 // If the entity is a user, and lookForModelUser is true, a model user must exist 313 // for the model. In the case of a user logging in to the controller, but 314 // not a model, there is no env user needed. While we have the env 315 // user, if we do have it, update the last login time. 316 // 317 // Note that when logging in with lookForModelUser true, the returned 318 // entity will be modelUserEntity, not *state.User (external users 319 // don't have user entries) or *state.ModelUser (we 320 // don't want to lose the local user information associated with that). 321 func checkCreds(st *state.State, req params.LoginRequest, lookForModelUser bool, authenticator authentication.EntityAuthenticator) (state.Entity, *time.Time, error) { 322 var tag names.Tag 323 if req.AuthTag != "" { 324 var err error 325 tag, err = names.ParseTag(req.AuthTag) 326 if err != nil { 327 return nil, nil, errors.Trace(err) 328 } 329 } 330 var entityFinder authentication.EntityFinder = st 331 if lookForModelUser { 332 // When looking up model users, use a custom 333 // entity finder that looks up both the local user (if the user 334 // tag is in the local domain) and the model user. 335 entityFinder = modelUserEntityFinder{st} 336 } 337 entity, err := authenticator.Authenticate(entityFinder, tag, req) 338 if err != nil { 339 return nil, nil, errors.Trace(err) 340 } 341 342 // For user logins, update the last login time. 343 var lastLogin *time.Time 344 if entity, ok := entity.(loginEntity); ok { 345 userLastLogin, err := entity.LastLogin() 346 if err != nil && !state.IsNeverLoggedInError(err) { 347 return nil, nil, errors.Trace(err) 348 } 349 entity.UpdateLastLogin() 350 lastLogin = &userLastLogin 351 } 352 return entity, lastLogin, nil 353 } 354 355 // checkControllerMachineCreds checks the special case of a controller 356 // machine creating an API connection for a different model so it can 357 // run workers that act on behalf of a hosted model. 358 func checkControllerMachineCreds( 359 controllerSt *state.State, 360 req params.LoginRequest, 361 authenticator authentication.EntityAuthenticator, 362 ) (state.Entity, error) { 363 entity, _, err := doCheckCreds(controllerSt, req, false, authenticator) 364 if err != nil { 365 return nil, errors.Trace(err) 366 } 367 if machine, ok := entity.(*state.Machine); !ok { 368 return nil, errors.Errorf("entity should be a machine, but is %T", entity) 369 } else if !machine.IsManager() { 370 // The machine exists in the controller model, but it doesn't 371 // manage models, so reject it. 372 return nil, errors.Trace(common.ErrPerm) 373 } 374 return entity, nil 375 } 376 377 // loginEntity defines the interface needed to log in as a user. 378 // Notable implementations are *state.User and *modelUserEntity. 379 type loginEntity interface { 380 state.Entity 381 state.Authenticator 382 LastLogin() (time.Time, error) 383 UpdateLastLogin() error 384 } 385 386 // modelUserEntityFinder implements EntityFinder by returning a 387 // loginEntity value for users, ensuring that the user exists in the 388 // state's current model as well as retrieving more global 389 // authentication details such as the password. 390 type modelUserEntityFinder struct { 391 st *state.State 392 } 393 394 // FindEntity implements authentication.EntityFinder.FindEntity. 395 func (f modelUserEntityFinder) FindEntity(tag names.Tag) (state.Entity, error) { 396 utag, ok := tag.(names.UserTag) 397 if !ok { 398 return f.st.FindEntity(tag) 399 } 400 401 modelUser, controllerUser, err := common.UserAccess(f.st, utag) 402 if err != nil { 403 return nil, errors.Trace(err) 404 } 405 u := &modelUserEntity{ 406 st: f.st, 407 modelUser: modelUser, 408 controllerUser: controllerUser, 409 } 410 if utag.IsLocal() { 411 user, err := f.st.User(utag) 412 if err != nil { 413 return nil, errors.Trace(err) 414 } 415 u.user = user 416 } 417 return u, nil 418 } 419 420 var _ loginEntity = &modelUserEntity{} 421 422 // modelUserEntity encapsulates an model user 423 // and, if the user is local, the local state user 424 // as well. This enables us to implement FindEntity 425 // in such a way that the authentication mechanisms 426 // can work without knowing these details. 427 type modelUserEntity struct { 428 st *state.State 429 430 controllerUser permission.UserAccess 431 modelUser permission.UserAccess 432 user *state.User 433 } 434 435 // Refresh implements state.Authenticator.Refresh. 436 func (u *modelUserEntity) Refresh() error { 437 if u.user == nil { 438 return nil 439 } 440 return u.user.Refresh() 441 } 442 443 // SetPassword implements state.Authenticator.SetPassword 444 // by setting the password on the local user. 445 func (u *modelUserEntity) SetPassword(pass string) error { 446 if u.user == nil { 447 return errors.New("cannot set password on external user") 448 } 449 return u.user.SetPassword(pass) 450 } 451 452 // PasswordValid implements state.Authenticator.PasswordValid. 453 func (u *modelUserEntity) PasswordValid(pass string) bool { 454 if u.user == nil { 455 return false 456 } 457 return u.user.PasswordValid(pass) 458 } 459 460 // Tag implements state.Entity.Tag. 461 func (u *modelUserEntity) Tag() names.Tag { 462 if u.user != nil { 463 return u.user.UserTag() 464 } 465 if !permission.IsEmptyUserAccess(u.modelUser) { 466 return u.modelUser.UserTag 467 } 468 return u.controllerUser.UserTag 469 470 } 471 472 // LastLogin implements loginEntity.LastLogin. 473 func (u *modelUserEntity) LastLogin() (time.Time, error) { 474 // The last connection for the model takes precedence over 475 // the local user last login time. 476 var err error 477 var t time.Time 478 if !permission.IsEmptyUserAccess(u.modelUser) { 479 t, err = u.st.LastModelConnection(u.modelUser.UserTag) 480 } else { 481 err = state.NeverConnectedError("controller user") 482 } 483 if state.IsNeverConnectedError(err) || permission.IsEmptyUserAccess(u.modelUser) { 484 if u.user != nil { 485 // There's a global user, so use that login time instead. 486 return u.user.LastLogin() 487 } 488 // Since we're implementing LastLogin, we need 489 // to implement LastLogin error semantics too. 490 err = state.NeverLoggedInError(err.Error()) 491 } 492 return t, errors.Trace(err) 493 } 494 495 // UpdateLastLogin implements loginEntity.UpdateLastLogin. 496 func (u *modelUserEntity) UpdateLastLogin() error { 497 var err error 498 499 if !permission.IsEmptyUserAccess(u.modelUser) { 500 if u.modelUser.Object.Kind() != names.ModelTagKind { 501 return errors.NotValidf("%s as model user", u.modelUser.Object.Kind()) 502 } 503 504 err = u.st.UpdateLastModelConnection(u.modelUser.UserTag) 505 } 506 507 if u.user != nil { 508 err1 := u.user.UpdateLastLogin() 509 if err == nil { 510 return err1 511 } 512 } 513 if err != nil { 514 return errors.Trace(err) 515 } 516 return nil 517 } 518 519 // presenceShim exists to represent a statepresence.Agent in a form 520 // convenient to the apiserver/presence package, which exists to work 521 // around the common.Resources infrastructure's lack of handling for 522 // failed resources. 523 type presenceShim struct { 524 agent statepresence.Agent 525 } 526 527 // Start starts and returns a running presence.Pinger. The caller is 528 // responsible for stopping it when no longer required, and for handling 529 // any errors returned from Wait. 530 func (shim presenceShim) Start() (presence.Pinger, error) { 531 pinger, err := shim.agent.SetAgentPresence() 532 if err != nil { 533 return nil, errors.Trace(err) 534 } 535 return pinger, nil 536 } 537 538 func startPingerIfAgent(clock clock.Clock, root *apiHandler, entity state.Entity) error { 539 // worker runs presence.Pingers -- absence of which will cause 540 // embarrassing "agent is lost" messages to show up in status -- 541 // until it's stopped. It's stored in resources purely for the 542 // side effects: we don't record its id, and nobody else 543 // retrieves it -- we just expect it to be stopped when the 544 // connection is shut down. 545 agent, ok := entity.(statepresence.Agent) 546 if !ok { 547 return nil 548 } 549 worker, err := presence.New(presence.Config{ 550 Identity: entity.Tag(), 551 Start: presenceShim{agent}.Start, 552 Clock: clock, 553 RetryDelay: 3 * time.Second, 554 }) 555 if err != nil { 556 return err 557 } 558 root.getResources().Register(worker) 559 560 // pingTimeout, by contrast, *is* used by the Pinger facade to 561 // stave off the call to action() that will shut down the agent 562 // connection if it gets lackadaisical about sending keepalive 563 // Pings. 564 // 565 // Do not confuse those (apiserver) Pings with those made by 566 // presence.Pinger (which *do* happen as a result of the former, 567 // but only as a relatively distant consequence). 568 // 569 // We should have picked better names... 570 action := func() { 571 logger.Debugf("closing connection due to ping timout") 572 if err := root.getRpcConn().Close(); err != nil { 573 logger.Errorf("error closing the RPC connection: %v", err) 574 } 575 } 576 pingTimeout := newPingTimeout(action, clock, maxClientPingInterval) 577 return root.getResources().RegisterNamed("pingTimeout", pingTimeout) 578 } 579 580 // errRoot implements the API that a client first sees 581 // when connecting to the API. It exposes the same API as initialRoot, except 582 // it returns the requested error when the client makes any request. 583 type errRoot struct { 584 err error 585 } 586 587 // FindMethod conforms to the same API as initialRoot, but we'll always return (nil, err) 588 func (r *errRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { 589 return nil, r.err 590 } 591 592 func (r *errRoot) Kill() { 593 }