github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/useraccess.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "strings" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/mgo/v3/txn" 13 "github.com/juju/names/v5" 14 15 "github.com/juju/juju/core/permission" 16 ) 17 18 type userAccessDoc struct { 19 ID string `bson:"_id"` 20 ObjectUUID string `bson:"object-uuid"` 21 UserName string `bson:"user"` 22 DisplayName string `bson:"displayname"` 23 CreatedBy string `bson:"createdby"` 24 DateCreated time.Time `bson:"datecreated"` 25 } 26 27 // UserAccessSpec defines the attributes that can be set when adding a new 28 // user access. 29 type UserAccessSpec struct { 30 User names.UserTag 31 CreatedBy names.UserTag 32 DisplayName string 33 Access permission.Access 34 } 35 36 // userAccessTarget defines the target of a user access granting. 37 type userAccessTarget struct { 38 uuid string 39 globalKey string 40 } 41 42 // AddUser adds a new user for the model to the database. 43 func (m *Model) AddUser(spec UserAccessSpec) (permission.UserAccess, error) { 44 if err := permission.ValidateModelAccess(spec.Access); err != nil { 45 return permission.UserAccess{}, errors.Annotate(err, "adding model user") 46 } 47 target := userAccessTarget{ 48 uuid: m.UUID(), 49 globalKey: modelGlobalKey, 50 } 51 return m.st.addUserAccess(spec, target) 52 } 53 54 // AddControllerUser adds a new user for the current controller to the database. 55 func (st *State) AddControllerUser(spec UserAccessSpec) (permission.UserAccess, error) { 56 if err := permission.ValidateControllerAccess(spec.Access); err != nil { 57 return permission.UserAccess{}, errors.Annotate(err, "adding controller user") 58 } 59 return st.addUserAccess(spec, userAccessTarget{globalKey: controllerGlobalKey}) 60 } 61 62 func (st *State) addUserAccess(spec UserAccessSpec, target userAccessTarget) (permission.UserAccess, error) { 63 // Ensure local user exists in state before adding them as an model user. 64 if spec.User.IsLocal() { 65 localUser, err := st.User(spec.User) 66 if err != nil { 67 return permission.UserAccess{}, errors.Annotate(err, fmt.Sprintf("user %q does not exist locally", spec.User.Name())) 68 } 69 if spec.DisplayName == "" { 70 spec.DisplayName = localUser.DisplayName() 71 } 72 } 73 74 // Ensure local createdBy user exists. 75 if spec.CreatedBy.IsLocal() { 76 if _, err := st.User(spec.CreatedBy); err != nil { 77 return permission.UserAccess{}, errors.Annotatef(err, "createdBy user %q does not exist locally", spec.CreatedBy.Name()) 78 } 79 } 80 var ( 81 ops []txn.Op 82 err error 83 targetTag names.Tag 84 ) 85 switch target.globalKey { 86 case modelGlobalKey: 87 ops = createModelUserOps( 88 target.uuid, 89 spec.User, 90 spec.CreatedBy, 91 spec.DisplayName, 92 st.nowToTheSecond(), 93 spec.Access) 94 targetTag = names.NewModelTag(target.uuid) 95 case controllerGlobalKey: 96 ops = createControllerUserOps( 97 st.ControllerUUID(), 98 spec.User, 99 spec.CreatedBy, 100 spec.DisplayName, 101 st.nowToTheSecond(), 102 spec.Access) 103 targetTag = st.controllerTag 104 default: 105 return permission.UserAccess{}, errors.NotSupportedf("user access global key %q", target.globalKey) 106 } 107 err = st.db().RunTransactionFor(target.uuid, ops) 108 if err == txn.ErrAborted { 109 err = errors.AlreadyExistsf("user access %q", spec.User.Id()) 110 } 111 if err != nil { 112 return permission.UserAccess{}, errors.Trace(err) 113 } 114 return st.UserAccess(spec.User, targetTag) 115 } 116 117 // userAccessID returns the document id of the user access. 118 func userAccessID(user names.UserTag) string { 119 username := user.Id() 120 return strings.ToLower(username) 121 } 122 123 // NewModelUserAccess returns a new permission.UserAccess for the given userDoc and 124 // current Model. 125 func NewModelUserAccess(st *State, userDoc userAccessDoc) (permission.UserAccess, error) { 126 perm, err := st.userPermission(modelKey(userDoc.ObjectUUID), userGlobalKey(strings.ToLower(userDoc.UserName))) 127 if err != nil { 128 return permission.UserAccess{}, errors.Annotate(err, "obtaining model permission") 129 } 130 return newUserAccess(perm, userDoc, names.NewModelTag(userDoc.ObjectUUID)), nil 131 } 132 133 // NewControllerUserAccess returns a new permission.UserAccess for the given userDoc and 134 // current Controller. 135 func NewControllerUserAccess(st *State, userDoc userAccessDoc) (permission.UserAccess, error) { 136 perm, err := st.userPermission(controllerKey(st.ControllerUUID()), userGlobalKey(strings.ToLower(userDoc.UserName))) 137 if err != nil { 138 return permission.UserAccess{}, errors.Annotate(err, "obtaining controller permission") 139 } 140 return newUserAccess(perm, userDoc, names.NewControllerTag(userDoc.ObjectUUID)), nil 141 } 142 143 // UserPermission returns the access permission for the passed subject and target. 144 func (st *State) UserPermission(subject names.UserTag, target names.Tag) (permission.Access, error) { 145 if err := st.userMayHaveAccess(subject); err != nil { 146 return "", errors.Trace(err) 147 } 148 149 switch target.Kind() { 150 case names.ModelTagKind, names.ControllerTagKind: 151 access, err := st.UserAccess(subject, target) 152 if err != nil { 153 return "", errors.Trace(err) 154 } 155 return access.Access, nil 156 case names.ApplicationOfferTagKind: 157 return st.GetOfferAccess(target.Id(), subject) 158 case names.CloudTagKind: 159 return st.GetCloudAccess(target.Id(), subject) 160 default: 161 return "", errors.NotValidf("%q as a target", target.Kind()) 162 } 163 } 164 165 func newUserAccess(perm *userPermission, userDoc userAccessDoc, object names.Tag) permission.UserAccess { 166 return permission.UserAccess{ 167 UserID: userDoc.ID, 168 UserTag: names.NewUserTag(userDoc.UserName), 169 Object: object, 170 Access: perm.access(), 171 CreatedBy: names.NewUserTag(userDoc.CreatedBy), 172 DateCreated: userDoc.DateCreated.UTC(), 173 DisplayName: userDoc.DisplayName, 174 UserName: userDoc.UserName, 175 } 176 } 177 178 func (st *State) userMayHaveAccess(tag names.UserTag) error { 179 if !tag.IsLocal() { 180 // external users may have access 181 return nil 182 } 183 localUser, err := st.User(tag) 184 if err != nil { 185 return errors.Trace(err) 186 } 187 // Since deleted users will throw an error above, we need to check whether the user has been disabled here. 188 if localUser.IsDisabled() { 189 return errors.Errorf("user %q is disabled", tag.Id()) 190 } 191 return nil 192 } 193 194 // UserAccess returns a new permission.UserAccess for the passed subject and target. 195 func (st *State) UserAccess(subject names.UserTag, target names.Tag) (permission.UserAccess, error) { 196 if err := st.userMayHaveAccess(subject); err != nil { 197 return permission.UserAccess{}, errors.Trace(err) 198 } 199 200 var ( 201 userDoc userAccessDoc 202 err error 203 ) 204 switch target.Kind() { 205 case names.ModelTagKind: 206 userDoc, err = st.modelUser(target.Id(), subject) 207 if err == nil { 208 return NewModelUserAccess(st, userDoc) 209 } 210 case names.ControllerTagKind: 211 userDoc, err = st.controllerUser(subject) 212 if err == nil { 213 return NewControllerUserAccess(st, userDoc) 214 } 215 default: 216 return permission.UserAccess{}, errors.NotValidf("%q as a target", target.Kind()) 217 } 218 return permission.UserAccess{}, errors.Trace(err) 219 } 220 221 // SetUserAccess sets <access> level on <target> to <subject>. 222 func (st *State) SetUserAccess(subject names.UserTag, target names.Tag, access permission.Access) (permission.UserAccess, error) { 223 err := access.Validate() 224 if err != nil { 225 return permission.UserAccess{}, errors.Trace(err) 226 } 227 switch target.Kind() { 228 case names.ModelTagKind: 229 err = st.setModelAccess(access, userGlobalKey(userAccessID(subject)), target.Id()) 230 case names.ControllerTagKind: 231 err = st.setControllerAccess(access, userGlobalKey(userAccessID(subject))) 232 default: 233 return permission.UserAccess{}, errors.NotValidf("%q as a target", target.Kind()) 234 } 235 if err != nil { 236 return permission.UserAccess{}, errors.Trace(err) 237 } 238 return st.UserAccess(subject, target) 239 } 240 241 // RemoveUserAccess removes access for subject to the passed tag. 242 func (st *State) RemoveUserAccess(subject names.UserTag, target names.Tag) error { 243 switch target.Kind() { 244 case names.ModelTagKind: 245 return errors.Trace(st.removeModelUser(subject)) 246 case names.ControllerTagKind: 247 return errors.Trace(st.removeControllerUser(subject)) 248 } 249 return errors.NotValidf("%q as a target", target.Kind()) 250 }