github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/stateauthenticator/modeluser.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package stateauthenticator 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "gopkg.in/juju/names.v2" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/permission" 14 "github.com/juju/juju/state" 15 ) 16 17 // loginEntity defines the interface needed to log in as a user. 18 // Notable implementations are *state.User and *modelUserEntity. 19 type loginEntity interface { 20 state.Entity 21 state.Authenticator 22 LastLogin() (time.Time, error) 23 UpdateLastLogin() error 24 } 25 26 // modelUserEntityFinder implements state.EntityFinder by returning 27 // an Entity value for model users, ensuring that the user exists in 28 // the state's current model, while also supporting external users. 29 type modelUserEntityFinder struct { 30 st *state.State 31 } 32 33 // FindEntity implements state.EntityFinder. 34 func (f modelUserEntityFinder) FindEntity(tag names.Tag) (state.Entity, error) { 35 utag, ok := tag.(names.UserTag) 36 if !ok { 37 return f.st.FindEntity(tag) 38 } 39 40 model, err := f.st.Model() 41 if err != nil { 42 return nil, errors.Trace(err) 43 } 44 modelUser, err := f.st.UserAccess(utag, model.ModelTag()) 45 if err != nil && !errors.IsNotFound(err) { 46 return nil, errors.Trace(err) 47 } 48 49 // No model user found, so see if the user has been granted 50 // access to the controller. 51 if permission.IsEmptyUserAccess(modelUser) { 52 controllerUser, err := state.ControllerAccess(f.st, utag) 53 if err != nil && !errors.IsNotFound(err) { 54 return nil, errors.Trace(err) 55 } 56 // TODO(perrito666) remove the following section about everyone group 57 // when groups are implemented, this accounts only for the lack of a local 58 // ControllerUser when logging in from an external user that has not been granted 59 // permissions on the controller but there are permissions for the special 60 // everyone group. 61 if permission.IsEmptyUserAccess(controllerUser) && !utag.IsLocal() { 62 everyoneTag := names.NewUserTag(common.EveryoneTagName) 63 controllerUser, err = f.st.UserAccess(everyoneTag, f.st.ControllerTag()) 64 if err != nil && !errors.IsNotFound(err) { 65 return nil, errors.Annotatef(err, "obtaining ControllerUser for everyone group") 66 } 67 } 68 if permission.IsEmptyUserAccess(controllerUser) { 69 return nil, errors.NotFoundf("model or controller user") 70 } 71 } 72 73 u := &modelUserEntity{ 74 st: f.st, 75 modelUser: modelUser, 76 tag: utag, 77 } 78 if utag.IsLocal() { 79 user, err := f.st.User(utag) 80 if err != nil { 81 return nil, errors.Trace(err) 82 } 83 u.user = user 84 } 85 return u, nil 86 } 87 88 // modelUserEntity encapsulates an model user 89 // and, if the user is local, the local state user 90 // as well. This enables us to implement FindEntity 91 // in such a way that the authentication mechanisms 92 // can work without knowing these details. 93 type modelUserEntity struct { 94 st *state.State 95 96 modelUser permission.UserAccess 97 user *state.User 98 tag names.Tag 99 } 100 101 // Refresh implements state.Authenticator.Refresh. 102 func (u *modelUserEntity) Refresh() error { 103 if u.user == nil { 104 return nil 105 } 106 return u.user.Refresh() 107 } 108 109 // SetPassword implements state.Authenticator.SetPassword 110 // by setting the password on the local user. 111 func (u *modelUserEntity) SetPassword(pass string) error { 112 if u.user == nil { 113 return errors.New("cannot set password on external user") 114 } 115 return u.user.SetPassword(pass) 116 } 117 118 // PasswordValid implements state.Authenticator.PasswordValid. 119 func (u *modelUserEntity) PasswordValid(pass string) bool { 120 if u.user == nil { 121 return false 122 } 123 return u.user.PasswordValid(pass) 124 } 125 126 // Tag implements state.Entity.Tag. 127 func (u *modelUserEntity) Tag() names.Tag { 128 return u.tag 129 } 130 131 // LastLogin implements loginEntity.LastLogin. 132 func (u *modelUserEntity) LastLogin() (time.Time, error) { 133 // The last connection for the model takes precedence over 134 // the local user last login time. 135 var err error 136 var t time.Time 137 138 model, err := u.st.Model() 139 if err != nil { 140 return t, errors.Trace(err) 141 } 142 143 if !permission.IsEmptyUserAccess(u.modelUser) { 144 t, err = model.LastModelConnection(u.modelUser.UserTag) 145 } else { 146 err = state.NeverConnectedError("controller user") 147 } 148 if state.IsNeverConnectedError(err) || permission.IsEmptyUserAccess(u.modelUser) { 149 if u.user != nil { 150 // There's a global user, so use that login time instead. 151 return u.user.LastLogin() 152 } 153 // Since we're implementing LastLogin, we need 154 // to implement LastLogin error semantics too. 155 err = state.NeverLoggedInError(err.Error()) 156 } 157 return t, errors.Trace(err) 158 } 159 160 // UpdateLastLogin implements loginEntity.UpdateLastLogin. 161 func (u *modelUserEntity) UpdateLastLogin() error { 162 var err error 163 164 if !permission.IsEmptyUserAccess(u.modelUser) { 165 if u.modelUser.Object.Kind() != names.ModelTagKind { 166 return errors.NotValidf("%s as model user", u.modelUser.Object.Kind()) 167 } 168 169 model, err := u.st.Model() 170 if err != nil { 171 return errors.Trace(err) 172 } 173 174 err = model.UpdateLastModelConnection(u.modelUser.UserTag) 175 } 176 177 if u.user != nil { 178 err1 := u.user.UpdateLastLogin() 179 if err == nil { 180 return err1 181 } 182 } 183 if err != nil { 184 return errors.Trace(err) 185 } 186 return nil 187 }