github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/secretbackends/secrets.go (about) 1 // Copyright 2022 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package secretbackends 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/errors" 11 "github.com/juju/names/v5" 12 13 commonsecrets "github.com/juju/juju/apiserver/common/secrets" 14 apiservererrors "github.com/juju/juju/apiserver/errors" 15 "github.com/juju/juju/apiserver/facade" 16 "github.com/juju/juju/core/permission" 17 "github.com/juju/juju/core/secrets" 18 "github.com/juju/juju/rpc/params" 19 "github.com/juju/juju/secrets/provider" 20 _ "github.com/juju/juju/secrets/provider/all" 21 "github.com/juju/juju/secrets/provider/juju" 22 "github.com/juju/juju/state" 23 ) 24 25 // SecretBackendsAPI is the server implementation for the SecretBackends facade. 26 type SecretBackendsAPI struct { 27 authorizer facade.Authorizer 28 controllerUUID string 29 30 clock clock.Clock 31 backendState SecretsBackendState 32 secretState SecretsState 33 statePool StatePool 34 } 35 36 func (s *SecretBackendsAPI) checkCanAdmin() error { 37 return s.authorizer.HasPermission(permission.SuperuserAccess, names.NewControllerTag(s.controllerUUID)) 38 } 39 40 // AddSecretBackends adds new secret backends. 41 func (s *SecretBackendsAPI) AddSecretBackends(args params.AddSecretBackendArgs) (params.ErrorResults, error) { 42 result := params.ErrorResults{ 43 Results: make([]params.ErrorResult, len(args.Args)), 44 } 45 if err := s.checkCanAdmin(); err != nil { 46 return result, errors.Trace(err) 47 } 48 for i, arg := range args.Args { 49 err := s.createBackend(arg.ID, arg.SecretBackend) 50 result.Results[i].Error = apiservererrors.ServerError(err) 51 } 52 return result, nil 53 } 54 55 func (s *SecretBackendsAPI) createBackend(id string, arg params.SecretBackend) error { 56 if arg.Name == "" { 57 return errors.NotValidf("missing backend name") 58 } 59 if arg.Name == juju.BackendName || arg.Name == provider.Auto { 60 return errors.NotValidf("backend %q") 61 } 62 p, err := commonsecrets.GetProvider(arg.BackendType) 63 if err != nil { 64 return errors.Annotatef(err, "creating backend provider type %q", arg.BackendType) 65 } 66 configValidator, ok := p.(provider.ProviderConfig) 67 if ok { 68 defaults := configValidator.ConfigDefaults() 69 if arg.Config == nil && len(defaults) > 0 { 70 arg.Config = make(map[string]interface{}) 71 } 72 for k, v := range defaults { 73 if _, ok := arg.Config[k]; !ok { 74 arg.Config[k] = v 75 } 76 } 77 err = configValidator.ValidateConfig(nil, arg.Config) 78 if err != nil { 79 return errors.Annotatef(err, "invalid config for provider %q", arg.BackendType) 80 } 81 } 82 if err := commonsecrets.PingBackend(p, arg.Config); err != nil { 83 return errors.Trace(err) 84 } 85 86 var nextRotateTime *time.Time 87 if arg.TokenRotateInterval != nil && *arg.TokenRotateInterval > 0 { 88 if !provider.HasAuthRefresh(p) { 89 return errors.NotSupportedf("token refresh on secret backend of type %q", p.Type()) 90 } 91 nextRotateTime, err = secrets.NextBackendRotateTime(s.clock.Now(), *arg.TokenRotateInterval) 92 if err != nil { 93 return errors.Trace(err) 94 } 95 } 96 _, err = s.backendState.CreateSecretBackend(state.CreateSecretBackendParams{ 97 ID: id, 98 Name: arg.Name, 99 BackendType: arg.BackendType, 100 TokenRotateInterval: arg.TokenRotateInterval, 101 NextRotateTime: nextRotateTime, 102 Config: arg.Config, 103 }) 104 if errors.IsAlreadyExists(err) { 105 return errors.AlreadyExistsf("secret backend with ID %q", id) 106 } 107 return errors.Trace(err) 108 } 109 110 // UpdateSecretBackends updates secret backends. 111 func (s *SecretBackendsAPI) UpdateSecretBackends(args params.UpdateSecretBackendArgs) (params.ErrorResults, error) { 112 result := params.ErrorResults{ 113 Results: make([]params.ErrorResult, len(args.Args)), 114 } 115 if err := s.checkCanAdmin(); err != nil { 116 return result, errors.Trace(err) 117 } 118 for i, arg := range args.Args { 119 err := s.updateBackend(arg) 120 result.Results[i].Error = apiservererrors.ServerError(err) 121 } 122 return result, nil 123 } 124 125 func (s *SecretBackendsAPI) updateBackend(arg params.UpdateSecretBackendArg) error { 126 if arg.Name == "" { 127 return errors.NotValidf("missing backend name") 128 } 129 if arg.Name == juju.BackendName || arg.Name == provider.Auto { 130 return errors.NotValidf("backend %q") 131 } 132 existing, err := s.backendState.GetSecretBackend(arg.Name) 133 if err != nil { 134 return errors.Trace(err) 135 } 136 p, err := commonsecrets.GetProvider(existing.BackendType) 137 if err != nil { 138 return errors.Trace(err) 139 } 140 141 cfg := make(map[string]interface{}) 142 for k, v := range existing.Config { 143 cfg[k] = v 144 } 145 for k, v := range arg.Config { 146 cfg[k] = v 147 } 148 for _, k := range arg.Reset { 149 delete(cfg, k) 150 } 151 configValidator, ok := p.(provider.ProviderConfig) 152 if ok { 153 defaults := configValidator.ConfigDefaults() 154 for _, k := range arg.Reset { 155 if defaultVal, ok := defaults[k]; ok { 156 cfg[k] = defaultVal 157 } 158 } 159 err = configValidator.ValidateConfig(existing.Config, cfg) 160 if err != nil { 161 return errors.Annotatef(err, "invalid config for provider %q", existing.BackendType) 162 } 163 } 164 if !arg.Force { 165 if err := commonsecrets.PingBackend(p, cfg); err != nil { 166 return errors.Trace(err) 167 } 168 } 169 var nextRotateTime *time.Time 170 if arg.TokenRotateInterval != nil && *arg.TokenRotateInterval > 0 { 171 if !provider.HasAuthRefresh(p) { 172 return errors.NotSupportedf("token refresh on secret backend of type %q", p.Type()) 173 } 174 nextRotateTime, err = secrets.NextBackendRotateTime(s.clock.Now(), *arg.TokenRotateInterval) 175 if err != nil { 176 return errors.Trace(err) 177 } 178 } 179 err = s.backendState.UpdateSecretBackend(state.UpdateSecretBackendParams{ 180 ID: existing.ID, 181 NameChange: arg.NameChange, 182 TokenRotateInterval: arg.TokenRotateInterval, 183 NextRotateTime: nextRotateTime, 184 Config: cfg, 185 }) 186 if errors.IsNotFound(err) { 187 return errors.NotFoundf("secret backend %q", arg.Name) 188 } 189 return err 190 } 191 192 // ListSecretBackends lists available secret backends. 193 func (s *SecretBackendsAPI) ListSecretBackends(arg params.ListSecretBackendsArgs) (params.ListSecretBackendsResults, error) { 194 result := params.ListSecretBackendsResults{} 195 if arg.Reveal { 196 if err := s.checkCanAdmin(); err != nil { 197 return result, errors.Trace(err) 198 } 199 } 200 201 results, err := commonsecrets.BackendSummaryInfo( 202 s.statePool, s.backendState, s.secretState, s.controllerUUID, arg.Reveal, commonsecrets.BackendFilter{Names: arg.Names, All: true}) 203 if err != nil { 204 return params.ListSecretBackendsResults{}, errors.Trace(err) 205 } 206 result.Results = results 207 return result, nil 208 } 209 210 // RemoveSecretBackends removes secret backends. 211 func (s *SecretBackendsAPI) RemoveSecretBackends(args params.RemoveSecretBackendArgs) (params.ErrorResults, error) { 212 result := params.ErrorResults{ 213 Results: make([]params.ErrorResult, len(args.Args)), 214 } 215 if err := s.checkCanAdmin(); err != nil { 216 return result, errors.Trace(err) 217 } 218 for i, arg := range args.Args { 219 err := s.removeBackend(arg) 220 result.Results[i].Error = apiservererrors.ServerError(err) 221 } 222 return result, nil 223 } 224 225 func (s *SecretBackendsAPI) removeBackend(arg params.RemoveSecretBackendArg) error { 226 if arg.Name == "" { 227 return errors.NotValidf("missing backend name") 228 } 229 if arg.Name == juju.BackendName || arg.Name == provider.Auto { 230 return errors.NotValidf("backend %q") 231 } 232 return s.backendState.DeleteSecretBackend(arg.Name, arg.Force) 233 }