github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/password.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/names/v5" 10 11 apiservererrors "github.com/juju/juju/apiserver/errors" 12 "github.com/juju/juju/rpc/params" 13 "github.com/juju/juju/state" 14 ) 15 16 var logger = loggo.GetLogger("juju.apiserver.common") 17 18 // PasswordChanger implements a common SetPasswords method for use by 19 // various facades. 20 type PasswordChanger struct { 21 st state.EntityFinder 22 getCanChange GetAuthFunc 23 } 24 25 // NewPasswordChanger returns a new PasswordChanger. The GetAuthFunc will be 26 // used on each invocation of SetPasswords to determine current permissions. 27 func NewPasswordChanger(st state.EntityFinder, getCanChange GetAuthFunc) *PasswordChanger { 28 return &PasswordChanger{ 29 st: st, 30 getCanChange: getCanChange, 31 } 32 } 33 34 // SetPasswords sets the given password for each supplied entity, if possible. 35 func (pc *PasswordChanger) SetPasswords(args params.EntityPasswords) (params.ErrorResults, error) { 36 result := params.ErrorResults{ 37 Results: make([]params.ErrorResult, len(args.Changes)), 38 } 39 if len(args.Changes) == 0 { 40 return result, nil 41 } 42 canChange, err := pc.getCanChange() 43 if err != nil { 44 return params.ErrorResults{}, errors.Trace(err) 45 } 46 for i, param := range args.Changes { 47 tag, err := names.ParseTag(param.Tag) 48 if err != nil { 49 result.Results[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm) 50 continue 51 } 52 if !canChange(tag) { 53 result.Results[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm) 54 continue 55 } 56 if err := pc.setPassword(tag, param.Password); err != nil { 57 result.Results[i].Error = apiservererrors.ServerError(err) 58 } 59 } 60 return result, nil 61 } 62 63 func (pc *PasswordChanger) setMongoPassword(entity state.Entity, password string) error { 64 type mongoPassworder interface { 65 SetMongoPassword(password string) error 66 } 67 // We set the mongo password first on the grounds that 68 // if it fails, the agent in question should still be able 69 // to authenticate to another API server and ask it to change 70 // its password. 71 if entity0, ok := entity.(mongoPassworder); ok { 72 if err := entity0.SetMongoPassword(password); err != nil { 73 return err 74 } 75 logger.Infof("setting mongo password for %q", entity.Tag()) 76 return nil 77 } 78 // TODO(dfc) fix 79 return apiservererrors.NotSupportedError(entity.Tag(), "mongo access") 80 } 81 82 func (pc *PasswordChanger) setPassword(tag names.Tag, password string) error { 83 type isManager interface { 84 IsManager() bool 85 } 86 var err error 87 entity0, err := pc.st.FindEntity(tag) 88 if err != nil { 89 return err 90 } 91 entity, ok := entity0.(state.Authenticator) 92 if !ok { 93 return apiservererrors.NotSupportedError(tag, "authentication") 94 } 95 if entity, ok := entity0.(isManager); ok && entity.IsManager() { 96 err = pc.setMongoPassword(entity0, password) 97 } 98 if err == nil { 99 err = entity.SetPassword(password) 100 logger.Infof("setting password for %q", tag) 101 } 102 return err 103 }