github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/testing/fakeauthorizer.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "strings" 8 9 "github.com/juju/errors" 10 "github.com/juju/names/v5" 11 12 "github.com/juju/juju/apiserver/authentication" 13 apiservererrors "github.com/juju/juju/apiserver/errors" 14 "github.com/juju/juju/core/permission" 15 ) 16 17 // FakeAuthorizer implements the facade.Authorizer interface. 18 type FakeAuthorizer struct { 19 Tag names.Tag 20 Controller bool 21 ModelUUID string 22 AdminTag names.UserTag 23 HasConsumeTag names.UserTag 24 HasWriteTag names.UserTag 25 } 26 27 func (fa FakeAuthorizer) AuthOwner(tag names.Tag) bool { 28 return fa.Tag == tag 29 } 30 31 func (fa FakeAuthorizer) AuthController() bool { 32 return fa.Controller 33 } 34 35 // AuthMachineAgent returns whether the current client is a machine agent. 36 func (fa FakeAuthorizer) AuthMachineAgent() bool { 37 // TODO(controlleragent) - add AuthControllerAgent function 38 _, isMachine := fa.GetAuthTag().(names.MachineTag) 39 _, isController := fa.GetAuthTag().(names.ControllerAgentTag) 40 return isMachine || isController 41 } 42 43 // AuthApplicationAgent returns whether the current client is an application operator. 44 func (fa FakeAuthorizer) AuthApplicationAgent() bool { 45 _, isApp := fa.GetAuthTag().(names.ApplicationTag) 46 return isApp 47 } 48 49 // AuthModelAgent returns true if the authenticated entity is a model agent 50 func (fa FakeAuthorizer) AuthModelAgent() bool { 51 _, isModel := fa.GetAuthTag().(names.ModelTag) 52 return isModel 53 } 54 55 // AuthUnitAgent returns whether the current client is a unit agent. 56 func (fa FakeAuthorizer) AuthUnitAgent() bool { 57 _, isUnit := fa.GetAuthTag().(names.UnitTag) 58 return isUnit 59 } 60 61 // AuthClient returns whether the authenticated entity is a client 62 // user. 63 func (fa FakeAuthorizer) AuthClient() bool { 64 _, isUser := fa.GetAuthTag().(names.UserTag) 65 return isUser 66 } 67 68 func (fa FakeAuthorizer) GetAuthTag() names.Tag { 69 return fa.Tag 70 } 71 72 // HasPermission returns true if the logged in user is admin or has a name equal to 73 // the pre-set admin tag. 74 func (fa FakeAuthorizer) HasPermission(operation permission.Access, target names.Tag) error { 75 if fa.Tag.Kind() == names.UserTagKind { 76 ut := fa.Tag.(names.UserTag) 77 emptyTag := names.UserTag{} 78 if fa.AdminTag != emptyTag && ut == fa.AdminTag { 79 return nil 80 } 81 if ut == fa.HasWriteTag && (operation == permission.WriteAccess || operation == permission.ReadAccess) { 82 return nil 83 } 84 85 uTag := fa.Tag.(names.UserTag) 86 var err error 87 res := nameBasedHasPermission(uTag.Name(), operation, target) 88 if !res { 89 err = errors.WithType(apiservererrors.ErrPerm, authentication.ErrorEntityMissingPermission) 90 } 91 return err 92 } 93 return errors.WithType(apiservererrors.ErrPerm, authentication.ErrorEntityMissingPermission) 94 } 95 96 // nameBasedHasPermission provides a way for tests to fake the expected outcomes of the 97 // authentication. 98 // setting permissionname as the name that user will always have the given permission. 99 // setting permissionnamemodeltagstring as the name will make that user have the given 100 // permission only in that model. 101 func nameBasedHasPermission(name string, operation permission.Access, target names.Tag) bool { 102 var perm permission.Access 103 switch { 104 case strings.HasPrefix(name, string(permission.SuperuserAccess)): 105 return operation == permission.SuperuserAccess 106 case strings.HasPrefix(name, string(permission.AddModelAccess)): 107 return operation == permission.AddModelAccess 108 case strings.HasPrefix(name, string(permission.LoginAccess)): 109 return operation == permission.LoginAccess 110 case strings.HasPrefix(name, string(permission.AdminAccess)): 111 perm = permission.AdminAccess 112 case strings.HasPrefix(name, string(permission.WriteAccess)): 113 perm = permission.WriteAccess 114 case strings.HasPrefix(name, string(permission.ConsumeAccess)): 115 perm = permission.ConsumeAccess 116 case strings.HasPrefix(name, string(permission.ReadAccess)): 117 perm = permission.ReadAccess 118 default: 119 return false 120 } 121 name = name[len(perm):] 122 if len(name) == 0 && perm == permission.AdminAccess { 123 return true 124 } 125 if len(name) == 0 { 126 return operation == perm 127 } 128 if name[0] == '-' { 129 name = name[1:] 130 } 131 targetTag, err := names.ParseTag(name) 132 if err != nil { 133 return false 134 } 135 return operation == perm && targetTag.String() == target.String() 136 } 137 138 // ConnectedModel returns the UUID of the model the current client is 139 // connected to. 140 func (fa FakeAuthorizer) ConnectedModel() string { 141 return fa.ModelUUID 142 } 143 144 // EntityHasPermission returns true if the passed entity is admin or has a name equal to 145 // the pre-set admin tag. 146 func (fa FakeAuthorizer) EntityHasPermission(entity names.Tag, operation permission.Access, _ names.Tag) error { 147 if entity.Kind() == names.UserTagKind && entity.Id() == "admin" { 148 return nil 149 } 150 emptyTag := names.UserTag{} 151 if fa.AdminTag != emptyTag && entity == fa.AdminTag { 152 return nil 153 } 154 if operation == permission.ConsumeAccess && fa.HasConsumeTag != emptyTag && entity == fa.HasConsumeTag { 155 return nil 156 } 157 return errors.WithType(apiservererrors.ErrPerm, authentication.ErrorEntityMissingPermission) 158 }