github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/root.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver 5 6 import ( 7 "context" 8 "fmt" 9 "net/url" 10 "reflect" 11 "sync" 12 "time" 13 14 "github.com/juju/clock" 15 "github.com/juju/errors" 16 "github.com/juju/names/v5" 17 "github.com/juju/rpcreflect" 18 19 "github.com/juju/juju/apiserver/authentication" 20 "github.com/juju/juju/apiserver/common" 21 "github.com/juju/juju/apiserver/facade" 22 "github.com/juju/juju/core/cache" 23 coredatabase "github.com/juju/juju/core/database" 24 "github.com/juju/juju/core/leadership" 25 "github.com/juju/juju/core/lease" 26 "github.com/juju/juju/core/multiwatcher" 27 "github.com/juju/juju/core/permission" 28 "github.com/juju/juju/rpc" 29 "github.com/juju/juju/rpc/params" 30 "github.com/juju/juju/state" 31 ) 32 33 type objectKey struct { 34 name string 35 version int 36 objId string 37 } 38 39 // apiHandler represents a single client's connection to the state 40 // after it has logged in. It contains an rpc.Root which it 41 // uses to dispatch API calls appropriately. 42 type apiHandler struct { 43 state *state.State 44 model *state.Model 45 rpcConn *rpc.Conn 46 resources *common.Resources 47 shared *sharedServerContext 48 49 // authInfo represents the authentication info established with this client 50 // connection. 51 authInfo authentication.AuthInfo 52 53 // An empty modelUUID means that the user has logged in through the 54 // root of the API server rather than the /model/:model-uuid/api 55 // path, logins processed with v2 or later will only offer the 56 // user manager and model manager api endpoints from here. 57 modelUUID string 58 59 // connectionID is shared between the API observer (including API 60 // requests and responses in the agent log) and the audit logger. 61 connectionID uint64 62 63 // serverHost is the host:port of the API server that the client 64 // connected to. 65 serverHost string 66 } 67 68 var _ = (*apiHandler)(nil) 69 70 var ( 71 // maxClientPingInterval defines the timeframe until the ping timeout 72 // closes the monitored connection. TODO(mue): Idea by Roger: 73 // Move to API (e.g. params) so that the pinging there may 74 // depend on the interval. 75 maxClientPingInterval = 3 * time.Minute 76 ) 77 78 // newAPIHandler returns a new apiHandler. 79 func newAPIHandler(srv *Server, st *state.State, rpcConn *rpc.Conn, modelUUID string, connectionID uint64, serverHost string) (*apiHandler, error) { 80 m, err := st.Model() 81 if err != nil { 82 if !errors.IsNotFound(err) { 83 return nil, errors.Trace(err) 84 } 85 86 // If this model used to be hosted on this controller but got 87 // migrated allow clients to connect and wait for a login 88 // request to decide whether the users should be redirected to 89 // the new controller for this model or not. 90 if _, migErr := st.CompletedMigration(); migErr != nil { 91 return nil, errors.Trace(err) // return original NotFound error 92 } 93 } 94 95 r := &apiHandler{ 96 state: st, 97 model: m, 98 resources: common.NewResources(), 99 shared: srv.shared, 100 rpcConn: rpcConn, 101 modelUUID: modelUUID, 102 connectionID: connectionID, 103 serverHost: serverHost, 104 } 105 106 if err := r.resources.RegisterNamed("machineID", common.StringResource(srv.tag.Id())); err != nil { 107 return nil, errors.Trace(err) 108 } 109 if err := r.resources.RegisterNamed("dataDir", common.StringResource(srv.dataDir)); err != nil { 110 return nil, errors.Trace(err) 111 } 112 if err := r.resources.RegisterNamed("logDir", common.StringResource(srv.logDir)); err != nil { 113 return nil, errors.Trace(err) 114 } 115 116 // Facades involved with managing application offers need the auth context 117 // to mint and validate macaroons. 118 offerAccessEndpoint := &url.URL{ 119 Scheme: "https", 120 Host: serverHost, 121 Path: localOfferAccessLocationPath, 122 } 123 124 controllerConfig, err := st.ControllerConfig() 125 if err != nil { 126 return nil, errors.Annotate(err, "unable to get controller config") 127 } 128 loginTokenRefreshURL := controllerConfig.LoginTokenRefreshURL() 129 if loginTokenRefreshURL != "" { 130 offerAccessEndpoint, err = url.Parse(loginTokenRefreshURL) 131 if err != nil { 132 return nil, errors.Trace(err) 133 } 134 } 135 offerAuthCtxt, err := srv.offerAuthCtxt.WithDischargeURL(offerAccessEndpoint.String()) 136 if err != nil { 137 return nil, errors.Trace(err) 138 } 139 if err := r.resources.RegisterNamed( 140 "offerAccessAuthContext", common.ValueResource{Value: offerAuthCtxt}, 141 ); err != nil { 142 return nil, errors.Trace(err) 143 } 144 return r, nil 145 } 146 147 // Resources returns the common resources. 148 func (r *apiHandler) Resources() *common.Resources { 149 return r.resources 150 } 151 152 // State returns the underlying state. 153 func (r *apiHandler) State() *state.State { 154 return r.state 155 } 156 157 // SharedContext returns the server shared context. 158 func (r *apiHandler) SharedContext() *sharedServerContext { 159 return r.shared 160 } 161 162 // Authorizer returns the authorizer used for accessing API method calls. 163 func (r *apiHandler) Authorizer() facade.Authorizer { 164 return r 165 } 166 167 func (r *apiHandler) getRpcConn() *rpc.Conn { 168 return r.rpcConn 169 } 170 171 // Kill implements rpc.Killer, cleaning up any resources that need 172 // cleaning up to ensure that all outstanding requests return. 173 func (r *apiHandler) Kill() { 174 r.resources.StopAll() 175 } 176 177 // srvCaller is our implementation of the rpcreflect.MethodCaller interface. 178 // It lives just long enough to encapsulate the methods that should be 179 // available for an RPC call and allow the RPC code to instantiate an object 180 // and place a call on its method. 181 type srvCaller struct { 182 objMethod rpcreflect.ObjMethod 183 creator func(id string) (reflect.Value, error) 184 } 185 186 // ParamsType defines the parameters that should be supplied to this function. 187 // See rpcreflect.MethodCaller for more detail. 188 func (s *srvCaller) ParamsType() reflect.Type { 189 return s.objMethod.Params 190 } 191 192 // ResultType defines the object that is returned from the function.` 193 // See rpcreflect.MethodCaller for more detail. 194 func (s *srvCaller) ResultType() reflect.Type { 195 return s.objMethod.Result 196 } 197 198 // Call takes the object Id and an instance of ParamsType to create an object and place 199 // a call on its method. It then returns an instance of ResultType. 200 func (s *srvCaller) Call(ctx context.Context, objId string, arg reflect.Value) (reflect.Value, error) { 201 objVal, err := s.creator(objId) 202 if err != nil { 203 return reflect.Value{}, err 204 } 205 return s.objMethod.Call(ctx, objVal, arg) 206 } 207 208 // apiRoot implements basic method dispatching to the facade registry. 209 type apiRoot struct { 210 clock clock.Clock 211 state *state.State 212 shared *sharedServerContext 213 facades *facade.Registry 214 resources *common.Resources 215 authorizer facade.Authorizer 216 objectMutex sync.RWMutex 217 objectCache map[objectKey]reflect.Value 218 requestRecorder facade.RequestRecorder 219 } 220 221 type apiRootHandler interface { 222 // State returns the underlying state. 223 State() *state.State 224 // SharedContext returns the server shared context. 225 SharedContext() *sharedServerContext 226 // Resources returns the common resources. 227 Resources() *common.Resources 228 // Authorizer returns the authorizer used for accessing API method calls. 229 Authorizer() facade.Authorizer 230 } 231 232 // newAPIRoot returns a new apiRoot. 233 func newAPIRoot(clock clock.Clock, 234 facades *facade.Registry, 235 root apiRootHandler, 236 requestRecorder facade.RequestRecorder, 237 ) (*apiRoot, error) { 238 st := root.State() 239 r := &apiRoot{ 240 clock: clock, 241 state: st, 242 shared: root.SharedContext(), 243 facades: facades, 244 resources: root.Resources(), 245 authorizer: root.Authorizer(), 246 objectCache: make(map[objectKey]reflect.Value), 247 requestRecorder: requestRecorder, 248 } 249 // Ensure that the model being requested is in our model cache. 250 // Client connections need it for status (or very soon will), and agents 251 // require it for model config and others. 252 // In all real cases we have a state object, but some test code avoids passing one 253 // in, in order to just probe endpoints. 254 if st != nil { 255 _, err := r.cachedModel(st.ModelUUID()) 256 if err != nil { 257 return nil, errors.Annotate(err, "model cache") 258 } 259 } 260 return r, nil 261 } 262 263 // restrictAPIRoot calls restrictAPIRootDuringMaintenance, and 264 // then restricts the result further to the controller or model 265 // facades, depending on the type of login. 266 func restrictAPIRoot( 267 srv *Server, 268 apiRoot rpc.Root, 269 model *state.Model, 270 auth authResult, 271 ) (rpc.Root, error) { 272 if !auth.controllerMachineLogin { 273 // Controller agents are allowed to 274 // connect even during maintenance. 275 restrictedRoot, err := restrictAPIRootDuringMaintenance( 276 srv, apiRoot, model, auth.tag, 277 ) 278 if err != nil { 279 return nil, errors.Trace(err) 280 } 281 apiRoot = restrictedRoot 282 } 283 if auth.controllerOnlyLogin { 284 apiRoot = restrictRoot(apiRoot, controllerFacadesOnly) 285 } else { 286 apiRoot = restrictRoot(apiRoot, modelFacadesOnly) 287 if model.Type() == state.ModelTypeCAAS { 288 apiRoot = restrictRoot(apiRoot, caasModelFacadesOnly) 289 } 290 } 291 return apiRoot, nil 292 } 293 294 // restrictAPIRootDuringMaintenance restricts the API root during 295 // maintenance events (upgrade or migration), depending 296 // on the authenticated client. 297 func restrictAPIRootDuringMaintenance( 298 srv *Server, 299 apiRoot rpc.Root, 300 model *state.Model, 301 authTag names.Tag, 302 ) (rpc.Root, error) { 303 describeLogin := func() string { 304 if authTag == nil { 305 return "anonymous login" 306 } 307 return fmt.Sprintf("login for %s", names.ReadableString(authTag)) 308 } 309 310 if !srv.upgradeComplete() { 311 if _, ok := authTag.(names.UserTag); ok { 312 // Users get access to a limited set of functionality 313 // while an upgrade is in progress. 314 return restrictRoot(apiRoot, upgradeMethodsOnly), nil 315 } 316 // Agent and anonymous logins are blocked during upgrade. 317 return nil, errors.Errorf("%s blocked because upgrade is in progress", describeLogin()) 318 } 319 320 // For user logins, we limit access during migrations. 321 if _, ok := authTag.(names.UserTag); ok { 322 switch model.MigrationMode() { 323 case state.MigrationModeImporting: 324 // The user is not able to access a model that is currently being 325 // imported until the model has been activated. 326 apiRoot = restrictAll(apiRoot, errors.New("migration in progress, model is importing")) 327 case state.MigrationModeExporting: 328 // The user is not allowed to change anything in a model that is 329 // currently being moved to another controller. 330 apiRoot = restrictRoot(apiRoot, migrationClientMethodsOnly) 331 } 332 } 333 334 return apiRoot, nil 335 } 336 337 // Kill implements rpc.Killer, stopping the root's resources. 338 func (r *apiRoot) Kill() { 339 r.resources.StopAll() 340 } 341 342 // FindMethod looks up the given rootName and version in our facade registry 343 // and returns a MethodCaller that will be used by the RPC code to place calls on 344 // that facade. 345 // FindMethod uses the global registry apiserver/common.Facades. 346 // For more information about how FindMethod should work, see rpc/server.go and 347 // rpc/rpcreflect/value.go 348 func (r *apiRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { 349 goType, objMethod, err := r.lookupMethod(rootName, version, methodName) 350 if err != nil { 351 return nil, err 352 } 353 354 creator := func(id string) (reflect.Value, error) { 355 objKey := objectKey{name: rootName, version: version, objId: id} 356 r.objectMutex.RLock() 357 objValue, ok := r.objectCache[objKey] 358 r.objectMutex.RUnlock() 359 if ok { 360 return objValue, nil 361 } 362 r.objectMutex.Lock() 363 defer r.objectMutex.Unlock() 364 if objValue, ok := r.objectCache[objKey]; ok { 365 return objValue, nil 366 } 367 // Now that we have the write lock, check one more time in case 368 // someone got the write lock before us. 369 factory, err := r.facades.GetFactory(rootName, version) 370 if err != nil { 371 // We don't check for IsNotFound here, because it 372 // should have already been handled in the GetType 373 // check. 374 return reflect.Value{}, err 375 } 376 obj, err := factory(r.facadeContext(objKey)) 377 if err != nil { 378 return reflect.Value{}, err 379 } 380 objValue = reflect.ValueOf(obj) 381 if !objValue.Type().AssignableTo(goType) { 382 return reflect.Value{}, errors.Errorf( 383 "internal error, %s(%d) claimed to return %s but returned %T", 384 rootName, version, goType, obj) 385 } 386 if goType.Kind() == reflect.Interface { 387 // If the original function wanted to return an 388 // interface type, the indirection in the factory via 389 // an interface{} strips the original interface 390 // information off. So here we have to create the 391 // interface again, and assign it. 392 asInterface := reflect.New(goType).Elem() 393 asInterface.Set(objValue) 394 objValue = asInterface 395 } 396 r.objectCache[objKey] = objValue 397 return objValue, nil 398 } 399 return &srvCaller{ 400 creator: creator, 401 objMethod: objMethod, 402 }, nil 403 } 404 405 func (r *apiRoot) lookupMethod(rootName string, version int, methodName string) (reflect.Type, rpcreflect.ObjMethod, error) { 406 goType, err := r.facades.GetType(rootName, version) 407 if err != nil { 408 if errors.IsNotFound(err) { 409 return nil, rpcreflect.ObjMethod{}, &rpcreflect.CallNotImplementedError{ 410 RootMethod: rootName, 411 Version: version, 412 } 413 } 414 return nil, rpcreflect.ObjMethod{}, err 415 } 416 rpcType := rpcreflect.ObjTypeOf(goType) 417 objMethod, err := rpcType.Method(methodName) 418 if err != nil { 419 if err == rpcreflect.ErrMethodNotFound { 420 return nil, rpcreflect.ObjMethod{}, &rpcreflect.CallNotImplementedError{ 421 RootMethod: rootName, 422 Version: version, 423 Method: methodName, 424 } 425 } 426 return nil, rpcreflect.ObjMethod{}, err 427 } 428 return goType, objMethod, nil 429 } 430 431 func (r *apiRoot) dispose(key objectKey) { 432 r.objectMutex.Lock() 433 defer r.objectMutex.Unlock() 434 delete(r.objectCache, key) 435 } 436 437 func (r *apiRoot) cachedModel(uuid string) (*cache.Model, error) { 438 model, err := r.shared.controller.WaitForModel(uuid, r.clock) 439 if err != nil { 440 // Check the database... 441 exists, err2 := r.state.ModelExists(uuid) 442 if err2 != nil { 443 return nil, errors.Trace(err2) 444 } 445 if exists { 446 return nil, errors.Trace(err) 447 } 448 return nil, errors.NotFoundf("model %q", uuid) 449 } 450 return model, nil 451 } 452 453 func (r *apiRoot) facadeContext(key objectKey) *facadeContext { 454 return &facadeContext{ 455 r: r, 456 key: key, 457 } 458 } 459 460 // facadeContext implements facade.Context 461 type facadeContext struct { 462 r *apiRoot 463 key objectKey 464 } 465 466 // Cancel is part of the facade.Context interface. 467 func (ctx *facadeContext) Cancel() <-chan struct{} { 468 return ctx.r.shared.cancel 469 } 470 471 // Auth is part of the facade.Context interface. 472 func (ctx *facadeContext) Auth() facade.Authorizer { 473 return ctx.r.authorizer 474 } 475 476 // Dispose is part of the facade.Context interface. 477 func (ctx *facadeContext) Dispose() { 478 ctx.r.dispose(ctx.key) 479 } 480 481 // Resources is part of the facade.Context interface. 482 func (ctx *facadeContext) Resources() facade.Resources { 483 return ctx.r.resources 484 } 485 486 // Presence implements facade.Context. 487 func (ctx *facadeContext) Presence() facade.Presence { 488 return ctx 489 } 490 491 // ModelPresence implements facade.ModelPresence. 492 func (ctx *facadeContext) ModelPresence(modelUUID string) facade.ModelPresence { 493 return ctx.r.shared.presence.Connections().ForModel(modelUUID) 494 } 495 496 // Hub implements facade.Context. 497 func (ctx *facadeContext) Hub() facade.Hub { 498 return ctx.r.shared.centralHub 499 } 500 501 // Controller implements facade.Context. 502 func (ctx *facadeContext) Controller() *cache.Controller { 503 return ctx.r.shared.controller 504 } 505 506 // CachedModel implements facade.Context. 507 func (ctx *facadeContext) CachedModel(uuid string) (*cache.Model, error) { 508 return ctx.r.cachedModel(uuid) 509 } 510 511 // State is part of the facade.Context interface. 512 func (ctx *facadeContext) State() *state.State { 513 return ctx.r.state 514 } 515 516 // StatePool is part of the facade.Context interface. 517 func (ctx *facadeContext) StatePool() *state.StatePool { 518 return ctx.r.shared.statePool 519 } 520 521 // MultiwatcherFactory is part of the facade.Context interface. 522 func (ctx *facadeContext) MultiwatcherFactory() multiwatcher.Factory { 523 return ctx.r.shared.multiwatcherFactory 524 } 525 526 // ID is part of the facade.Context interface. 527 func (ctx *facadeContext) ID() string { 528 return ctx.key.objId 529 } 530 531 // RequestRecorder defines a metrics collector for outbound requests. 532 func (ctx *facadeContext) RequestRecorder() facade.RequestRecorder { 533 return ctx.r.requestRecorder 534 } 535 536 // LeadershipClaimer is part of the facade.Context interface. 537 func (ctx *facadeContext) LeadershipClaimer(modelUUID string) (leadership.Claimer, error) { 538 claimer, err := ctx.r.shared.leaseManager.Claimer( 539 lease.ApplicationLeadershipNamespace, 540 modelUUID, 541 ) 542 if err != nil { 543 return nil, errors.Trace(err) 544 } 545 return leadershipClaimer{claimer}, nil 546 } 547 548 // LeadershipRevoker is part of the facade.Context interface. 549 func (ctx *facadeContext) LeadershipRevoker(modelUUID string) (leadership.Revoker, error) { 550 revoker, err := ctx.r.shared.leaseManager.Revoker( 551 lease.ApplicationLeadershipNamespace, 552 modelUUID, 553 ) 554 if err != nil { 555 return nil, errors.Trace(err) 556 } 557 return leadershipRevoker{revoker}, nil 558 } 559 560 // LeadershipChecker is part of the facade.Context interface. 561 func (ctx *facadeContext) LeadershipChecker() (leadership.Checker, error) { 562 checker, err := ctx.r.shared.leaseManager.Checker( 563 lease.ApplicationLeadershipNamespace, 564 ctx.State().ModelUUID(), 565 ) 566 if err != nil { 567 return nil, errors.Trace(err) 568 } 569 return leadershipChecker{checker}, nil 570 } 571 572 // LeadershipPinner is part of the facade.Context interface. 573 // Pinning functionality is only available with the Raft leases implementation. 574 func (ctx *facadeContext) LeadershipPinner(modelUUID string) (leadership.Pinner, error) { 575 pinner, err := ctx.r.shared.leaseManager.Pinner( 576 lease.ApplicationLeadershipNamespace, 577 modelUUID, 578 ) 579 if err != nil { 580 return nil, errors.Trace(err) 581 } 582 return leadershipPinner{pinner}, nil 583 } 584 585 // LeadershipReader is part of the facade.Context interface. 586 // It returns a reader that can be used to return all application leaders 587 // in the model. 588 func (ctx *facadeContext) LeadershipReader(modelUUID string) (leadership.Reader, error) { 589 reader, err := ctx.r.shared.leaseManager.Reader( 590 lease.ApplicationLeadershipNamespace, 591 modelUUID, 592 ) 593 if err != nil { 594 return nil, errors.Trace(err) 595 } 596 return leadershipReader{reader}, nil 597 } 598 599 // SingularClaimer is part of the facade.Context interface. 600 func (ctx *facadeContext) SingularClaimer() (lease.Claimer, error) { 601 return ctx.r.shared.leaseManager.Claimer( 602 lease.SingularControllerNamespace, 603 ctx.State().ModelUUID(), 604 ) 605 } 606 607 func (ctx *facadeContext) HTTPClient(purpose facade.HTTPClientPurpose) facade.HTTPClient { 608 switch purpose { 609 case facade.CharmhubHTTPClient: 610 return ctx.r.shared.charmhubHTTPClient 611 default: 612 return nil 613 } 614 } 615 616 // ControllerDB returns a TrackedDB reference for the controller database. 617 func (ctx *facadeContext) ControllerDB() (coredatabase.TrackedDB, error) { 618 db, err := ctx.r.shared.dbGetter.GetDB(coredatabase.ControllerNS) 619 return db, errors.Trace(err) 620 } 621 622 // adminRoot dispatches API calls to those available to an anonymous connection 623 // which has not logged in, which here is the admin facade. 624 type adminRoot struct { 625 *apiHandler 626 adminAPIs map[int]interface{} 627 } 628 629 // newAdminRoot creates a new AnonRoot which dispatches to the given Admin API implementation. 630 func newAdminRoot(h *apiHandler, adminAPIs map[int]interface{}) *adminRoot { 631 r := &adminRoot{ 632 apiHandler: h, 633 adminAPIs: adminAPIs, 634 } 635 return r 636 } 637 638 func (r *adminRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { 639 if rootName != "Admin" { 640 return nil, &rpcreflect.CallNotImplementedError{ 641 RootMethod: rootName, 642 Version: version, 643 } 644 } 645 if api, ok := r.adminAPIs[version]; ok { 646 return rpcreflect.ValueOf(reflect.ValueOf(api)).FindMethod(rootName, 0, methodName) 647 } 648 return nil, &rpc.RequestError{ 649 Code: params.CodeNotSupported, 650 Message: "this version of Juju does not support login from old clients", 651 } 652 } 653 654 // AuthMachineAgent returns whether the current client is a machine agent. 655 // TODO(controlleragent) - add AuthControllerAgent function 656 func (r *apiHandler) AuthMachineAgent() bool { 657 _, isMachine := r.GetAuthTag().(names.MachineTag) 658 _, isControllerAgent := r.GetAuthTag().(names.ControllerAgentTag) 659 return isMachine || isControllerAgent 660 } 661 662 // AuthModelAgent return whether the current client is a model agent 663 func (r *apiHandler) AuthModelAgent() bool { 664 _, isModel := r.GetAuthTag().(names.ModelTag) 665 return isModel 666 } 667 668 // AuthApplicationAgent returns whether the current client is an application operator. 669 func (r *apiHandler) AuthApplicationAgent() bool { 670 _, isApp := r.GetAuthTag().(names.ApplicationTag) 671 return isApp 672 } 673 674 // AuthUnitAgent returns whether the current client is a unit agent. 675 func (r *apiHandler) AuthUnitAgent() bool { 676 _, isUnit := r.GetAuthTag().(names.UnitTag) 677 return isUnit 678 } 679 680 // AuthOwner returns whether the authenticated user's tag matches the 681 // given entity tag. 682 func (r *apiHandler) AuthOwner(tag names.Tag) bool { 683 return r.GetAuthTag() == tag 684 } 685 686 // AuthController returns whether the authenticated user is a 687 // machine with running the ManageEnviron job. 688 func (r *apiHandler) AuthController() bool { 689 type hasIsManager interface { 690 IsManager() bool 691 } 692 m, ok := r.authInfo.Entity.(hasIsManager) 693 return ok && m.IsManager() 694 } 695 696 // AuthClient returns whether the authenticated entity is a client 697 // user. 698 func (r *apiHandler) AuthClient() bool { 699 _, isUser := r.GetAuthTag().(names.UserTag) 700 return isUser 701 } 702 703 // GetAuthTag returns the tag of the authenticated entity, if any. 704 func (r *apiHandler) GetAuthTag() names.Tag { 705 if r.authInfo.Entity == nil { 706 return nil 707 } 708 return r.authInfo.Entity.Tag() 709 } 710 711 // ConnectedModel returns the UUID of the model authenticated 712 // against. It's possible for it to be empty if the login was made 713 // directly to the root of the API instead of a model endpoint, but 714 // that method is deprecated. 715 func (r *apiHandler) ConnectedModel() string { 716 return r.modelUUID 717 } 718 719 // HasPermission is responsible for reporting if the logged in user is 720 // able to perform operation x on target y. It uses the authentication mechanism 721 // of the user to interrogate their permissions. If the entity does not have 722 // permission to perform the operation then the authentication provider is asked 723 // to provide a permission error. All permissions errors returned satisfy 724 // errors.Is(err, ErrorEntityMissingPermission) to distinguish before errors and 725 // no permissions errors. If error is nil then the user has permission. 726 func (r *apiHandler) HasPermission(operation permission.Access, target names.Tag) error { 727 return r.EntityHasPermission(r.GetAuthTag(), operation, target) 728 } 729 730 // EntityHasPermission is responsible for reporting if the supplied entity is 731 // able to perform operation x on target y. It uses the authentication mechanism 732 // of the user to interrogate their permissions. If the entity does not have 733 // permission to perform the operation then the authentication provider is asked 734 // to provide a permission error. All permissions errors returned satisfy 735 // errors.Is(err, ErrorEntityMissingPermission) to distinguish before errors and 736 // no permissions errors. If error is nil then the user has permission. 737 func (r *apiHandler) EntityHasPermission(entity names.Tag, operation permission.Access, target names.Tag) error { 738 var userAccessFunc common.UserAccessFunc = func(entity names.UserTag, subject names.Tag) (permission.Access, error) { 739 if r.authInfo.Delegator == nil { 740 return permission.NoAccess, fmt.Errorf("permissions %w for auth info", errors.NotImplemented) 741 } 742 return r.authInfo.Delegator.SubjectPermissions(authentication.TagToEntity(entity), subject) 743 } 744 has, err := common.HasPermission(userAccessFunc, entity, operation, target) 745 if err != nil && !errors.Is(err, errors.NotFound) { 746 return fmt.Errorf("checking entity %q has permission: %w", entity, err) 747 } 748 if !has && r.authInfo.Delegator != nil { 749 err = r.authInfo.Delegator.PermissionError(target, operation) 750 } 751 if !has { 752 return errors.WithType(err, authentication.ErrorEntityMissingPermission) 753 } 754 755 return nil 756 } 757 758 // DescribeFacades returns the list of available Facades and their Versions 759 func DescribeFacades(registry *facade.Registry) []params.FacadeVersions { 760 facades := registry.List() 761 result := make([]params.FacadeVersions, len(facades)) 762 for i, f := range facades { 763 result[i].Name = f.Name 764 result[i].Versions = f.Versions 765 } 766 return result 767 }