github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/api/agent/secretsmanager/client.go (about) 1 // Copyright 2021 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package secretsmanager 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/names/v5" 9 10 "github.com/juju/juju/api/base" 11 commonsecretbackends "github.com/juju/juju/api/common/secretbackends" 12 apiwatcher "github.com/juju/juju/api/watcher" 13 apiservererrors "github.com/juju/juju/apiserver/errors" 14 coresecrets "github.com/juju/juju/core/secrets" 15 "github.com/juju/juju/core/watcher" 16 "github.com/juju/juju/rpc/params" 17 ) 18 19 // Client is the api client for the SecretsManager facade. 20 type Client struct { 21 facade base.FacadeCaller 22 *commonsecretbackends.Client 23 } 24 25 // NewClient creates a secrets api client. 26 func NewClient(caller base.APICaller) *Client { 27 facade := base.NewFacadeCaller(caller, "SecretsManager") 28 return &Client{ 29 facade: facade, 30 Client: commonsecretbackends.NewClient(facade), 31 } 32 } 33 34 // CreateSecretURIs generates new secret URIs. 35 func (c *Client) CreateSecretURIs(count int) ([]*coresecrets.URI, error) { 36 var results params.StringResults 37 38 if count <= 0 { 39 return nil, errors.NotValidf("secret URi count %d", count) 40 } 41 if err := c.facade.FacadeCall("CreateSecretURIs", params.CreateSecretURIsArg{ 42 Count: count, 43 }, &results); err != nil { 44 return nil, errors.Trace(err) 45 } 46 if n := len(results.Results); n != count { 47 return nil, errors.Errorf("expected %d result(s), got %d", count, n) 48 } 49 uris := make([]*coresecrets.URI, count) 50 for i, s := range results.Results { 51 if err := s.Error; err != nil { 52 return nil, errors.Trace(err) 53 } 54 uri, err := coresecrets.ParseURI(s.Result) 55 if err != nil { 56 return nil, errors.Trace(err) 57 } 58 uris[i] = uri 59 } 60 return uris, nil 61 } 62 63 // WatchConsumedSecretsChanges returns a watcher which serves changes to 64 // secrets payloads for any secrets consumed by the specified unit. 65 func (c *Client) WatchConsumedSecretsChanges(unitName string) (watcher.StringsWatcher, error) { 66 var results params.StringsWatchResults 67 args := params.Entities{ 68 Entities: []params.Entity{{Tag: names.NewUnitTag(unitName).String()}}, 69 } 70 err := c.facade.FacadeCall("WatchConsumedSecretsChanges", args, &results) 71 if err != nil { 72 return nil, err 73 } 74 if len(results.Results) != 1 { 75 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 76 } 77 result := results.Results[0] 78 if result.Error != nil { 79 return nil, apiservererrors.RestoreError(result.Error) 80 } 81 w := apiwatcher.NewStringsWatcher(c.facade.RawAPICaller(), result) 82 return w, nil 83 } 84 85 // WatchObsolete returns a watcher for notifying when: 86 // - a secret owned by the entity is deleted 87 // - a secret revision owed by the entity no longer 88 // has any consumers 89 // 90 // Obsolete revisions results are "uri/revno" and deleted 91 // secret results are "uri". 92 func (c *Client) WatchObsolete(ownerTags ...names.Tag) (watcher.StringsWatcher, error) { 93 var result params.StringsWatchResult 94 args := params.Entities{Entities: make([]params.Entity, len(ownerTags))} 95 for i, tag := range ownerTags { 96 args.Entities[i] = params.Entity{Tag: tag.String()} 97 } 98 err := c.facade.FacadeCall("WatchObsolete", args, &result) 99 if err != nil { 100 return nil, err 101 } 102 if result.Error != nil { 103 return nil, apiservererrors.RestoreError(result.Error) 104 } 105 w := apiwatcher.NewStringsWatcher(c.facade.RawAPICaller(), result) 106 return w, nil 107 } 108 109 // GetConsumerSecretsRevisionInfo returns the current revision and labels for secrets consumed 110 // by the specified unit. 111 func (c *Client) GetConsumerSecretsRevisionInfo(unitName string, uris []string) (map[string]coresecrets.SecretRevisionInfo, error) { 112 var results params.SecretConsumerInfoResults 113 args := params.GetSecretConsumerInfoArgs{ 114 ConsumerTag: names.NewUnitTag(unitName).String(), 115 URIs: uris, 116 } 117 118 err := c.facade.FacadeCall("GetConsumerSecretsRevisionInfo", args, &results) 119 if err != nil { 120 return nil, err 121 } 122 if len(results.Results) != len(uris) { 123 return nil, errors.Errorf("expected %d result, got %d", len(uris), len(results.Results)) 124 } 125 info := make(map[string]coresecrets.SecretRevisionInfo) 126 for i, latest := range results.Results { 127 if err := results.Results[i].Error; err != nil { 128 // If deleted or now unauthorised, do not report any info for this url. 129 if err.Code == params.CodeNotFound || err.Code == params.CodeUnauthorized { 130 continue 131 } 132 return nil, errors.Annotatef(err, "finding latest info for secret %q", uris[i]) 133 } 134 info[uris[i]] = coresecrets.SecretRevisionInfo{ 135 Revision: latest.Revision, 136 Label: latest.Label, 137 } 138 } 139 return info, err 140 } 141 142 // SecretMetadata returns metadata for the specified secrets. 143 func (c *Client) SecretMetadata() ([]coresecrets.SecretOwnerMetadata, error) { 144 var results params.ListSecretResults 145 err := c.facade.FacadeCall("GetSecretMetadata", nil, &results) 146 if err != nil { 147 return nil, errors.Trace(err) 148 } 149 var result []coresecrets.SecretOwnerMetadata 150 for _, info := range results.Results { 151 uri, err := coresecrets.ParseURI(info.URI) 152 if err != nil { 153 return nil, errors.NotValidf("secret URI %q", info.URI) 154 } 155 md := coresecrets.SecretMetadata{ 156 URI: uri, 157 OwnerTag: info.OwnerTag, 158 Description: info.Description, 159 Label: info.Label, 160 RotatePolicy: coresecrets.RotatePolicy(info.RotatePolicy), 161 LatestRevision: info.LatestRevision, 162 LatestExpireTime: info.LatestExpireTime, 163 NextRotateTime: info.NextRotateTime, 164 } 165 for _, g := range info.Access { 166 md.Access = append(md.Access, coresecrets.AccessInfo{ 167 Target: g.TargetTag, Scope: g.ScopeTag, Role: g.Role, 168 }) 169 } 170 revisions := make([]int, len(info.Revisions)) 171 for i, r := range info.Revisions { 172 revisions[i] = r.Revision 173 } 174 result = append(result, coresecrets.SecretOwnerMetadata{ 175 Metadata: md, 176 Revisions: revisions, 177 }) 178 } 179 return result, nil 180 } 181 182 // WatchSecretsRotationChanges returns a watcher which serves changes to 183 // secrets rotation config for any secrets managed by the specified owner. 184 func (c *Client) WatchSecretsRotationChanges(ownerTags ...names.Tag) (watcher.SecretTriggerWatcher, error) { 185 var result params.SecretTriggerWatchResult 186 args := params.Entities{Entities: make([]params.Entity, len(ownerTags))} 187 for i, tag := range ownerTags { 188 args.Entities[i] = params.Entity{Tag: tag.String()} 189 } 190 err := c.facade.FacadeCall("WatchSecretsRotationChanges", args, &result) 191 if err != nil { 192 return nil, err 193 } 194 if result.Error != nil { 195 return nil, result.Error 196 } 197 w := apiwatcher.NewSecretsTriggerWatcher(c.facade.RawAPICaller(), result) 198 return w, nil 199 } 200 201 // SecretRotated records the outcome of rotating a secret. 202 func (c *Client) SecretRotated(uri string, oldRevision int) error { 203 secretUri, err := coresecrets.ParseURI(uri) 204 if err != nil { 205 return errors.Trace(err) 206 } 207 208 var results params.ErrorResults 209 args := params.SecretRotatedArgs{ 210 Args: []params.SecretRotatedArg{{ 211 URI: secretUri.String(), 212 OriginalRevision: oldRevision, 213 }}, 214 } 215 err = c.facade.FacadeCall("SecretsRotated", args, &results) 216 if err != nil { 217 return errors.Trace(err) 218 } 219 if len(results.Results) != 1 { 220 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 221 } 222 result := results.Results[0] 223 if result.Error != nil { 224 return result.Error 225 } 226 return nil 227 } 228 229 // WatchSecretRevisionsExpiryChanges returns a watcher which serves changes to 230 // secret revision expiry config for any secrets managed by the specified owner. 231 func (c *Client) WatchSecretRevisionsExpiryChanges(ownerTags ...names.Tag) (watcher.SecretTriggerWatcher, error) { 232 var result params.SecretTriggerWatchResult 233 args := params.Entities{Entities: make([]params.Entity, len(ownerTags))} 234 for i, tag := range ownerTags { 235 args.Entities[i] = params.Entity{Tag: tag.String()} 236 } 237 err := c.facade.FacadeCall("WatchSecretRevisionsExpiryChanges", args, &result) 238 if err != nil { 239 return nil, err 240 } 241 if result.Error != nil { 242 return nil, result.Error 243 } 244 w := apiwatcher.NewSecretsTriggerWatcher(c.facade.RawAPICaller(), result) 245 return w, nil 246 } 247 248 // SecretRevokeGrantArgs holds the args used to grant or revoke access to a secret. 249 // To grant access, specify one of ApplicationName or UnitName, plus optionally RelationId. 250 // To revoke access, specify one of ApplicationName or UnitName. 251 type SecretRevokeGrantArgs struct { 252 ApplicationName *string 253 UnitName *string 254 RelationKey *string 255 Role coresecrets.SecretRole 256 } 257 258 // Grant grants access to the specified secret. 259 func (c *Client) Grant(uri *coresecrets.URI, p *SecretRevokeGrantArgs) error { 260 args := grantRevokeArgsToParams(p, uri) 261 var results params.ErrorResults 262 err := c.facade.FacadeCall("SecretsGrant", args, &results) 263 if err != nil { 264 return errors.Trace(err) 265 } 266 if len(results.Results) != 1 { 267 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 268 } 269 result := results.Results[0] 270 if result.Error != nil { 271 return result.Error 272 } 273 return nil 274 } 275 276 func grantRevokeArgsToParams(p *SecretRevokeGrantArgs, secretUri *coresecrets.URI) params.GrantRevokeSecretArgs { 277 var subjectTag, scopeTag string 278 if p.ApplicationName != nil { 279 subjectTag = names.NewApplicationTag(*p.ApplicationName).String() 280 } 281 if p.UnitName != nil { 282 subjectTag = names.NewUnitTag(*p.UnitName).String() 283 } 284 if p.RelationKey != nil { 285 scopeTag = names.NewRelationTag(*p.RelationKey).String() 286 } else { 287 scopeTag = subjectTag 288 } 289 args := params.GrantRevokeSecretArgs{ 290 Args: []params.GrantRevokeSecretArg{{ 291 URI: secretUri.String(), 292 ScopeTag: scopeTag, 293 SubjectTags: []string{subjectTag}, 294 Role: string(p.Role), 295 }}, 296 } 297 return args 298 } 299 300 // Revoke revokes access to the specified secret. 301 func (c *Client) Revoke(uri *coresecrets.URI, p *SecretRevokeGrantArgs) error { 302 args := grantRevokeArgsToParams(p, uri) 303 var results params.ErrorResults 304 err := c.facade.FacadeCall("SecretsRevoke", args, &results) 305 if err != nil { 306 return errors.Trace(err) 307 } 308 if len(results.Results) != 1 { 309 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 310 } 311 result := results.Results[0] 312 if result.Error != nil { 313 return result.Error 314 } 315 return nil 316 }