github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/secrets.go (about) 1 // Copyright 2021 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/juju/collections/set" 13 "github.com/juju/errors" 14 "github.com/juju/mgo/v3" 15 "github.com/juju/mgo/v3/bson" 16 "github.com/juju/mgo/v3/txn" 17 "github.com/juju/names/v5" 18 jujutxn "github.com/juju/txn/v3" 19 "gopkg.in/tomb.v2" 20 21 "github.com/juju/juju/core/leadership" 22 "github.com/juju/juju/core/secrets" 23 corewatcher "github.com/juju/juju/core/watcher" 24 "github.com/juju/juju/mongo/utils" 25 "github.com/juju/juju/state/watcher" 26 ) 27 28 // LabelExists is returned when a duplicate label is used. 29 const LabelExists = errors.ConstError("label exists") 30 31 // CreateSecretParams are used to create a secret. 32 type CreateSecretParams struct { 33 UpdateSecretParams 34 35 Version int 36 Owner names.Tag 37 } 38 39 // UpdateSecretParams are used to update a secret. 40 type UpdateSecretParams struct { 41 LeaderToken leadership.Token 42 RotatePolicy *secrets.RotatePolicy 43 NextRotateTime *time.Time 44 ExpireTime *time.Time 45 Description *string 46 Label *string 47 Params map[string]interface{} 48 Data secrets.SecretData 49 ValueRef *secrets.ValueRef 50 AutoPrune *bool 51 } 52 53 func (u *UpdateSecretParams) hasUpdate() bool { 54 return u.NextRotateTime != nil || 55 u.RotatePolicy != nil || 56 u.Description != nil || 57 u.Label != nil || 58 u.ExpireTime != nil || 59 len(u.Data) > 0 || 60 u.ValueRef != nil || 61 len(u.Params) > 0 || 62 u.AutoPrune != nil 63 } 64 65 // ChangeSecretBackendParams are used to change the backend of a secret. 66 type ChangeSecretBackendParams struct { 67 Token leadership.Token 68 URI *secrets.URI 69 Revision int 70 ValueRef *secrets.ValueRef 71 Data secrets.SecretData 72 } 73 74 // SecretsFilter holds attributes to match when listing secrets. 75 type SecretsFilter struct { 76 URI *secrets.URI 77 Label *string 78 OwnerTags []names.Tag 79 ConsumerTags []names.Tag 80 } 81 82 // SecretsStore instances use mongo as a secrets store. 83 type SecretsStore interface { 84 CreateSecret(*secrets.URI, CreateSecretParams) (*secrets.SecretMetadata, error) 85 UpdateSecret(*secrets.URI, UpdateSecretParams) (*secrets.SecretMetadata, error) 86 DeleteSecret(*secrets.URI, ...int) ([]secrets.ValueRef, error) 87 GetSecret(*secrets.URI) (*secrets.SecretMetadata, error) 88 GetSecretValue(*secrets.URI, int) (secrets.SecretValue, *secrets.ValueRef, error) 89 ListSecrets(SecretsFilter) ([]*secrets.SecretMetadata, error) 90 ListModelSecrets(bool) (map[string]set.Strings, error) 91 ListSecretRevisions(uri *secrets.URI) ([]*secrets.SecretRevisionMetadata, error) 92 ListUnusedSecretRevisions(uri *secrets.URI) ([]int, error) 93 GetSecretRevision(uri *secrets.URI, revision int) (*secrets.SecretRevisionMetadata, error) 94 WatchObsolete(owners []names.Tag) (StringsWatcher, error) 95 WatchRevisionsToPrune(ownerTags []names.Tag) (StringsWatcher, error) 96 ChangeSecretBackend(ChangeSecretBackendParams) error 97 SecretGrants(uri *secrets.URI, role secrets.SecretRole) ([]secrets.AccessInfo, error) 98 } 99 100 // NewSecrets creates a new mongo backed secrets store. 101 func NewSecrets(st *State) *secretsStore { 102 return &secretsStore{st: st} 103 } 104 105 type secretMetadataDoc struct { 106 DocID string `bson:"_id"` 107 108 Version int `bson:"version"` 109 OwnerTag string `bson:"owner-tag"` 110 111 Description string `bson:"description"` 112 Label string `bson:"label"` 113 114 // LatestRevision is denormalised here - it is the 115 // revision of the latest revision doc, 116 LatestRevision int `bson:"latest-revision"` 117 // LatestExpireTime is denormalised here - it is the 118 // expire time of the latest revision doc, 119 LatestExpireTime *time.Time `bson:"latest-expire-time"` 120 121 RotatePolicy string `bson:"rotate-policy"` 122 123 CreateTime time.Time `bson:"create-time"` 124 UpdateTime time.Time `bson:"update-time"` 125 126 // AutoPrune is true if the secret revisions should be pruned when it's not been used. 127 AutoPrune bool `bson:"auto-prune"` 128 } 129 130 type valueRefDoc struct { 131 BackendID string `bson:"backend-id"` 132 RevisionID string `bson:"revision-id"` 133 } 134 135 type secretRevisionDoc struct { 136 DocID string `bson:"_id"` 137 TxnRevno int64 `bson:"txn-revno"` 138 139 Revision int `bson:"revision"` 140 CreateTime time.Time `bson:"create-time"` 141 UpdateTime time.Time `bson:"update-time"` 142 ExpireTime *time.Time `bson:"expire-time,omitempty"` 143 Obsolete bool `bson:"obsolete"` 144 Data secretsDataMap `bson:"data"` 145 ValueRef *valueRefDoc `bson:"value-reference,omitempty"` 146 147 // PendingDelete is true if the revision is to be deleted. 148 // It will not be drained to a new active backend. 149 PendingDelete bool `bson:"pending-delete"` 150 151 // OwnerTag is denormalised here so that watchers do not need 152 // to do an extra query on the secret metadata collection to 153 // filter on owner. 154 OwnerTag string `bson:"owner-tag"` 155 } 156 157 type secretsDataMap map[string]interface{} 158 159 func (m *secretsDataMap) SetBSON(raw bson.Raw) error { 160 rawMap := make(map[string]interface{}) 161 if err := raw.Unmarshal(rawMap); err != nil { 162 return err 163 } 164 *m = utils.UnescapeKeys(rawMap) 165 return nil 166 } 167 168 func (m secretsDataMap) GetBSON() (interface{}, error) { 169 escapedMap := utils.EscapeKeys(m) 170 return escapedMap, nil 171 } 172 173 type secretsStore struct { 174 st *State 175 } 176 177 func ptr[T any](v T) *T { 178 return &v 179 } 180 181 func toValue[T any](v *T) T { 182 if v == nil { 183 return *new(T) 184 } 185 return *v 186 } 187 188 func (s *secretsStore) secretMetadataDoc(uri *secrets.URI, p *CreateSecretParams) (*secretMetadataDoc, error) { 189 now := s.st.nowToTheSecond() 190 md := &secretMetadataDoc{ 191 DocID: uri.ID, 192 Version: p.Version, 193 OwnerTag: p.Owner.String(), 194 CreateTime: now, 195 UpdateTime: now, 196 } 197 _, err := names.ParseTag(md.OwnerTag) 198 if err != nil { 199 return nil, errors.Annotate(err, "invalid owner tag") 200 } 201 err = s.updateSecretMetadataDoc(md, &p.UpdateSecretParams) 202 return md, err 203 } 204 205 func (s *secretsStore) updateSecretMetadataDoc(doc *secretMetadataDoc, p *UpdateSecretParams) error { 206 if p.Description != nil { 207 doc.Description = toValue(p.Description) 208 } 209 if p.Label != nil { 210 doc.Label = toValue(p.Label) 211 } 212 if p.AutoPrune != nil { 213 doc.AutoPrune = *p.AutoPrune 214 } 215 if p.RotatePolicy != nil { 216 doc.RotatePolicy = string(toValue(p.RotatePolicy)) 217 } 218 hasData := len(p.Data) > 0 || p.ValueRef != nil 219 if p.ExpireTime != nil || hasData { 220 if p.ExpireTime == nil || p.ExpireTime.IsZero() { 221 doc.LatestExpireTime = nil 222 } else { 223 doc.LatestExpireTime = ptr(toValue(p.ExpireTime).Round(time.Second).UTC()) 224 } 225 } 226 if hasData { 227 doc.LatestRevision++ 228 } 229 doc.UpdateTime = s.st.nowToTheSecond() 230 return nil 231 } 232 233 func secretRevisionKey(uri *secrets.URI, revision int) string { 234 return fmt.Sprintf("%s/%d", uri.ID, revision) 235 } 236 237 func splitSecretRevision(c string) (string, int) { 238 parts := strings.Split(c, "/") 239 if len(parts) < 2 { 240 return parts[0], 0 241 } 242 rev, _ := strconv.Atoi(parts[1]) 243 return parts[0], rev 244 } 245 246 func (s *secretsStore) secretRevisionDoc(uri *secrets.URI, owner string, revision int, expireTime *time.Time, data secrets.SecretData, valueRef *secrets.ValueRef) *secretRevisionDoc { 247 dataCopy := make(secretsDataMap) 248 for k, v := range data { 249 dataCopy[k] = v 250 } 251 now := s.st.nowToTheSecond() 252 var valRefDoc *valueRefDoc 253 if valueRef != nil { 254 valRefDoc = &valueRefDoc{ 255 BackendID: valueRef.BackendID, 256 RevisionID: valueRef.RevisionID, 257 } 258 } 259 doc := &secretRevisionDoc{ 260 DocID: secretRevisionKey(uri, revision), 261 Revision: revision, 262 OwnerTag: owner, 263 CreateTime: now, 264 UpdateTime: now, 265 Data: dataCopy, 266 ValueRef: valRefDoc, 267 } 268 if expireTime != nil { 269 expire := expireTime.Round(time.Second).UTC() 270 doc.ExpireTime = &expire 271 } 272 return doc 273 } 274 275 // CreateSecret creates a new secret. 276 func (s *secretsStore) CreateSecret(uri *secrets.URI, p CreateSecretParams) (*secrets.SecretMetadata, error) { 277 if len(p.Data) == 0 && p.ValueRef == nil { 278 return nil, errors.New("cannot create a secret without content") 279 } 280 metadataDoc, err := s.secretMetadataDoc(uri, &p) 281 if err != nil { 282 return nil, errors.Trace(err) 283 } 284 revision := 1 285 valueDoc := s.secretRevisionDoc(uri, p.Owner.String(), revision, p.ExpireTime, p.Data, p.ValueRef) 286 // OwnerTag has already been validated. 287 owner, _ := names.ParseTag(metadataDoc.OwnerTag) 288 entity, scopeCollName, scopeDocID, err := s.st.findSecretEntity(owner) 289 if err != nil { 290 return nil, errors.Annotate(err, "invalid owner reference") 291 } 292 if entity.Life() != Alive { 293 return nil, errors.Errorf("cannot create secret for owner %q which is not alive", owner) 294 } 295 isOwnerAliveOp := txn.Op{ 296 C: scopeCollName, 297 Id: scopeDocID, 298 Assert: isAliveDoc, 299 } 300 301 buildTxn := func(attempt int) ([]txn.Op, error) { 302 var ops []txn.Op 303 if p.Label != nil { 304 uniqueLabelOps, err := s.st.uniqueSecretOwnerLabelOps(owner, *p.Label) 305 if err != nil { 306 return nil, errors.Trace(err) 307 } 308 ops = append(ops, uniqueLabelOps...) 309 } 310 if attempt > 0 { 311 if _, _, err := s.getSecretValue(uri, revision, false); err == nil { 312 return nil, errors.AlreadyExistsf("secret value for %q", uri.String()) 313 } 314 } 315 ops = append(ops, []txn.Op{ 316 { 317 C: secretMetadataC, 318 Id: metadataDoc.DocID, 319 Assert: txn.DocMissing, 320 Insert: *metadataDoc, 321 }, { 322 C: secretRevisionsC, 323 Id: valueDoc.DocID, 324 Assert: txn.DocMissing, 325 Insert: *valueDoc, 326 }, isOwnerAliveOp, 327 }...) 328 if valueDoc.ValueRef != nil { 329 refOps, err := s.st.incBackendRevisionCountOps(valueDoc.ValueRef.BackendID, 1) 330 if err != nil { 331 return nil, errors.Trace(err) 332 } 333 ops = append(ops, refOps...) 334 } 335 if p.NextRotateTime != nil { 336 rotateOps, err := s.secretRotationOps(uri, metadataDoc.OwnerTag, p.RotatePolicy, p.NextRotateTime) 337 if err != nil { 338 return nil, errors.Trace(err) 339 } 340 ops = append(ops, rotateOps...) 341 } 342 return ops, nil 343 } 344 err = s.st.db().Run(buildTxnWithLeadership(buildTxn, p.LeaderToken)) 345 if err != nil { 346 return nil, errors.Trace(err) 347 } 348 return s.toSecretMetadata(metadataDoc, p.NextRotateTime) 349 } 350 351 func (st *State) checkExists(uri *secrets.URI) error { 352 secretMetadataCollection, closer := st.db().GetCollection(secretMetadataC) 353 defer closer() 354 n, err := secretMetadataCollection.FindId(uri.ID).Count() 355 if err != nil { 356 return errors.Trace(err) 357 } 358 if n == 0 { 359 return errors.NotFoundf("secret %q", uri.String()) 360 } 361 return nil 362 } 363 364 // UpdateSecret updates an existing secret. 365 func (s *secretsStore) UpdateSecret(uri *secrets.URI, p UpdateSecretParams) (*secrets.SecretMetadata, error) { 366 if !p.hasUpdate() { 367 return nil, errors.New("must specify a new value or metadata to update a secret") 368 } 369 // Used later but look up early and return if it fails. 370 nextRotateTime, err := s.st.nextRotateTime(uri.ID) 371 if err != nil { 372 return nil, errors.Trace(err) 373 } 374 375 secretMetadataCollection, closer := s.st.db().GetCollection(secretMetadataC) 376 defer closer() 377 378 // Pre-process the expire time update. 379 haveExpireTime := false 380 newExpireTime := p.ExpireTime 381 if newExpireTime != nil { 382 haveExpireTime = true 383 if newExpireTime.IsZero() { 384 newExpireTime = nil 385 } 386 } 387 388 var metadataDoc secretMetadataDoc 389 buildTxn := func(attempt int) ([]txn.Op, error) { 390 err := secretMetadataCollection.FindId(uri.ID).One(&metadataDoc) 391 if err == mgo.ErrNotFound { 392 return nil, errors.NotFoundf("secret %q", uri.String()) 393 } 394 if err != nil { 395 return nil, errors.Trace(err) 396 } 397 var ops []txn.Op 398 if p.Label != nil && *p.Label != metadataDoc.Label { 399 // OwnerTag has already been validated. 400 owner, _ := names.ParseTag(metadataDoc.OwnerTag) 401 if metadataDoc.Label != "" { 402 removeOldLabelOps, err := s.st.removeOwnerSecretLabelOps(owner, metadataDoc.Label) 403 if err != nil { 404 return nil, errors.Trace(err) 405 } 406 ops = append(ops, removeOldLabelOps...) 407 } 408 uniqueLabelOps, err := s.st.uniqueSecretOwnerLabelOps(owner, *p.Label) 409 if err != nil { 410 return nil, errors.Trace(err) 411 } 412 ops = append(ops, uniqueLabelOps...) 413 } 414 currentRevision := metadataDoc.LatestRevision 415 if err := s.updateSecretMetadataDoc(&metadataDoc, &p); err != nil { 416 if err != nil { 417 return nil, errors.Trace(err) 418 } 419 } 420 ops = append(ops, []txn.Op{ 421 { 422 C: secretMetadataC, 423 Id: metadataDoc.DocID, 424 Assert: bson.D{{"latest-revision", currentRevision}}, 425 Update: bson.M{"$set": metadataDoc}, 426 }, 427 }...) 428 _, _, err = s.getSecretValue(uri, metadataDoc.LatestRevision, false) 429 revisionExists := err == nil 430 if !revisionExists && !errors.IsNotFound(err) { 431 return nil, errors.Trace(err) 432 } 433 if len(p.Data) > 0 || p.ValueRef != nil { 434 if revisionExists { 435 return nil, errors.AlreadyExistsf("secret value with revision %d for %q", metadataDoc.LatestRevision, uri.String()) 436 } 437 revisionDoc := s.secretRevisionDoc(uri, metadataDoc.OwnerTag, metadataDoc.LatestRevision, newExpireTime, p.Data, p.ValueRef) 438 ops = append(ops, txn.Op{ 439 C: secretRevisionsC, 440 Id: revisionDoc.DocID, 441 Assert: txn.DocMissing, 442 Insert: *revisionDoc, 443 }) 444 if p.ValueRef != nil { 445 refOps, err := s.st.incBackendRevisionCountOps(p.ValueRef.BackendID, 1) 446 if err != nil { 447 return nil, errors.Trace(err) 448 } 449 ops = append(ops, refOps...) 450 } 451 // Ensure no new consumers are added while update is in progress. 452 countOps, err := s.st.checkConsumerCountOps(uri, 0) 453 if err != nil { 454 return nil, errors.Trace(err) 455 } 456 ops = append(ops, countOps...) 457 458 updateConsumersOps, err := s.st.secretUpdateConsumersOps(secretConsumersC, uri, metadataDoc.LatestRevision) 459 if err != nil { 460 return nil, errors.Trace(err) 461 } 462 ops = append(ops, updateConsumersOps...) 463 464 updateRemoteConsumersOps, err := s.st.secretUpdateConsumersOps(secretRemoteConsumersC, uri, metadataDoc.LatestRevision) 465 if err != nil { 466 return nil, errors.Trace(err) 467 } 468 ops = append(ops, updateRemoteConsumersOps...) 469 470 // Saving a new revision might result in the previous latest revision 471 // being obsolete if it had not been read yet. 472 obsoleteOps, err := s.st.markObsoleteRevisionOps(uri, "", revisionDoc.Revision) 473 if err != nil { 474 return nil, errors.Trace(err) 475 } 476 ops = append(ops, obsoleteOps...) 477 478 } else if haveExpireTime { 479 if !revisionExists { 480 return nil, errors.NotFoundf("reversion %d for secret %q", metadataDoc.LatestRevision, uri.String()) 481 } 482 // If the expire time is being removed, it needs to be unset. 483 toSet := bson.D{{"update-time", s.st.nowToTheSecond()}} 484 if newExpireTime != nil { 485 toSet = append(toSet, bson.DocElem{"expire-time", newExpireTime}) 486 } 487 var toUnset bson.D 488 if newExpireTime == nil { 489 toUnset = bson.D{{"expire-time", nil}} 490 } 491 updates := bson.D{{"$set", toSet}} 492 if len(toUnset) > 0 { 493 updates = append(updates, bson.DocElem{"$unset", toUnset}) 494 } 495 ops = append(ops, txn.Op{ 496 C: secretRevisionsC, 497 Id: secretRevisionKey(uri, metadataDoc.LatestRevision), 498 Assert: txn.DocExists, 499 Update: updates, 500 }) 501 } 502 if p.RotatePolicy != nil || p.NextRotateTime != nil { 503 rotateOps, err := s.secretRotationOps(uri, metadataDoc.OwnerTag, p.RotatePolicy, p.NextRotateTime) 504 if err != nil { 505 return nil, errors.Trace(err) 506 } 507 ops = append(ops, rotateOps...) 508 } 509 return ops, nil 510 } 511 err = s.st.db().Run(buildTxnWithLeadership(buildTxn, p.LeaderToken)) 512 if err != nil { 513 return nil, errors.Trace(err) 514 } 515 return s.toSecretMetadata(&metadataDoc, nextRotateTime) 516 } 517 518 func (st *State) nextRotateTime(docID string) (*time.Time, error) { 519 secretRotateCollection, closer := st.db().GetCollection(secretRotateC) 520 defer closer() 521 522 var rotateDoc secretRotationDoc 523 err := secretRotateCollection.FindId(docID).One(&rotateDoc) 524 if err == mgo.ErrNotFound { 525 return nil, nil 526 } 527 if err != nil { 528 return nil, errors.Trace(err) 529 } 530 return &rotateDoc.NextRotateTime, nil 531 } 532 533 func (s *secretsStore) toSecretMetadata(doc *secretMetadataDoc, nextRotateTime *time.Time) (*secrets.SecretMetadata, error) { 534 uri, err := secrets.ParseURI(s.st.localID(doc.DocID)) 535 if err != nil { 536 return nil, errors.Trace(err) 537 } 538 return &secrets.SecretMetadata{ 539 URI: uri, 540 Version: doc.Version, 541 RotatePolicy: secrets.RotatePolicy(doc.RotatePolicy), 542 NextRotateTime: nextRotateTime, 543 LatestRevision: doc.LatestRevision, 544 LatestExpireTime: doc.LatestExpireTime, 545 Description: doc.Description, 546 Label: doc.Label, 547 OwnerTag: doc.OwnerTag, 548 AutoPrune: doc.AutoPrune, 549 CreateTime: doc.CreateTime, 550 UpdateTime: doc.UpdateTime, 551 }, nil 552 } 553 554 // DeleteSecret deletes the specified secret revisions. 555 // If revisions is nil or the last remaining revisions are 556 // removed, the entire secret is deleted and the return bool is true. 557 // Also returned are any references to content stored in an external 558 // backend for any deleted revisions. 559 func (s *secretsStore) DeleteSecret(uri *secrets.URI, revisions ...int) (external []secrets.ValueRef, err error) { 560 return s.st.deleteSecrets([]*secrets.URI{uri}, revisions...) 561 } 562 563 func (st *State) deleteSecrets(uris []*secrets.URI, revisions ...int) (external []secrets.ValueRef, err error) { 564 // We will bulk delete the various artefacts, starting with the secret itself. 565 // Deleting the parent secret metadata first will ensure that any consumers of 566 // the secret get notified and subsequent attempts to access any secret 567 // attributes (revision etc) return not found. 568 // It is not practical to do this record by record in a legacy client side mgo txn operation. 569 if len(uris) == 0 && len(revisions) == 0 { 570 // Nothing to remove. 571 return nil, nil 572 } 573 574 if len(uris) == 0 || len(uris) > 1 && len(revisions) > 0 { 575 return nil, errors.Errorf("PROGRAMMING ERROR: invalid secret deletion args uris=%v, revisions=%v", uris, revisions) 576 } 577 session := st.MongoSession() 578 err = session.StartTransaction() 579 if err != nil { 580 return nil, errors.Trace(err) 581 } 582 defer func() { 583 if err == nil { 584 err = session.CommitTransaction() 585 return 586 } 587 if err2 := session.AbortTransaction(); err2 != nil { 588 logger.Warningf("aborting failed delete select transaction: %v", err2) 589 } 590 }() 591 592 // If we're not deleting all revisions for a secret, just remove the affected 593 // revision docs and exit early. 594 if len(revisions) > 0 { 595 uri := uris[0] 596 597 secretRevisionsCollection, closer := st.db().GetCollection(secretRevisionsC) 598 defer closer() 599 600 var savedRevisionDocs []secretRevisionDoc 601 err := secretRevisionsCollection.Find(bson.D{{"_id", 602 bson.D{{"$regex", fmt.Sprintf("%s/.*", uri.ID)}}}}).Select( 603 bson.D{{"revision", 1}, {"value-reference", 1}}).All(&savedRevisionDocs) 604 if err != nil { 605 return nil, errors.Annotatef(err, "counting revisions for %s", uri.String()) 606 } 607 externalRevisionCounts := make(map[string]int) 608 toDelete := set.NewInts(revisions...) 609 savedRevisions := set.NewInts() 610 for _, r := range savedRevisionDocs { 611 savedRevisions.Add(r.Revision) 612 if !toDelete.Contains(r.Revision) { 613 continue 614 } 615 if r.ValueRef != nil { 616 external = append(external, secrets.ValueRef{ 617 BackendID: r.ValueRef.BackendID, 618 RevisionID: r.ValueRef.RevisionID, 619 }) 620 externalRevisionCounts[r.ValueRef.BackendID] = externalRevisionCounts[r.ValueRef.BackendID] + 1 621 } 622 } 623 if savedRevisions.Difference(toDelete).Size() > 0 { 624 revs := make([]string, len(revisions)) 625 for i, r := range revisions { 626 revs[i] = strconv.Itoa(r) 627 } 628 revisionRegexp := fmt.Sprintf("(%s)", strings.Join(revs, "|")) 629 _, err = secretRevisionsCollection.Writeable().RemoveAll(bson.D{{ 630 "_id", bson.D{{"$regex", fmt.Sprintf("%s/%s", uri.ID, revisionRegexp)}}, 631 }}) 632 if err != nil { 633 return nil, errors.Annotatef(err, "deleting revisions for %s", uri.String()) 634 } 635 // Decrement the count of secret revisions stored in the external backends. 636 // This allows backends without stored revisions to be removed without using force. 637 globalRefCountsCollection, closer := st.db().GetCollection(globalRefcountsC) 638 defer closer() 639 for backendID, count := range externalRevisionCounts { 640 if secrets.IsInternalSecretBackendID(backendID) { 641 continue 642 } 643 err = globalRefCountsCollection.Writeable().UpdateId( 644 secretBackendRefCountKey(backendID), 645 bson.D{{"$inc", bson.D{{"refcount", -1 * count}}}}) 646 if err != nil { 647 return nil, errors.Annotatef(err, "updating backend refcounts for %s", uri.String()) 648 } 649 } 650 return nil, nil 651 } 652 } 653 654 for _, uri := range uris { 655 deletedExternal, err := st.deleteOne(uri) 656 if err != nil { 657 return nil, errors.Annotatef(err, "deleting secret %q", uri.String()) 658 } 659 // Don't collate the external revisions twice. 660 // If specific revisions are being removed, the external 661 // references have already been added. 662 if len(revisions) == 0 { 663 external = append(external, deletedExternal...) 664 } 665 } 666 return external, nil 667 } 668 669 func (st *State) deleteOne(uri *secrets.URI) (external []secrets.ValueRef, _ error) { 670 secretMetadataCollection, closer := st.db().GetCollection(secretMetadataC) 671 defer closer() 672 673 secretRevisionsCollection, closer := st.db().GetCollection(secretRevisionsC) 674 defer closer() 675 676 var md secretMetadataDoc 677 err := secretMetadataCollection.FindId(uri.ID).One(&md) 678 if err == mgo.ErrNotFound { 679 return nil, nil 680 } 681 if err != nil { 682 return nil, errors.Trace(err) 683 } 684 _, err = secretMetadataCollection.Writeable().RemoveAll(bson.D{{ 685 "_id", uri.ID, 686 }}) 687 if err != nil { 688 return nil, errors.Annotatef(err, "deleting revisions for %s", uri.String()) 689 } 690 691 secretRotateCollection, closer := st.db().GetCollection(secretRotateC) 692 defer closer() 693 _, err = secretRotateCollection.Writeable().RemoveAll(bson.D{{ 694 "_id", uri.ID, 695 }}) 696 if err != nil { 697 return nil, errors.Annotatef(err, "deleting revisions for %s", uri.String()) 698 } 699 700 var savedRevisionDocs []secretRevisionDoc 701 externalRevisionCounts := make(map[string]int) 702 err = secretRevisionsCollection.Find(bson.D{{"_id", 703 bson.D{{"$regex", fmt.Sprintf("%s/.*", uri.ID)}}}}).Select( 704 bson.D{{"revision", 1}, {"value-reference", 1}}).All(&savedRevisionDocs) 705 if err != nil { 706 return nil, errors.Annotatef(err, "reading revisions for %s", uri.String()) 707 } 708 for _, r := range savedRevisionDocs { 709 if r.ValueRef != nil { 710 external = append(external, secrets.ValueRef{ 711 BackendID: r.ValueRef.BackendID, 712 RevisionID: r.ValueRef.RevisionID, 713 }) 714 externalRevisionCounts[r.ValueRef.BackendID] = externalRevisionCounts[r.ValueRef.BackendID] + 1 715 } 716 } 717 _, err = secretRevisionsCollection.Writeable().RemoveAll(bson.D{{ 718 "_id", bson.D{{"$regex", fmt.Sprintf("%s/.*", uri.ID)}}, 719 }}) 720 if err != nil { 721 return nil, errors.Annotatef(err, "deleting revisions for %s", uri.String()) 722 } 723 724 secretPermissionsCollection, closer := st.db().GetCollection(secretPermissionsC) 725 defer closer() 726 _, err = secretPermissionsCollection.Writeable().RemoveAll(bson.D{{ 727 "_id", bson.D{{"$regex", fmt.Sprintf("%s#.*", uri.ID)}}, 728 }}) 729 if err != nil { 730 return nil, errors.Annotatef(err, "deleting permissions for %s", uri.String()) 731 } 732 733 if err = st.removeSecretConsumerInfo(uri); err != nil { 734 return nil, errors.Trace(err) 735 } 736 if err = st.removeSecretRemoteConsumerInfo(uri); err != nil { 737 return nil, errors.Trace(err) 738 } 739 740 refCountsCollection, closer := st.db().GetCollection(refcountsC) 741 defer closer() 742 _, err = refCountsCollection.Writeable().RemoveAll(bson.D{{ 743 "_id", fmt.Sprintf("%s#%s", uri.ID, "consumer"), 744 }}) 745 if err != nil { 746 return nil, errors.Annotatef(err, "deleting consumer refcounts for %s", uri.String()) 747 } 748 749 // Decrement the count of secret revisions stored in the external backends. 750 // This allows backends without stored revisions to be removed without using force. 751 globalRefCountsCollection, closer := st.db().GetCollection(globalRefcountsC) 752 defer closer() 753 for backendID, count := range externalRevisionCounts { 754 if secrets.IsInternalSecretBackendID(backendID) { 755 continue 756 } 757 err = globalRefCountsCollection.Writeable().UpdateId( 758 secretBackendRefCountKey(backendID), 759 bson.D{{"$inc", bson.D{{"refcount", -1 * count}}}}) 760 if err != nil { 761 return nil, errors.Annotatef(err, "updating backend refcounts for %s", uri.String()) 762 } 763 } 764 765 if md.Label != "" { 766 owner, _ := names.ParseTag(md.OwnerTag) 767 _, err = refCountsCollection.Writeable().RemoveAll(bson.D{{ 768 "_id", secretOwnerLabelKey(owner, md.Label), 769 }}) 770 if err != nil { 771 return nil, errors.Annotatef(err, "deleting owner label refcounts for %s", uri.String()) 772 } 773 } 774 return external, nil 775 } 776 777 // GetSecretValue gets the secret value for the specified URL. 778 func (s *secretsStore) GetSecretValue(uri *secrets.URI, revision int) (secrets.SecretValue, *secrets.ValueRef, error) { 779 return s.getSecretValue(uri, revision, true) 780 } 781 782 func (s *secretsStore) getSecretValue(uri *secrets.URI, revision int, checkExists bool) (secrets.SecretValue, *secrets.ValueRef, error) { 783 if checkExists { 784 if err := s.st.checkExists(uri); err != nil { 785 return nil, nil, errors.Trace(err) 786 } 787 } 788 secretValuesCollection, closer := s.st.db().GetCollection(secretRevisionsC) 789 defer closer() 790 791 var doc secretRevisionDoc 792 key := secretRevisionKey(uri, revision) 793 err := secretValuesCollection.FindId(key).One(&doc) 794 if err == mgo.ErrNotFound { 795 return nil, nil, errors.NotFoundf("secret revision %q", key) 796 } 797 if err != nil { 798 return nil, nil, errors.Trace(err) 799 } 800 data := make(secrets.SecretData) 801 for k, v := range doc.Data { 802 data[k] = fmt.Sprintf("%v", v) 803 } 804 var valueRef *secrets.ValueRef 805 if doc.ValueRef != nil { 806 valueRef = &secrets.ValueRef{ 807 BackendID: doc.ValueRef.BackendID, 808 RevisionID: doc.ValueRef.RevisionID, 809 } 810 } 811 return secrets.NewSecretValue(data), valueRef, nil 812 } 813 814 // ChangeSecretBackend updates the backend ID for the provided secret revision. 815 func (s *secretsStore) ChangeSecretBackend(arg ChangeSecretBackendParams) error { 816 if err := s.st.checkExists(arg.URI); err != nil { 817 return errors.Trace(err) 818 } 819 820 secretRevisionsCollection, closer := s.st.db().GetCollection(secretRevisionsC) 821 defer closer() 822 var doc secretRevisionDoc 823 key := secretRevisionKey(arg.URI, arg.Revision) 824 err := secretRevisionsCollection.FindId(key).One(&doc) 825 if err == mgo.ErrNotFound { 826 return errors.NotFoundf("secret revision %q", key) 827 } 828 if err != nil { 829 return errors.Trace(err) 830 } 831 832 dataCopy := make(secretsDataMap) 833 for k, v := range arg.Data { 834 dataCopy[k] = v 835 } 836 var valRefDoc *valueRefDoc 837 if arg.ValueRef != nil { 838 valRefDoc = &valueRefDoc{ 839 BackendID: arg.ValueRef.BackendID, 840 RevisionID: arg.ValueRef.RevisionID, 841 } 842 } 843 844 buildTxn := func(attempt int) ([]txn.Op, error) { 845 var ops []txn.Op 846 if doc.ValueRef != nil { 847 refOps, err := s.st.decSecretBackendRefCountOp(doc.ValueRef.BackendID) 848 if err != nil { 849 return nil, errors.Trace(err) 850 } 851 ops = append(ops, refOps...) 852 } 853 if valRefDoc != nil { 854 refOps, err := s.st.incBackendRevisionCountOps(valRefDoc.BackendID, 1) 855 if err != nil { 856 return nil, errors.Trace(err) 857 } 858 ops = append(ops, refOps...) 859 } 860 return append(ops, txn.Op{ 861 C: secretRevisionsC, 862 Id: doc.DocID, 863 Assert: txn.DocExists, 864 Update: bson.M{"$set": bson.M{"value-reference": valRefDoc, "data": dataCopy}}, 865 }), nil 866 } 867 err = s.st.db().Run(buildTxnWithLeadership(buildTxn, arg.Token)) 868 return errors.Trace(err) 869 } 870 871 // SecretGrants returns the list of access information of the secret for the specified role. 872 func (s *secretsStore) SecretGrants(uri *secrets.URI, role secrets.SecretRole) ([]secrets.AccessInfo, error) { 873 secretPermissionsCollection, closer := s.st.db().GetCollection(secretPermissionsC) 874 defer closer() 875 876 if err := s.st.checkExists(uri); err != nil { 877 return nil, errors.Trace(err) 878 } 879 880 var docs []secretPermissionDoc 881 err := secretPermissionsCollection.Find( 882 bson.M{ 883 "_id": bson.M{ 884 "$regex": fmt.Sprintf("%s#.*", uri.ID), 885 }, 886 "role": role, 887 }, 888 ).All(&docs) 889 if err != nil { 890 return nil, errors.Annotatef(err, "cannot retrieve secret permissions for %s", uri.String()) 891 } 892 var results []secrets.AccessInfo 893 for _, doc := range docs { 894 results = append(results, secrets.AccessInfo{ 895 Target: doc.Subject, 896 Scope: doc.Scope, 897 Role: secrets.SecretRole(doc.Role), 898 }) 899 } 900 return results, nil 901 } 902 903 // GetSecret gets the secret metadata for the specified URL. 904 func (s *secretsStore) GetSecret(uri *secrets.URI) (*secrets.SecretMetadata, error) { 905 if uri == nil { 906 return nil, errors.NewNotValid(nil, "empty URI") 907 } 908 909 secretMetadataCollection, closer := s.st.db().GetCollection(secretMetadataC) 910 defer closer() 911 912 var doc secretMetadataDoc 913 err := secretMetadataCollection.FindId(uri.ID).One(&doc) 914 if err == mgo.ErrNotFound { 915 return nil, errors.NotFoundf("secret %q", uri.String()) 916 } 917 if err != nil { 918 return nil, errors.Trace(err) 919 } 920 nextRotateTime, err := s.st.nextRotateTime(uri.ID) 921 if err != nil { 922 return nil, errors.Trace(err) 923 } 924 return s.toSecretMetadata(&doc, nextRotateTime) 925 } 926 927 func secretOwnerTerm(owners []string) bson.DocElem { 928 return bson.DocElem{Name: "owner-tag", Value: bson.D{{Name: "$in", Value: owners}}} 929 } 930 931 // ListSecrets list the secrets using the specified filter. 932 func (s *secretsStore) ListSecrets(filter SecretsFilter) ([]*secrets.SecretMetadata, error) { 933 secretMetadataCollection, closer := s.st.db().GetCollection(secretMetadataC) 934 defer closer() 935 936 var docs []secretMetadataDoc 937 q := bson.D{} 938 if filter.URI != nil { 939 q = append(q, bson.DocElem{"_id", filter.URI.ID}) 940 } 941 if filter.Label != nil { 942 q = append(q, bson.DocElem{"label", *filter.Label}) 943 } 944 if len(filter.OwnerTags) > 0 { 945 owners := make([]string, len(filter.OwnerTags)) 946 for i, tag := range filter.OwnerTags { 947 owners[i] = tag.String() 948 } 949 q = append(q, secretOwnerTerm(owners)) 950 } 951 // Only query here if we want everything or no consumers were specified. 952 // We need to do the consumer processing below as the results are seeded 953 // from a different collection. 954 if len(q) > 0 || len(filter.ConsumerTags) == 0 { 955 err := secretMetadataCollection.Find(q).All(&docs) 956 if err != nil { 957 return nil, errors.Trace(err) 958 } 959 } 960 result := make([]*secrets.SecretMetadata, len(docs)) 961 for i, doc := range docs { 962 nextRotateTime, err := s.st.nextRotateTime(doc.DocID) 963 if err != nil { 964 return nil, errors.Trace(err) 965 } 966 result[i], err = s.toSecretMetadata(&doc, nextRotateTime) 967 if err != nil { 968 return nil, errors.Trace(err) 969 } 970 } 971 if len(filter.ConsumerTags) == 0 { 972 return result, nil 973 } 974 consumers := make([]string, len(filter.ConsumerTags)) 975 for i, tag := range filter.ConsumerTags { 976 consumers[i] = tag.String() 977 } 978 consumedIds, err := s.listConsumedSecrets(consumers) 979 if err != nil { 980 return nil, errors.Trace(err) 981 } 982 983 docs = []secretMetadataDoc(nil) 984 q2 := bson.M{"_id": bson.M{"$in": consumedIds}} 985 err = secretMetadataCollection.Find(q2).All(&docs) 986 if err != nil { 987 return nil, errors.Trace(err) 988 } 989 for _, doc := range docs { 990 nextRotateTime, err := s.st.nextRotateTime(doc.DocID) 991 if err != nil { 992 return nil, errors.Trace(err) 993 } 994 md, err := s.toSecretMetadata(&doc, nextRotateTime) 995 if err != nil { 996 return nil, errors.Trace(err) 997 } 998 result = append(result, md) 999 } 1000 return result, nil 1001 } 1002 1003 // allModelRevisions uses a raw collection to load secret revisions for all models. 1004 func (s *secretsStore) allModelRevisions() ([]secretRevisionDoc, error) { 1005 var docs []secretRevisionDoc 1006 secretRevisionCollection, closer := s.st.db().GetRawCollection(secretRevisionsC) 1007 defer closer() 1008 1009 err := secretRevisionCollection.Find(nil).Select(bson.D{{"_id", 1}, {"value-reference", 1}}).All(&docs) 1010 if err != nil { 1011 return nil, errors.Trace(err) 1012 } 1013 return docs, nil 1014 } 1015 1016 // modelRevisions uses a warpped collection to load secret revisions for the current model. 1017 func (s *secretsStore) modelRevisions() ([]secretRevisionDoc, error) { 1018 var docs []secretRevisionDoc 1019 secretRevisionCollection, closer := s.st.db().GetCollection(secretRevisionsC) 1020 defer closer() 1021 1022 err := secretRevisionCollection.Find(nil).Select(bson.D{{"_id", 1}, {"value-reference", 1}}).All(&docs) 1023 if err != nil { 1024 return nil, errors.Trace(err) 1025 } 1026 return docs, nil 1027 } 1028 1029 // ListModelSecrets returns a map of backend id to secret uris stored in that backend. 1030 // If all is true, secrets for all models are included, else just the current model. 1031 func (s *secretsStore) ListModelSecrets(all bool) (map[string]set.Strings, error) { 1032 var ( 1033 docs []secretRevisionDoc 1034 err error 1035 ) 1036 if all { 1037 docs, err = s.allModelRevisions() 1038 } else { 1039 docs, err = s.modelRevisions() 1040 } 1041 if err != nil { 1042 return nil, errors.Annotate(err, "reading secret revisions") 1043 } 1044 1045 controllerUUID := s.st.ControllerUUID() 1046 result := make(map[string]set.Strings) 1047 for _, doc := range docs { 1048 // Deal with the raw doc id. 1049 parts := strings.SplitN(doc.DocID, ":", 2) 1050 if len(parts) < 2 { 1051 continue 1052 } 1053 uriStr, _ := splitSecretRevision(parts[1]) 1054 backendID := controllerUUID 1055 if doc.ValueRef != nil { 1056 backendID = doc.ValueRef.BackendID 1057 } 1058 if _, ok := result[backendID]; !ok { 1059 result[backendID] = set.NewStrings(uriStr) 1060 continue 1061 } 1062 result[backendID].Add(uriStr) 1063 } 1064 return result, nil 1065 } 1066 1067 func (s *secretsStore) listConsumedSecrets(consumers []string) ([]string, error) { 1068 secretPermissionsCollection, closer := s.st.db().GetCollection(secretPermissionsC) 1069 defer closer() 1070 var docs []secretPermissionDoc 1071 err := secretPermissionsCollection.Find(bson.M{ 1072 "subject-tag": bson.M{ 1073 "$in": consumers, 1074 }, 1075 "role": secrets.RoleView, 1076 }).Select(bson.D{{"_id", 1}}).All(&docs) 1077 if err != nil { 1078 return nil, errors.Annotatef(err, "reading permissions for %q", consumers) 1079 } 1080 var ids []string 1081 for _, doc := range docs { 1082 id := s.st.localID(doc.DocID) 1083 ids = append(ids, strings.Split(id, "#")[0]) 1084 } 1085 return ids, nil 1086 } 1087 1088 // allSecretPermissions is used for model export. 1089 func (s *secretsStore) allSecretPermissions() ([]secretPermissionDoc, error) { 1090 secretPermissionCollection, closer := s.st.db().GetCollection(secretPermissionsC) 1091 defer closer() 1092 1093 var docs []secretPermissionDoc 1094 1095 err := secretPermissionCollection.Find(nil).All(&docs) 1096 if err != nil { 1097 return nil, errors.Trace(err) 1098 } 1099 return docs, nil 1100 } 1101 1102 // ListSecretRevisions returns the revision metadata for the given secret. 1103 func (s *secretsStore) ListSecretRevisions(uri *secrets.URI) ([]*secrets.SecretRevisionMetadata, error) { 1104 return s.listSecretRevisions(uri, nil) 1105 } 1106 1107 // ListUnusedSecretRevisions returns the revision numbers that are not consumered by any applications. 1108 func (s *secretsStore) ListUnusedSecretRevisions(uri *secrets.URI) ([]int, error) { 1109 docs, err := s.listSecretRevisionDocs(uri, nil) 1110 if err != nil { 1111 return nil, errors.Trace(err) 1112 } 1113 var revisions []int 1114 for _, doc := range docs { 1115 if doc.Obsolete { 1116 revisions = append(revisions, doc.Revision) 1117 } 1118 } 1119 return revisions, nil 1120 } 1121 1122 // GetSecretRevision returns the specified revision metadata for the given secret. 1123 func (s *secretsStore) GetSecretRevision(uri *secrets.URI, revision int) (*secrets.SecretRevisionMetadata, error) { 1124 rev, err := s.listSecretRevisions(uri, &revision) 1125 if err != nil { 1126 return nil, errors.Trace(err) 1127 } 1128 if len(rev) == 0 { 1129 return nil, errors.NotFoundf("revison %d for secret %q", revision, uri) 1130 } 1131 return rev[0], nil 1132 } 1133 1134 func (s *secretsStore) listSecretRevisionDocs(uri *secrets.URI, revision *int) ([]secretRevisionDoc, error) { 1135 secretRevisionCollection, closer := s.st.db().GetCollection(secretRevisionsC) 1136 defer closer() 1137 1138 var ( 1139 docs []secretRevisionDoc 1140 q interface{} 1141 ) 1142 1143 if revision == nil { 1144 q = bson.D{{"_id", bson.D{{"$regex", uri.ID + "/.*"}}}} 1145 } else { 1146 q = bson.D{{"_id", secretRevisionKey(uri, *revision)}} 1147 } 1148 err := secretRevisionCollection.Find(q).Sort("_id").All(&docs) 1149 if err != nil { 1150 return nil, errors.Trace(err) 1151 } 1152 return docs, nil 1153 } 1154 1155 func (s *secretsStore) listSecretRevisions(uri *secrets.URI, revision *int) ([]*secrets.SecretRevisionMetadata, error) { 1156 docs, err := s.listSecretRevisionDocs(uri, revision) 1157 if err != nil { 1158 return nil, errors.Trace(err) 1159 } 1160 1161 secretBackendsColl, closer := s.st.db().GetCollection(secretBackendsC) 1162 defer closer() 1163 1164 backendNames := make(map[string]string) 1165 result := make([]*secrets.SecretRevisionMetadata, len(docs)) 1166 for i, doc := range docs { 1167 var ( 1168 valueRef *secrets.ValueRef 1169 backendName *string 1170 ) 1171 if doc.ValueRef != nil { 1172 valueRef = &secrets.ValueRef{ 1173 BackendID: doc.ValueRef.BackendID, 1174 RevisionID: doc.ValueRef.RevisionID, 1175 } 1176 if doc.ValueRef.BackendID != s.st.ModelUUID() { 1177 name, ok := backendNames[doc.ValueRef.BackendID] 1178 if !ok { 1179 var backendDoc secretBackendDoc 1180 err := secretBackendsColl.FindId(doc.ValueRef.BackendID).One(&backendDoc) 1181 if err == nil { 1182 name = backendDoc.Name 1183 } else { 1184 name = "unknown" 1185 } 1186 backendNames[doc.ValueRef.BackendID] = name 1187 } 1188 backendName = &name 1189 } 1190 } 1191 result[i] = &secrets.SecretRevisionMetadata{ 1192 Revision: doc.Revision, 1193 ValueRef: valueRef, 1194 BackendName: backendName, 1195 CreateTime: doc.CreateTime, 1196 UpdateTime: doc.UpdateTime, 1197 ExpireTime: doc.ExpireTime, 1198 } 1199 } 1200 return result, nil 1201 } 1202 1203 // allSecretRevisions is used for model export. 1204 func (s *secretsStore) allSecretRevisions() ([]secretRevisionDoc, error) { 1205 secretRevisionCollection, closer := s.st.db().GetCollection(secretRevisionsC) 1206 defer closer() 1207 1208 var docs []secretRevisionDoc 1209 1210 err := secretRevisionCollection.Find(nil).All(&docs) 1211 if err != nil { 1212 return nil, errors.Trace(err) 1213 } 1214 return docs, nil 1215 } 1216 1217 type secretConsumerDoc struct { 1218 DocID string `bson:"_id"` 1219 1220 ConsumerTag string `bson:"consumer-tag"` 1221 Label string `bson:"label"` 1222 CurrentRevision int `bson:"current-revision"` 1223 1224 // LatestRevision is denormalised here so that the 1225 // consumer watcher can be triggered when a new 1226 // secret revision is added. 1227 LatestRevision int `bson:"latest-revision"` 1228 } 1229 1230 func (st *State) secretConsumerKey(uri *secrets.URI, consumer string) string { 1231 if uri.IsLocal(st.ModelUUID()) { 1232 return fmt.Sprintf("%s#%s", uri.ID, consumer) 1233 } 1234 return fmt.Sprintf("%s/%s#%s", uri.SourceUUID, uri.ID, consumer) 1235 } 1236 1237 func splitSecretConsumerKey(key string) (string, string) { 1238 parts := strings.Split(key, "#") 1239 if len(parts) != 2 { 1240 return "", "" 1241 } 1242 return parts[0], parts[1] 1243 } 1244 1245 // checkConsumerCountOps returns txn ops to ensure that no new secrets consumers 1246 // are added whilst a txn is in progress. 1247 func (st *State) checkConsumerCountOps(uri *secrets.URI, inc int) ([]txn.Op, error) { 1248 refCountCollection, ccloser := st.db().GetCollection(refcountsC) 1249 defer ccloser() 1250 1251 key := fmt.Sprintf("%s#consumer", uri.ID) 1252 countOp, _, err := nsRefcounts.CurrentOp(refCountCollection, key) 1253 if err != nil { 1254 return nil, errors.Trace(err) 1255 } 1256 // If not incrementing the consumer count, just ensure the count is stable. 1257 if inc == 0 { 1258 return []txn.Op{countOp}, nil 1259 } 1260 if inc > 0 { 1261 incOp, err := nsRefcounts.CreateOrIncRefOp(refCountCollection, key, inc) 1262 if err != nil { 1263 return nil, errors.Trace(err) 1264 } 1265 return []txn.Op{countOp, incOp}, nil 1266 } 1267 incOp := nsRefcounts.JustIncRefOp(refcountsC, key, inc) 1268 return []txn.Op{countOp, incOp}, nil 1269 } 1270 1271 const ( 1272 secretOwnerLabelKeyPrefix = "secretOwnerlabel" 1273 secretConsumerLabelKeyPrefix = "secretConsumerlabel" 1274 ) 1275 1276 func secretOwnerLabelKey(ownerTag names.Tag, label string) string { 1277 return fmt.Sprintf("%s#%s#%s", secretOwnerLabelKeyPrefix, ownerTag.String(), label) 1278 } 1279 1280 func secretConsumerLabelKey(consumerTag names.Tag, label string) string { 1281 return fmt.Sprintf("%s#%s#%s", secretConsumerLabelKeyPrefix, consumerTag.String(), label) 1282 } 1283 1284 func (st *State) uniqueSecretOwnerLabelOps(ownerTag names.Tag, label string) (ops []txn.Op, err error) { 1285 if ops, err = st.uniqueSecretLabelBaseOps(ownerTag, label); err != nil { 1286 return nil, errors.Trace(err) 1287 } 1288 1289 // Check that there is no consumer with the same label. 1290 assertNoConsumerLabel, err := st.uniqueSecretLabelOpsRaw(ownerTag, label, "consumer", secretConsumerLabelKey, true) 1291 if err != nil { 1292 return nil, errors.Trace(err) 1293 } 1294 ops = append(ops, assertNoConsumerLabel...) 1295 // Check that there is no owner with the same label. 1296 ops2, err := st.uniqueSecretLabelOpsRaw(ownerTag, label, "owner", secretOwnerLabelKey, false) 1297 if err != nil { 1298 return nil, errors.Trace(err) 1299 } 1300 return append(ops, ops2...), nil 1301 } 1302 1303 func (st *State) uniqueSecretConsumerLabelOps(consumerTag names.Tag, label string) (ops []txn.Op, err error) { 1304 if ops, err = st.uniqueSecretLabelBaseOps(consumerTag, label); err != nil { 1305 return nil, errors.Trace(err) 1306 } 1307 1308 // Check that there is no owner with the same label. 1309 assertNoOwnerLabel, err := st.uniqueSecretLabelOpsRaw(consumerTag, label, "owner", secretOwnerLabelKey, true) 1310 if err != nil { 1311 return nil, errors.Trace(err) 1312 } 1313 ops = append(ops, assertNoOwnerLabel...) 1314 // Check that there is no consumer with the same label. 1315 ops2, err := st.uniqueSecretLabelOpsRaw(consumerTag, label, "consumer", secretConsumerLabelKey, false) 1316 if err != nil { 1317 return nil, errors.Trace(err) 1318 } 1319 return append(ops, ops2...), nil 1320 } 1321 1322 // uniqueSecretLabelBaseOps is used when creating or updating a secret with an owner label, or 1323 // when saving a secret consumer record. It checks that the label is not used twice: 1324 // - a unit of the same application consuming an application owned secret cannot use the same label 1325 // as is used in the secret metadata of any application owned secret. 1326 // The check is done when creating a new application owned secret, or saving a consumer record. 1327 func (st *State) uniqueSecretLabelBaseOps(tag names.Tag, label string) (ops []txn.Op, _ error) { 1328 col, close := st.db().GetCollection(refcountsC) 1329 defer close() 1330 1331 var ( 1332 keyPattern string 1333 errorMsg string 1334 ) 1335 1336 switch tag := tag.(type) { 1337 case names.ApplicationTag: 1338 // Ensure no units use this label for both owner and consumer label. 1339 keyPattern = fmt.Sprintf( 1340 "^%s:(%s|%s)#unit-%s-[0-9]+#%s$", 1341 st.ModelUUID(), secretOwnerLabelKeyPrefix, secretConsumerLabelKeyPrefix, tag.Name, label, 1342 ) 1343 errorMsg = fmt.Sprintf("secret label %q for a unit of application %q already exists", label, tag.Name) 1344 case names.UnitTag: 1345 // Ensure no application owned secret uses this label. 1346 applicationName, _ := names.UnitApplication(tag.Id()) 1347 appTag := names.NewApplicationTag(applicationName) 1348 1349 keyPattern = fmt.Sprintf( 1350 "^%s:(%s|%s)#%s#%s$", 1351 st.ModelUUID(), secretOwnerLabelKeyPrefix, secretConsumerLabelKeyPrefix, appTag.String(), label, 1352 ) 1353 errorMsg = fmt.Sprintf("secret label %q for application %q already exists", label, applicationName) 1354 case names.ModelTag: 1355 keyPattern = fmt.Sprintf("^%s:%s#%s#%s$", st.ModelUUID(), secretOwnerLabelKeyPrefix, tag.String(), label) 1356 errorMsg = fmt.Sprintf("user secret label %q already exists", label) 1357 default: 1358 return nil, errors.NotSupportedf("tag type %T", tag) 1359 } 1360 1361 count, err := col.Find(bson.M{"_id": bson.M{"$regex": keyPattern}}).Count() 1362 if err != nil { 1363 return nil, errors.Trace(err) 1364 } 1365 if count > 0 { 1366 return nil, errors.WithType(errors.New(errorMsg), LabelExists) 1367 } 1368 1369 return []txn.Op{ 1370 { 1371 C: col.Name(), 1372 Id: bson.M{"$regex": keyPattern}, 1373 Assert: txn.DocMissing, 1374 }, 1375 }, nil 1376 } 1377 1378 func (st *State) uniqueSecretLabelOpsRaw(tag names.Tag, label, role string, keyGenerator func(names.Tag, string) string, assertionOnly bool) ([]txn.Op, error) { 1379 refCountCollection, ccloser := st.db().GetCollection(refcountsC) 1380 defer ccloser() 1381 1382 key := keyGenerator(tag, label) 1383 countOp, count, err := nsRefcounts.CurrentOp(refCountCollection, key) 1384 if err != nil { 1385 return nil, errors.Trace(err) 1386 } 1387 if count > 0 { 1388 return nil, errors.WithType(errors.Errorf("secret label %q for %s %q already exists", label, role, tag), LabelExists) 1389 } 1390 if assertionOnly { 1391 // We only assert the doc doesn't exist but donot create the doc. 1392 return []txn.Op{countOp}, nil 1393 } 1394 incOp, err := nsRefcounts.CreateOrIncRefOp(refCountCollection, key, 1) 1395 if err != nil { 1396 return nil, errors.Trace(err) 1397 } 1398 return []txn.Op{countOp, incOp}, nil 1399 } 1400 1401 func (st *State) removeOwnerSecretLabelOps(ownerTag names.Tag, label string) ([]txn.Op, error) { 1402 refCountCollection, ccloser := st.db().GetCollection(refcountsC) 1403 defer ccloser() 1404 1405 key := secretOwnerLabelKey(ownerTag, label) 1406 countOp, count, err := nsRefcounts.CurrentOp(refCountCollection, key) 1407 if err != nil { 1408 return nil, errors.Trace(err) 1409 } 1410 if count == 0 { 1411 return []txn.Op{countOp}, nil 1412 } 1413 1414 return []txn.Op{ 1415 { 1416 C: refcountsC, 1417 Id: secretOwnerLabelKey(ownerTag, label), 1418 Assert: txn.DocExists, 1419 Remove: true, 1420 }, 1421 }, nil 1422 } 1423 1424 func (st *State) removeOwnerSecretLabelsOps(ownerTag names.Tag) ([]txn.Op, error) { 1425 return st.removeSecretLabelOps(ownerTag, secretOwnerLabelKey) 1426 } 1427 1428 func (st *State) removeConsumerSecretLabelsOps(consumerTag names.Tag) ([]txn.Op, error) { 1429 return st.removeSecretLabelOps(consumerTag, secretConsumerLabelKey) 1430 } 1431 1432 func (st *State) removeSecretLabelOps(tag names.Tag, keyGenerator func(names.Tag, string) string) ([]txn.Op, error) { 1433 refCountsCollection, closer := st.db().GetCollection(refcountsC) 1434 defer closer() 1435 1436 var ( 1437 doc bson.M 1438 ops []txn.Op 1439 ) 1440 id := keyGenerator(tag, ".*") 1441 q := bson.D{{"_id", bson.D{{"$regex", id}}}} 1442 iter := refCountsCollection.Find(q).Iter() 1443 for iter.Next(&doc) { 1444 id, ok := doc["_id"].(string) 1445 if !ok { 1446 continue 1447 } 1448 count, _ := doc["refcount"].(int) 1449 op := nsRefcounts.JustRemoveOp(refcountsC, id, count) 1450 ops = append(ops, op) 1451 } 1452 return ops, iter.Close() 1453 } 1454 1455 // GetURIByConsumerLabel gets the secret URI for the specified secret consumer label. 1456 func (st *State) GetURIByConsumerLabel(label string, consumer names.Tag) (*secrets.URI, error) { 1457 secretConsumersCollection, closer := st.db().GetCollection(secretConsumersC) 1458 defer closer() 1459 1460 var doc secretConsumerDoc 1461 err := secretConsumersCollection.Find(bson.M{ 1462 "consumer-tag": consumer.String(), "label": label, 1463 }).Select(bson.D{{"_id", 1}}).One(&doc) 1464 if err == mgo.ErrNotFound { 1465 return nil, errors.NotFoundf("secret consumer with label %q for %q", label, consumer) 1466 } 1467 if err != nil { 1468 return nil, errors.Trace(err) 1469 } 1470 uriStr, _ := splitSecretConsumerKey(st.localID(doc.DocID)) 1471 if uriStr == "" { 1472 return nil, errors.NotFoundf("secret consumer with label %q for %q", label, consumer) 1473 } 1474 return secrets.ParseURI(uriStr) 1475 } 1476 1477 // GetSecretConsumer gets secret consumer metadata. 1478 func (st *State) GetSecretConsumer(uri *secrets.URI, consumer names.Tag) (*secrets.SecretConsumerMetadata, error) { 1479 if uri == nil { 1480 return nil, errors.NewNotValid(nil, "empty URI") 1481 } 1482 1483 if uri.IsLocal(st.ModelUUID()) { 1484 if err := st.checkExists(uri); err != nil { 1485 return nil, errors.Trace(err) 1486 } 1487 } 1488 1489 secretConsumersCollection, closer := st.db().GetCollection(secretConsumersC) 1490 defer closer() 1491 key := st.secretConsumerKey(uri, consumer.String()) 1492 var doc secretConsumerDoc 1493 err := secretConsumersCollection.FindId(key).One(&doc) 1494 if errors.Cause(err) == mgo.ErrNotFound { 1495 return nil, errors.NotFoundf("consumer %q metadata for secret %q", consumer, uri.String()) 1496 } 1497 if err != nil { 1498 return nil, errors.Trace(err) 1499 } 1500 return &secrets.SecretConsumerMetadata{ 1501 Label: doc.Label, 1502 CurrentRevision: doc.CurrentRevision, 1503 LatestRevision: doc.LatestRevision, 1504 }, nil 1505 } 1506 1507 type secretRemoteConsumerDoc struct { 1508 DocID string `bson:"_id"` 1509 1510 ConsumerTag string `bson:"consumer-tag"` 1511 CurrentRevision int `bson:"current-revision"` 1512 1513 // LatestRevision is denormalised here so that the 1514 // consumer watcher can be triggered when a new 1515 // secret revision is added. 1516 LatestRevision int `bson:"latest-revision"` 1517 } 1518 1519 // GetSecretRemoteConsumer gets secret consumer metadata 1520 // for a cross model consumer. 1521 func (st *State) GetSecretRemoteConsumer(uri *secrets.URI, consumer names.Tag) (*secrets.SecretConsumerMetadata, error) { 1522 if uri == nil { 1523 return nil, errors.NewNotValid(nil, "empty URI") 1524 } 1525 1526 if err := st.checkExists(uri); err != nil { 1527 return nil, errors.Trace(err) 1528 } 1529 1530 secretConsumersCollection, closer := st.db().GetCollection(secretRemoteConsumersC) 1531 defer closer() 1532 1533 key := st.secretConsumerKey(uri, consumer.String()) 1534 var doc secretRemoteConsumerDoc 1535 err := secretConsumersCollection.FindId(key).One(&doc) 1536 if errors.Cause(err) == mgo.ErrNotFound { 1537 return nil, errors.NotFoundf("consumer %q metadata for secret %q", consumer, uri.String()) 1538 } 1539 if err != nil { 1540 return nil, errors.Trace(err) 1541 } 1542 md := &secrets.SecretConsumerMetadata{ 1543 CurrentRevision: doc.CurrentRevision, 1544 LatestRevision: doc.LatestRevision, 1545 } 1546 1547 return md, nil 1548 } 1549 1550 func (st *State) removeSecretConsumerInfo(uri *secrets.URI) error { 1551 secretConsumersCollection, closer := st.db().GetCollection(secretConsumersC) 1552 defer closer() 1553 1554 var docs []secretConsumerDoc 1555 err := secretConsumersCollection.Find( 1556 bson.D{ 1557 { 1558 Name: "$and", Value: []bson.D{ 1559 {{"_id", bson.D{{"$regex", fmt.Sprintf("%s#.*", uri.ID)}}}}, 1560 {{"label", bson.D{{"$exists", true}, {"$ne", ""}}}}, 1561 }, 1562 }, 1563 }, 1564 ).Select(bson.D{{"consumer-tag", 1}, {"label", 1}}).All(&docs) 1565 if err != nil && errors.Cause(err) != mgo.ErrNotFound { 1566 return errors.Trace(err) 1567 } 1568 refCountsCollection, closer := st.db().GetCollection(refcountsC) 1569 defer closer() 1570 for _, doc := range docs { 1571 consumer, _ := names.ParseTag(doc.ConsumerTag) 1572 key := secretConsumerLabelKey(consumer, doc.Label) 1573 _, err = refCountsCollection.Writeable().RemoveAll(bson.D{{ 1574 "_id", key, 1575 }}) 1576 if err != nil { 1577 return errors.Annotatef(err, "cannot delete consumer label refcounts for %s", key) 1578 } 1579 } 1580 1581 _, err = secretConsumersCollection.Writeable().RemoveAll(bson.D{{ 1582 "_id", bson.D{{"$regex", fmt.Sprintf("%s#.*", uri.ID)}}, 1583 }}) 1584 if err != nil { 1585 return errors.Annotatef(err, "cannot delete consumer info for %s", uri.String()) 1586 } 1587 return nil 1588 } 1589 1590 func (st *State) removeSecretRemoteConsumerInfo(uri *secrets.URI) error { 1591 secretConsumersCollection, closer := st.db().GetCollection(secretRemoteConsumersC) 1592 defer closer() 1593 1594 _, err := secretConsumersCollection.Writeable().RemoveAll(bson.D{{ 1595 "_id", bson.D{{"$regex", fmt.Sprintf("%s#.*", uri.ID)}}, 1596 }}) 1597 if err != nil { 1598 return errors.Annotatef(err, "cannot delete remote consumer info for %s", uri.String()) 1599 } 1600 return nil 1601 } 1602 1603 // RemoveSecretConsumer removes secret references for the specified consumer. 1604 func (st *State) RemoveSecretConsumer(consumer names.Tag) error { 1605 secretConsumersCollection, closer := st.db().GetCollection(secretConsumersC) 1606 defer closer() 1607 1608 var docs []secretConsumerDoc 1609 err := secretConsumersCollection.Find( 1610 bson.D{{"consumer-tag", consumer.String()}}, 1611 ).Select(bson.D{{"_id", 1}, {"label", 1}}).All(&docs) 1612 if err != nil && errors.Cause(err) != mgo.ErrNotFound { 1613 return errors.Trace(err) 1614 } 1615 refCountsCollection, closer := st.db().GetCollection(refcountsC) 1616 defer closer() 1617 for _, doc := range docs { 1618 key := secretConsumerLabelKey(consumer, doc.Label) 1619 _, err = refCountsCollection.Writeable().RemoveAll(bson.D{{ 1620 "_id", key, 1621 }}) 1622 if err != nil { 1623 return errors.Annotatef(err, "cannot delete consumer label refcounts for %s", key) 1624 } 1625 } 1626 1627 _, err = secretConsumersCollection.Writeable().RemoveAll( 1628 bson.D{{"consumer-tag", consumer.String()}}) 1629 if err != nil { 1630 return errors.Annotatef(err, "cannot delete consumer info for %s", consumer.String()) 1631 } 1632 return nil 1633 } 1634 1635 // removeRemoteSecretConsumer removes secret consumer info for the specified 1636 // remote application and also any of its units. 1637 func (st *State) removeRemoteSecretConsumer(appName string) error { 1638 secretConsumersCollection, closer := st.db().GetCollection(secretRemoteConsumersC) 1639 defer closer() 1640 1641 match := fmt.Sprintf("(unit|application)-%s(\\/\\d)?", appName) 1642 q := bson.D{{"consumer-tag", bson.D{{"$regex", match}}}} 1643 _, err := secretConsumersCollection.Writeable().RemoveAll(q) 1644 return err 1645 } 1646 1647 // updateSecretConsumerOperation is used to update secret consumers 1648 // in the consuming model when the secret in the offering model gets a new 1649 // revision added. 1650 type updateSecretConsumerOperation struct { 1651 st *State 1652 uri *secrets.URI 1653 latestRevision int 1654 } 1655 1656 // Build implements ModelOperation. 1657 func (u *updateSecretConsumerOperation) Build(attempt int) ([]txn.Op, error) { 1658 if attempt > 0 { 1659 return nil, errors.NotFoundf("secret consumers for secret %q", u.uri) 1660 } 1661 return u.st.secretUpdateConsumersOps(secretConsumersC, u.uri, u.latestRevision) 1662 } 1663 1664 // Done implements ModelOperation. 1665 func (u *updateSecretConsumerOperation) Done(err error) error { 1666 return err 1667 } 1668 1669 // UpdateSecretConsumerOperation returns a model operation to update 1670 // secret consumer metadata when a secret in the offering model 1671 // gets a new revision added.. 1672 func (st *State) UpdateSecretConsumerOperation(uri *secrets.URI, latestRevision int) (ModelOperation, error) { 1673 return &updateSecretConsumerOperation{ 1674 st: st, 1675 uri: uri, 1676 latestRevision: latestRevision, 1677 }, nil 1678 } 1679 1680 // SaveSecretConsumer saves or updates secret consumer metadata. 1681 func (st *State) SaveSecretConsumer(uri *secrets.URI, consumer names.Tag, metadata *secrets.SecretConsumerMetadata) error { 1682 key := st.secretConsumerKey(uri, consumer.String()) 1683 secretConsumersCollection, closer := st.db().GetCollection(secretConsumersC) 1684 defer closer() 1685 1686 // Cross model secrets do not exist in this model. 1687 localSecret := uri.IsLocal(st.ModelUUID()) 1688 1689 var doc secretConsumerDoc 1690 buildTxn := func(attempt int) ([]txn.Op, error) { 1691 if localSecret { 1692 if err := st.checkExists(uri); err != nil { 1693 return nil, errors.Trace(err) 1694 } 1695 } 1696 err := secretConsumersCollection.FindId(key).One(&doc) 1697 if err != nil && err != mgo.ErrNotFound { 1698 return nil, errors.Trace(err) 1699 } 1700 create := err != nil 1701 1702 var ops []txn.Op 1703 1704 if metadata.Label != "" && (create || metadata.Label != doc.Label) { 1705 uniqueLabelOps, err := st.uniqueSecretConsumerLabelOps(consumer, metadata.Label) 1706 if err != nil { 1707 return nil, errors.Trace(err) 1708 } 1709 ops = append(ops, uniqueLabelOps...) 1710 } 1711 if create { 1712 ops = append(ops, txn.Op{ 1713 C: secretConsumersC, 1714 Id: key, 1715 Assert: txn.DocMissing, 1716 Insert: secretConsumerDoc{ 1717 DocID: key, 1718 ConsumerTag: consumer.String(), 1719 Label: metadata.Label, 1720 CurrentRevision: metadata.CurrentRevision, 1721 LatestRevision: metadata.LatestRevision, 1722 }, 1723 }) 1724 1725 if localSecret { 1726 // Increment the consumer count, ensuring no new consumers 1727 // are added while update is in progress. 1728 countRefOps, err := st.checkConsumerCountOps(uri, 1) 1729 if err != nil { 1730 return nil, errors.Trace(err) 1731 } 1732 ops = append(ops, countRefOps...) 1733 } 1734 } else { 1735 ops = append(ops, txn.Op{ 1736 C: secretConsumersC, 1737 Id: key, 1738 Assert: txn.DocExists, 1739 Update: bson.M{"$set": bson.M{ 1740 "label": metadata.Label, 1741 "current-revision": metadata.CurrentRevision, 1742 }}, 1743 }) 1744 if localSecret && metadata.CurrentRevision > doc.CurrentRevision { 1745 // The consumer is tracking a new revision, which might result in the 1746 // previous revision becoming obsolete. 1747 obsoleteOps, err := st.markObsoleteRevisionOps(uri, consumer.String(), metadata.CurrentRevision) 1748 if err != nil { 1749 return nil, errors.Trace(err) 1750 } 1751 ops = append(ops, obsoleteOps...) 1752 } 1753 } 1754 1755 return ops, nil 1756 } 1757 return st.db().Run(buildTxn) 1758 } 1759 1760 // SaveSecretRemoteConsumer saves or updates secret consumer metadata 1761 // for a cross model consumer. 1762 func (st *State) SaveSecretRemoteConsumer(uri *secrets.URI, consumer names.Tag, metadata *secrets.SecretConsumerMetadata) error { 1763 key := st.secretConsumerKey(uri, consumer.String()) 1764 secretConsumersCollection, closer := st.db().GetCollection(secretRemoteConsumersC) 1765 defer closer() 1766 1767 var doc secretRemoteConsumerDoc 1768 buildTxn := func(attempt int) ([]txn.Op, error) { 1769 if err := st.checkExists(uri); err != nil { 1770 return nil, errors.Trace(err) 1771 } 1772 err := secretConsumersCollection.FindId(key).One(&doc) 1773 if err != nil && err != mgo.ErrNotFound { 1774 return nil, errors.Trace(err) 1775 } 1776 create := err != nil 1777 1778 var ops []txn.Op 1779 1780 if create { 1781 ops = append(ops, txn.Op{ 1782 C: secretRemoteConsumersC, 1783 Id: key, 1784 Assert: txn.DocMissing, 1785 Insert: secretRemoteConsumerDoc{ 1786 DocID: key, 1787 ConsumerTag: consumer.String(), 1788 CurrentRevision: metadata.CurrentRevision, 1789 LatestRevision: metadata.LatestRevision, 1790 }, 1791 }) 1792 1793 // Increment the consumer count, ensuring no new consumers 1794 // are added while update is in progress. 1795 countRefOps, err := st.checkConsumerCountOps(uri, 1) 1796 if err != nil { 1797 return nil, errors.Trace(err) 1798 } 1799 ops = append(ops, countRefOps...) 1800 } else { 1801 ops = append(ops, txn.Op{ 1802 C: secretRemoteConsumersC, 1803 Id: key, 1804 Assert: txn.DocExists, 1805 Update: bson.M{"$set": bson.M{ 1806 "current-revision": metadata.CurrentRevision, 1807 }}, 1808 }) 1809 if metadata.CurrentRevision > doc.CurrentRevision { 1810 // The consumer is tracking a new revision, which might result in the 1811 // previous revision becoming obsolete. 1812 obsoleteOps, err := st.markObsoleteRevisionOps(uri, consumer.String(), metadata.CurrentRevision) 1813 if err != nil { 1814 return nil, errors.Trace(err) 1815 } 1816 ops = append(ops, obsoleteOps...) 1817 } 1818 } 1819 1820 return ops, nil 1821 } 1822 return st.db().Run(buildTxn) 1823 } 1824 1825 // secretUpdateConsumersOps updates the latest secret revision number 1826 // on all consumers. This triggers the secrets change watcher. 1827 func (st *State) secretUpdateConsumersOps(coll string, uri *secrets.URI, newRevision int) ([]txn.Op, error) { 1828 secretConsumersCollection, closer := st.db().GetCollection(coll) 1829 defer closer() 1830 1831 var ( 1832 doc secretConsumerDoc 1833 ops []txn.Op 1834 ) 1835 key := st.secretConsumerKey(uri, ".*") 1836 q := bson.D{{"_id", bson.D{{"$regex", key}}}} 1837 iter := secretConsumersCollection.Find(q).Iter() 1838 for iter.Next(&doc) { 1839 ops = append(ops, txn.Op{ 1840 C: coll, 1841 Id: doc.DocID, 1842 Assert: txn.DocExists, 1843 Update: bson.M{"$set": bson.M{"latest-revision": newRevision}}, 1844 }) 1845 } 1846 if err := iter.Close(); err != nil { 1847 return nil, errors.Annotate(err, "getting secret consumers") 1848 } 1849 return ops, nil 1850 } 1851 1852 const ( 1853 idSnippet = `[0-9a-z]{20}` 1854 uuidSnippet = `[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}` 1855 ) 1856 1857 // allLocalSecretConsumers is used for model export. 1858 func (s *secretsStore) allLocalSecretConsumers() ([]secretConsumerDoc, error) { 1859 secretConsumerCollection, closer := s.st.db().GetCollection(secretConsumersC) 1860 defer closer() 1861 1862 var docs []secretConsumerDoc 1863 1864 err := secretConsumerCollection.Find(bson.D{{"_id", bson.D{{"$regex", idSnippet}}}}).All(&docs) 1865 if err != nil { 1866 return nil, errors.Trace(err) 1867 } 1868 return docs, nil 1869 } 1870 1871 // allRemoteSecretConsumers is used for model export. 1872 func (s *secretsStore) allRemoteSecretConsumers() ([]secretConsumerDoc, error) { 1873 secretConsumerCollection, closer := s.st.db().GetCollection(secretConsumersC) 1874 defer closer() 1875 1876 var docs []secretConsumerDoc 1877 1878 q := fmt.Sprintf(`%s/%s`, uuidSnippet, idSnippet) 1879 err := secretConsumerCollection.Find(bson.D{{"_id", bson.D{{"$regex", q}}}}).All(&docs) 1880 if err != nil { 1881 return nil, errors.Trace(err) 1882 } 1883 return docs, nil 1884 } 1885 1886 // allSecretRemoteConsumers is used for model export. 1887 func (s *secretsStore) allSecretRemoteConsumers() ([]secretRemoteConsumerDoc, error) { 1888 secretRemoteConsumerCollection, closer := s.st.db().GetCollection(secretRemoteConsumersC) 1889 defer closer() 1890 1891 var docs []secretRemoteConsumerDoc 1892 1893 err := secretRemoteConsumerCollection.Find(nil).All(&docs) 1894 if err != nil { 1895 return nil, errors.Trace(err) 1896 } 1897 return docs, nil 1898 } 1899 1900 // WatchConsumedSecretsChanges returns a watcher for updates and deletes 1901 // of secrets that have been previously read by the specified consumer. 1902 func (st *State) WatchConsumedSecretsChanges(consumer names.Tag) (StringsWatcher, error) { 1903 return newConsumedSecretsWatcher(st, consumer.String(), false), nil 1904 } 1905 1906 // WatchRemoteConsumedSecretsChanges returns a watcher for updates and deletes 1907 // of secrets that have been previously read by the specified remote consumer app. 1908 func (st *State) WatchRemoteConsumedSecretsChanges(consumerApp string) (StringsWatcher, error) { 1909 return newConsumedSecretsWatcher(st, consumerApp, true), nil 1910 } 1911 1912 type consumedSecretsWatcher struct { 1913 commonWatcher 1914 out chan []string 1915 1916 knownRevisions map[string]int 1917 1918 coll string 1919 matchQuery bson.D 1920 filter func(id string) bool 1921 } 1922 1923 func newConsumedSecretsWatcher(st modelBackend, consumer string, remote bool) StringsWatcher { 1924 w := &consumedSecretsWatcher{ 1925 commonWatcher: newCommonWatcher(st), 1926 out: make(chan []string), 1927 knownRevisions: make(map[string]int), 1928 } 1929 if !remote { 1930 w.coll = secretConsumersC 1931 w.matchQuery = bson.D{{"consumer-tag", consumer}} 1932 w.filter = func(id string) bool { 1933 return strings.HasSuffix(id, "#"+consumer) 1934 } 1935 } else { 1936 w.coll = secretRemoteConsumersC 1937 match := fmt.Sprintf("(unit|application)-%s(\\/\\d)?", consumer) 1938 w.matchQuery = bson.D{{"consumer-tag", bson.D{{"$regex", match}}}} 1939 w.filter = func(id string) bool { 1940 return strings.HasSuffix(id, "#application-"+consumer) || strings.Contains(id, "#unit-"+consumer+"-") 1941 } 1942 } 1943 w.tomb.Go(func() error { 1944 defer close(w.out) 1945 return w.loop() 1946 }) 1947 return w 1948 } 1949 1950 // Changes implements StringsWatcher. 1951 func (w *consumedSecretsWatcher) Changes() <-chan []string { 1952 return w.out 1953 } 1954 1955 func (w *consumedSecretsWatcher) initial() ([]string, error) { 1956 var doc secretConsumerDoc 1957 secretConsumersCollection, closer := w.db.GetCollection(w.coll) 1958 defer closer() 1959 1960 var ids []string 1961 iter := secretConsumersCollection.Find(w.matchQuery).Select(bson.D{{"latest-revision", 1}}).Iter() 1962 for iter.Next(&doc) { 1963 w.knownRevisions[doc.DocID] = doc.LatestRevision 1964 if doc.LatestRevision < 2 { 1965 continue 1966 } 1967 uriStr := strings.Split(w.backend.localID(doc.DocID), "#")[0] 1968 uri, err := secrets.ParseURI(uriStr) 1969 if err != nil { 1970 return nil, errors.Trace(err) 1971 } 1972 ids = append(ids, uri.String()) 1973 } 1974 return ids, errors.Trace(iter.Close()) 1975 } 1976 1977 func (w *consumedSecretsWatcher) merge(currentChanges []string, change watcher.Change) ([]string, error) { 1978 changeID := change.Id.(string) 1979 uriStr := strings.Split(w.backend.localID(changeID), "#")[0] 1980 seenRevision, known := w.knownRevisions[changeID] 1981 1982 if change.Revno < 0 { 1983 // Record deleted. 1984 if !known { 1985 return currentChanges, nil 1986 } 1987 delete(w.knownRevisions, changeID) 1988 uri, err := secrets.ParseURI(uriStr) 1989 if err != nil { 1990 return nil, errors.Trace(err) 1991 } 1992 currentChanges = append(currentChanges, uri.String()) 1993 return currentChanges, nil 1994 } 1995 1996 // Record added or updated. 1997 var doc secretConsumerDoc 1998 secretConsumerColl, closer := w.db.GetCollection(w.coll) 1999 defer closer() 2000 2001 err := secretConsumerColl.FindId(change.Id).Select(bson.D{{"latest-revision", 1}}).One(&doc) 2002 if err != nil && err != mgo.ErrNotFound { 2003 return nil, errors.Trace(err) 2004 } 2005 // Changed but no longer in the model so ignore. 2006 if err != nil { 2007 return currentChanges, nil 2008 } 2009 w.knownRevisions[changeID] = doc.LatestRevision 2010 if doc.LatestRevision > 1 && doc.LatestRevision != seenRevision { 2011 uri, err := secrets.ParseURI(uriStr) 2012 if err != nil { 2013 return nil, errors.Trace(err) 2014 } 2015 currentChanges = append(currentChanges, uri.String()) 2016 } 2017 return currentChanges, nil 2018 } 2019 2020 func (w *consumedSecretsWatcher) loop() (err error) { 2021 ch := make(chan watcher.Change) 2022 filter := func(id interface{}) bool { 2023 k, err := w.backend.strictLocalID(id.(string)) 2024 if err != nil { 2025 return false 2026 } 2027 return w.filter(k) 2028 } 2029 w.watcher.WatchCollectionWithFilter(w.coll, ch, filter) 2030 defer w.watcher.UnwatchCollection(w.coll, ch) 2031 2032 changes, err := w.initial() 2033 if err != nil { 2034 return errors.Trace(err) 2035 } 2036 2037 out := w.out 2038 for { 2039 select { 2040 case <-w.tomb.Dying(): 2041 return tomb.ErrDying 2042 case <-w.watcher.Dead(): 2043 return stateWatcherDeadError(w.watcher.Err()) 2044 case change, ok := <-ch: 2045 if !ok { 2046 return tomb.ErrDying 2047 } 2048 if changes, err = w.merge(changes, change); err != nil { 2049 return err 2050 } 2051 if len(changes) > 0 { 2052 out = w.out 2053 } 2054 case out <- changes: 2055 out = nil 2056 changes = nil 2057 } 2058 } 2059 } 2060 2061 // WatchObsolete returns a watcher for notifying when: 2062 // - a secret owned by the entity is deleted 2063 // - a secret revision owed by the entity no longer 2064 // has any consumers 2065 // 2066 // Obsolete revisions results are "uri/revno" and deleted 2067 // secret results are "uri". 2068 func (s *secretsStore) WatchObsolete(ownerTags []names.Tag) (StringsWatcher, error) { 2069 if len(ownerTags) == 0 { 2070 return nil, errors.New("missing secret owners") 2071 } 2072 owners := make([]string, len(ownerTags)) 2073 for i, owner := range ownerTags { 2074 owners[i] = owner.String() 2075 } 2076 return newObsoleteSecretsWatcher(s.st, owners, nil), nil 2077 } 2078 2079 // WatchRevisionsToPrune returns a watcher for notifying when a user supplied secret revision needs to be pruned. 2080 func (s *secretsStore) WatchRevisionsToPrune(ownerTags []names.Tag) (StringsWatcher, error) { 2081 if len(ownerTags) == 0 { 2082 return nil, errors.New("missing secret owners") 2083 } 2084 owners := make([]string, len(ownerTags)) 2085 for i, owner := range ownerTags { 2086 owners[i] = owner.String() 2087 } 2088 fitler := func(id string) bool { 2089 uri, err := secrets.ParseURI(id) 2090 if err != nil { 2091 logger.Warningf("invalid secret URI %q, err %#v", id, err) 2092 return false 2093 } 2094 md, err := s.GetSecret(uri) 2095 if err != nil { 2096 logger.Warningf("cannot get secret %q, err %#v", uri, err) 2097 return false 2098 } 2099 if s.st.modelTag.String() != md.OwnerTag { 2100 // Only prune secrets owned by this model (user secrets). 2101 return false 2102 } 2103 return md.AutoPrune 2104 } 2105 return newObsoleteSecretsWatcher(s.st, owners, fitler), nil 2106 } 2107 2108 type obsoleteSecretsWatcher struct { 2109 commonWatcher 2110 out chan []string 2111 2112 obsoleteRevisionsWatcher *collectionWatcher 2113 2114 owners []string 2115 known set.Strings 2116 } 2117 2118 func newObsoleteSecretsWatcher(st modelBackend, owners []string, filter func(string) bool) *obsoleteSecretsWatcher { 2119 // obsoleteRevisionsWatcher is for tracking secret revisions with no consumers. 2120 obsoleteRevisionsWatcher := newCollectionWatcher(st, colWCfg{ 2121 col: secretRevisionsC, 2122 filter: func(key interface{}) bool { 2123 secretRevisionsCollection, closer := st.db().GetCollection(secretRevisionsC) 2124 defer closer() 2125 2126 var doc secretRevisionDoc 2127 err := secretRevisionsCollection.Find(bson.D{{"_id", key}, secretOwnerTerm(owners)}).Select( 2128 bson.D{{"obsolete", 1}}, 2129 ).One(&doc) 2130 if err != nil { 2131 return false 2132 } 2133 if filter == nil { 2134 return doc.Obsolete 2135 } 2136 uri, _ := splitSecretRevision(st.localID(doc.DocID)) 2137 return doc.Obsolete && filter(uri) 2138 }, 2139 idconv: func(idStr string) string { 2140 id, rev := splitSecretRevision(idStr) 2141 if rev == 0 { 2142 return idStr 2143 } 2144 uri := secrets.URI{ID: id} 2145 return uri.String() + fmt.Sprintf("/%d", rev) 2146 }, 2147 }) 2148 2149 w := &obsoleteSecretsWatcher{ 2150 commonWatcher: newCommonWatcher(st), 2151 obsoleteRevisionsWatcher: obsoleteRevisionsWatcher.(*collectionWatcher), 2152 out: make(chan []string), 2153 known: set.NewStrings(), 2154 owners: owners, 2155 } 2156 w.tomb.Go(func() error { 2157 defer w.finish() 2158 return w.loop() 2159 }) 2160 return w 2161 } 2162 2163 func (w *obsoleteSecretsWatcher) finish() { 2164 watcher.Stop(w.obsoleteRevisionsWatcher, &w.tomb) 2165 close(w.out) 2166 } 2167 2168 // Changes implements StringsWatcher. 2169 func (w *obsoleteSecretsWatcher) Changes() <-chan []string { 2170 return w.out 2171 } 2172 2173 func (w *obsoleteSecretsWatcher) initial() error { 2174 var doc secretMetadataDoc 2175 secretMetadataCollection, closer := w.db.GetCollection(secretMetadataC) 2176 defer closer() 2177 2178 iter := secretMetadataCollection.Find(bson.D{secretOwnerTerm(w.owners)}).Iter() 2179 for iter.Next(&doc) { 2180 w.known.Add(doc.DocID) 2181 } 2182 return errors.Trace(iter.Close()) 2183 } 2184 2185 func (w *obsoleteSecretsWatcher) mergedOwnedChanges(currentChanges []string, change watcher.Change) ([]string, error) { 2186 changeID := change.Id.(string) 2187 known := w.known.Contains(changeID) 2188 2189 if change.Revno < 0 { 2190 if !known { 2191 return currentChanges, nil 2192 } 2193 // Secret deleted. 2194 delete(w.known, changeID) 2195 uriStr := w.backend.localID(changeID) 2196 uri, err := secrets.ParseURI(uriStr) 2197 if err != nil { 2198 return nil, errors.Trace(err) 2199 } 2200 // If there are any pending changes for obsolete revisions and the 2201 // secret becomes deleted, the obsolete revision changes are no 2202 // longer relevant. 2203 var newChanges []string 2204 for _, c := range currentChanges { 2205 id, _ := splitSecretRevision(c) 2206 if id != uri.String() { 2207 newChanges = append(newChanges, c) 2208 } 2209 } 2210 newChanges = append(newChanges, uri.String()) 2211 return newChanges, nil 2212 } 2213 if known { 2214 return currentChanges, nil 2215 } 2216 var doc secretMetadataDoc 2217 // Record added or updated - we don't emit an event but 2218 // record that we know about it. 2219 secretMetadataColl, closer := w.db.GetCollection(secretMetadataC) 2220 defer closer() 2221 err := secretMetadataColl.Find(bson.D{{"_id", change.Id}, secretOwnerTerm(w.owners)}).One(&doc) 2222 if err != nil && err != mgo.ErrNotFound { 2223 return nil, errors.Trace(err) 2224 } 2225 // Changed but no longer in the model so ignore. 2226 if err != nil { 2227 return currentChanges, nil 2228 } 2229 w.known.Add(changeID) 2230 return currentChanges, nil 2231 } 2232 2233 func (w *obsoleteSecretsWatcher) mergeRevisionChanges(currentChanges []string, obsolete []string) []string { 2234 newChanges := set.NewStrings(currentChanges...).Union(set.NewStrings(obsolete...)) 2235 return newChanges.Values() 2236 } 2237 2238 func (w *obsoleteSecretsWatcher) loop() (err error) { 2239 // Watch changes to secrets owned by the entity. 2240 ownedChanges := make(chan watcher.Change) 2241 w.watcher.WatchCollection(secretMetadataC, ownedChanges) 2242 defer w.watcher.UnwatchCollection(secretMetadataC, ownedChanges) 2243 2244 if err = w.initial(); err != nil { 2245 return errors.Trace(err) 2246 } 2247 2248 var ( 2249 changes []string 2250 gotInitialObsoleteChange bool 2251 ) 2252 out := w.out 2253 gotInitialObsoleteChange = false 2254 for { 2255 // Give any incoming secret deletion events 2256 // time to arrive so the revision obsolete and 2257 // delete events can be squashed. 2258 timeout := time.After(time.Second) 2259 select { 2260 case <-w.tomb.Dying(): 2261 return tomb.ErrDying 2262 case <-w.watcher.Dead(): 2263 return stateWatcherDeadError(w.watcher.Err()) 2264 case obsoleteRevisions, ok := <-w.obsoleteRevisionsWatcher.Changes(): 2265 if !ok { 2266 return tomb.ErrDying 2267 } 2268 if !gotInitialObsoleteChange { 2269 gotInitialObsoleteChange = true 2270 break 2271 } 2272 changes = w.mergeRevisionChanges(changes, obsoleteRevisions) 2273 case change, ok := <-ownedChanges: 2274 if !ok { 2275 return tomb.ErrDying 2276 } 2277 if changes, err = w.mergedOwnedChanges(changes, change); err != nil { 2278 return err 2279 } 2280 case <-timeout: 2281 if len(changes) > 0 { 2282 out = w.out 2283 } 2284 case out <- changes: 2285 out = nil 2286 changes = nil 2287 } 2288 } 2289 } 2290 2291 // markObsoleteRevisionOps returns ops for marking any revisions which are currently 2292 // not being tracked by any consumers as obsolete. 2293 func (st *State) markObsoleteRevisionOps(uri *secrets.URI, exceptForConsumer string, exceptForRev int) ([]txn.Op, error) { 2294 var obsoleteOps []txn.Op 2295 revs, latest, err := st.getOrphanedSecretRevisions(uri, exceptForConsumer, exceptForRev) 2296 if err != nil { 2297 return nil, errors.Annotate(err, "getting orphaned secret revisions") 2298 } 2299 2300 for _, rev := range revs { 2301 obsoleteOps = append(obsoleteOps, txn.Op{ 2302 C: secretRevisionsC, 2303 Id: secretRevisionKey(uri, rev), 2304 Assert: txn.DocExists, 2305 Update: bson.M{"$set": bson.M{ 2306 "obsolete": true, 2307 }}, 2308 }) 2309 } 2310 // Ensure that no concurrent revision updates can happen. 2311 if len(obsoleteOps) > 0 { 2312 obsoleteOps = append(obsoleteOps, txn.Op{ 2313 C: secretMetadataC, 2314 Id: uri.ID, 2315 Assert: bson.D{{"latest-revision", latest}}, 2316 }) 2317 } 2318 return obsoleteOps, nil 2319 } 2320 2321 // getOrphanedSecretRevisions returns revisions which are not being tracked by any consumer, 2322 // plus the current latest revision, for the specified secret, excluding the specified 2323 // consumer and/or revision. 2324 func (st *State) getOrphanedSecretRevisions(uri *secrets.URI, exceptForConsumer string, exceptForRev int) ([]int, int, error) { 2325 store := NewSecrets(st) 2326 revInfo, err := store.listSecretRevisions(uri, nil) 2327 if err != nil { 2328 return nil, 0, errors.Trace(err) 2329 } 2330 allRevisions := set.NewInts() 2331 for _, r := range revInfo { 2332 allRevisions.Add(r.Revision) 2333 } 2334 latest := allRevisions.SortedValues()[allRevisions.Size()-1] 2335 allRevisions.Remove(exceptForRev) 2336 2337 consumedRevs, err := st.getInUseSecretRevisions(secretConsumersC, uri, exceptForConsumer) 2338 if err != nil { 2339 return nil, 0, errors.Trace(err) 2340 } 2341 remoteConsumedRevs, err := st.getInUseSecretRevisions(secretRemoteConsumersC, uri, exceptForConsumer) 2342 if err != nil { 2343 return nil, 0, errors.Trace(err) 2344 } 2345 usedRevisions := consumedRevs.Union(remoteConsumedRevs) 2346 return allRevisions.Difference(usedRevisions).Values(), latest, nil 2347 } 2348 2349 func (st *State) getInUseSecretRevisions(collName string, uri *secrets.URI, exceptForConsumer string) (set.Ints, error) { 2350 secretConsumersCollection, closer := st.db().GetCollection(collName) 2351 defer closer() 2352 2353 pipe := secretConsumersCollection.Pipe([]bson.M{ 2354 { 2355 "$match": bson.M{ 2356 "_id": bson.M{"$regex": st.docID(uri.ID + "#.*")}, 2357 "consumer-tag": bson.M{"$ne": exceptForConsumer}, 2358 }, 2359 }, 2360 { 2361 "$group": bson.M{ 2362 "_id": bson.M{"$toString": "$current-revision"}, "count": bson.M{"$sum": 1}, 2363 }, 2364 }, 2365 { 2366 "$sort": bson.M{"_id": 1}, 2367 }, 2368 { 2369 "$group": bson.M{ 2370 "_id": nil, 2371 "counts": bson.M{ 2372 "$push": bson.M{"k": "$_id", "v": "$count"}, 2373 }, 2374 }, 2375 }, 2376 { 2377 "$replaceRoot": bson.M{ 2378 "newRoot": bson.M{"$arrayToObject": "$counts"}, 2379 }, 2380 }, 2381 }) 2382 var usedRevisions []map[string]int 2383 err := pipe.All(&usedRevisions) 2384 if err != nil { 2385 return nil, errors.Trace(err) 2386 } 2387 result := set.NewInts() 2388 if len(usedRevisions) == 0 { 2389 return result, nil 2390 } 2391 2392 for revStr := range usedRevisions[0] { 2393 r, _ := strconv.Atoi(revStr) 2394 result.Add(r) 2395 } 2396 return result, nil 2397 } 2398 2399 type secretPermissionDoc struct { 2400 DocID string `bson:"_id"` 2401 2402 Scope string `bson:"scope-tag"` 2403 Subject string `bson:"subject-tag"` 2404 Role string `bson:"role"` 2405 } 2406 2407 func (st *State) findSecretEntity(tag names.Tag) (entity Lifer, collName, docID string, err error) { 2408 id := tag.Id() 2409 switch tag.(type) { 2410 case names.RelationTag: 2411 entity, err = st.KeyRelation(id) 2412 collName = relationsC 2413 docID = id 2414 case names.UnitTag: 2415 entity, err = st.Unit(id) 2416 collName = unitsC 2417 docID = id 2418 case names.ApplicationTag: 2419 entity, err = st.Application(id) 2420 docID = id 2421 if err == nil { 2422 collName = applicationsC 2423 } else if errors.IsNotFound(err) { 2424 entity, err = st.RemoteApplication(id) 2425 collName = remoteApplicationsC 2426 } 2427 case names.ModelTag: 2428 if st.ModelUUID() != tag.Id() { 2429 // This should never happen, but just in case. 2430 return nil, "", "", errors.NotFoundf("model %q", tag.Id()) 2431 } 2432 entity, err = st.Model() 2433 if err != nil { 2434 return nil, "", "", errors.Trace(err) 2435 } 2436 collName = modelsC 2437 docID = id 2438 default: 2439 err = errors.NotValidf("secret scope reference %q", tag.String()) 2440 } 2441 return entity, collName, docID, err 2442 } 2443 2444 func (st *State) referencedSecrets(ref names.Tag, attr string) ([]*secrets.URI, error) { 2445 secretMetadataCollection, closer := st.db().GetCollection(secretMetadataC) 2446 defer closer() 2447 2448 var ( 2449 doc secretMetadataDoc 2450 result []*secrets.URI 2451 ) 2452 iter := secretMetadataCollection.Find(bson.D{{attr, ref.String()}}).Select(bson.D{{"_id", 1}}).Iter() 2453 for iter.Next(&doc) { 2454 uri, err := secrets.ParseURI(st.localID(doc.DocID)) 2455 if err != nil { 2456 _ = iter.Close() 2457 return nil, errors.Trace(err) 2458 } 2459 result = append(result, uri) 2460 } 2461 return result, iter.Close() 2462 2463 } 2464 2465 // SecretAccessParams are used to grant/revoke secret access. 2466 type SecretAccessParams struct { 2467 LeaderToken leadership.Token 2468 Scope names.Tag 2469 Subject names.Tag 2470 Role secrets.SecretRole 2471 } 2472 2473 // GrantSecretAccess saves the secret access role for the subject with the specified scope. 2474 func (st *State) GrantSecretAccess(uri *secrets.URI, p SecretAccessParams) (err error) { 2475 if p.Role == secrets.RoleNone || !p.Role.IsValid() { 2476 return errors.NotValidf("secret role %q", p.Role) 2477 } 2478 2479 scopeEntity, scopeCollName, scopeDocID, err := st.findSecretEntity(p.Scope) 2480 if err != nil { 2481 return errors.Annotate(err, "invalid scope reference") 2482 } 2483 if scopeEntity.Life() != Alive { 2484 return errors.Errorf("cannot grant access to secret in scope of %q which is not alive", p.Scope) 2485 } 2486 subjectEntity, subjectCollName, subjectDocID, err := st.findSecretEntity(p.Subject) 2487 if p.Subject.Kind() == names.UnitTagKind && errors.Is(err, errors.NotFound) { 2488 unitApp, _ := names.UnitApplication(p.Subject.Id()) 2489 _, err2 := st.RemoteApplication(unitApp) 2490 if err2 != nil && !errors.Is(err2, errors.NotFound) { 2491 return errors.Trace(err2) 2492 } 2493 if err2 == nil { 2494 return errors.NotSupportedf("sharing secrets with a unit across a cross model relation") 2495 } 2496 } 2497 if err != nil { 2498 return errors.Annotate(err, "invalid subject reference") 2499 } 2500 if subjectEntity.Life() != Alive { 2501 return errors.Errorf("cannot grant dying %q access to secret", p.Subject) 2502 } 2503 2504 // Apps on the offering side of a cross model relation can grant secret access. 2505 type remoteApp interface { 2506 IsConsumerProxy() bool 2507 } 2508 var subjectApp remoteApp 2509 2510 if subjectCollName == remoteApplicationsC { 2511 if e, ok := subjectEntity.(remoteApp); ok { 2512 subjectApp = e 2513 } 2514 if subjectApp == nil || !subjectApp.IsConsumerProxy() { 2515 return errors.NotSupportedf("sharing consumer secrets across a cross model relation") 2516 } 2517 } 2518 2519 isScopeAliveOp := txn.Op{ 2520 C: scopeCollName, 2521 Id: scopeDocID, 2522 Assert: isAliveDoc, 2523 } 2524 isSubjectAliveOp := txn.Op{ 2525 C: subjectCollName, 2526 Id: subjectDocID, 2527 Assert: isAliveDoc, 2528 } 2529 2530 key := st.secretConsumerKey(uri, p.Subject.String()) 2531 2532 secretPermissionsCollection, closer := st.db().GetCollection(secretPermissionsC) 2533 defer closer() 2534 2535 var doc secretPermissionDoc 2536 buildTxn := func(attempt int) ([]txn.Op, error) { 2537 if err := st.checkExists(uri); err != nil { 2538 return nil, errors.Trace(err) 2539 } 2540 err = secretPermissionsCollection.FindId(key).One(&doc) 2541 if err == mgo.ErrNotFound { 2542 return []txn.Op{{ 2543 C: secretPermissionsC, 2544 Id: key, 2545 Assert: txn.DocMissing, 2546 Insert: secretPermissionDoc{ 2547 DocID: key, 2548 Subject: p.Subject.String(), 2549 Scope: p.Scope.String(), 2550 Role: string(p.Role), 2551 }, 2552 }}, nil 2553 } else if err != nil { 2554 return nil, errors.Trace(err) 2555 } 2556 if doc.Scope != p.Scope.String() { 2557 return nil, errors.New("cannot change secret permission scope") 2558 } 2559 if doc.Subject != p.Subject.String() { 2560 return nil, errors.New("cannot change secret permission subject") 2561 } 2562 return []txn.Op{{ 2563 C: secretPermissionsC, 2564 Id: key, 2565 Assert: txn.DocExists, 2566 Update: bson.M{"$set": bson.M{ 2567 "role": p.Role, 2568 }}, 2569 }, isScopeAliveOp, isSubjectAliveOp}, nil 2570 } 2571 return st.db().Run(buildTxnWithLeadership(buildTxn, p.LeaderToken)) 2572 } 2573 2574 // RevokeSecretAccess removes any secret access role for the subject. 2575 func (st *State) RevokeSecretAccess(uri *secrets.URI, p SecretAccessParams) error { 2576 key := st.secretConsumerKey(uri, p.Subject.String()) 2577 2578 secretPermissionsCollection, closer := st.db().GetCollection(secretPermissionsC) 2579 defer closer() 2580 2581 var doc secretPermissionDoc 2582 buildTxn := func(attempt int) ([]txn.Op, error) { 2583 if err := st.checkExists(uri); err != nil { 2584 if errors.IsNotFound(err) { 2585 return nil, jujutxn.ErrNoOperations 2586 } 2587 return nil, errors.Trace(err) 2588 } 2589 err := secretPermissionsCollection.FindId(key).One(&doc) 2590 if err == mgo.ErrNotFound { 2591 return nil, jujutxn.ErrNoOperations 2592 } else if err != nil { 2593 return nil, errors.Trace(err) 2594 } 2595 2596 ops := []txn.Op{{ 2597 C: secretPermissionsC, 2598 Id: key, 2599 Assert: txn.DocExists, 2600 Remove: true, 2601 }} 2602 return ops, nil 2603 } 2604 return st.db().Run(buildTxnWithLeadership(buildTxn, p.LeaderToken)) 2605 } 2606 2607 // SecretAccess returns the secret access role for the subject. 2608 func (st *State) SecretAccess(uri *secrets.URI, subject names.Tag) (secrets.SecretRole, error) { 2609 key := st.secretConsumerKey(uri, subject.String()) 2610 2611 secretPermissionsCollection, closer := st.db().GetCollection(secretPermissionsC) 2612 defer closer() 2613 2614 if err := st.checkExists(uri); err != nil { 2615 return secrets.RoleNone, errors.Trace(err) 2616 } 2617 2618 var doc secretPermissionDoc 2619 err := secretPermissionsCollection.FindId(key).One(&doc) 2620 if err == mgo.ErrNotFound { 2621 return secrets.RoleNone, nil 2622 } 2623 if err != nil { 2624 return secrets.RoleNone, errors.Trace(err) 2625 } 2626 return secrets.SecretRole(doc.Role), nil 2627 } 2628 2629 // SecretAccessScope returns the secret access scope for the subject. 2630 func (st *State) SecretAccessScope(uri *secrets.URI, subject names.Tag) (names.Tag, error) { 2631 key := st.secretConsumerKey(uri, subject.String()) 2632 2633 secretPermissionsCollection, closer := st.db().GetCollection(secretPermissionsC) 2634 defer closer() 2635 2636 if err := st.checkExists(uri); err != nil { 2637 return nil, errors.Trace(err) 2638 } 2639 2640 var doc secretPermissionDoc 2641 err := secretPermissionsCollection.FindId(key).One(&doc) 2642 if err == mgo.ErrNotFound { 2643 return nil, errors.NotFoundf("secret access for consumer %q", subject) 2644 } 2645 if err != nil { 2646 return nil, errors.Trace(err) 2647 } 2648 return names.ParseTag(doc.Scope) 2649 } 2650 2651 func (st *State) removeScopedSecretPermissionOps(scope names.Tag) ([]txn.Op, error) { 2652 secretPermissionsCollection, closer := st.db().GetCollection(secretPermissionsC) 2653 defer closer() 2654 2655 var ( 2656 doc secretPermissionDoc 2657 ops []txn.Op 2658 ) 2659 iter := secretPermissionsCollection.Find(bson.D{{"scope-tag", scope.String()}}).Select(bson.D{{"_id", 1}}).Iter() 2660 for iter.Next(&doc) { 2661 ops = append(ops, txn.Op{ 2662 C: secretPermissionsC, 2663 Id: doc.DocID, 2664 Remove: true, 2665 }) 2666 } 2667 return ops, iter.Close() 2668 } 2669 2670 func (st *State) removeConsumerSecretPermissionOps(consumer names.Tag) ([]txn.Op, error) { 2671 secretPermissionsCollection, closer := st.db().GetCollection(secretPermissionsC) 2672 defer closer() 2673 2674 var ( 2675 doc secretPermissionDoc 2676 ops []txn.Op 2677 ) 2678 iter := secretPermissionsCollection.Find(bson.D{{"subject-tag", consumer.String()}}).Select(bson.D{{"_id", 1}}).Iter() 2679 for iter.Next(&doc) { 2680 ops = append(ops, txn.Op{ 2681 C: secretPermissionsC, 2682 Id: doc.DocID, 2683 Remove: true, 2684 }) 2685 } 2686 return ops, iter.Close() 2687 } 2688 2689 type secretRotationDoc struct { 2690 DocID string `bson:"_id"` 2691 TxnRevno int64 `bson:"txn-revno"` 2692 2693 // These fields are denormalised here so that the watcher 2694 // only needs to access this collection. 2695 NextRotateTime time.Time `bson:"next-rotate-time"` 2696 OwnerTag string `bson:"owner-tag"` 2697 } 2698 2699 func (s *secretsStore) secretRotationOps(uri *secrets.URI, owner string, rotatePolicy *secrets.RotatePolicy, nextRotateTime *time.Time) ([]txn.Op, error) { 2700 secretKey := uri.ID 2701 if rotatePolicy != nil && !rotatePolicy.WillRotate() { 2702 return []txn.Op{{ 2703 C: secretRotateC, 2704 Id: secretKey, 2705 Remove: true, 2706 }}, nil 2707 } 2708 if nextRotateTime == nil { 2709 return nil, errors.New("must specify a secret rotate time") 2710 } 2711 secretRotateCollection, closer := s.st.db().GetCollection(secretRotateC) 2712 defer closer() 2713 2714 var doc secretRotationDoc 2715 err := secretRotateCollection.FindId(secretKey).One(&doc) 2716 if err == mgo.ErrNotFound { 2717 return []txn.Op{{ 2718 C: secretRotateC, 2719 Id: secretKey, 2720 Assert: txn.DocMissing, 2721 Insert: secretRotationDoc{ 2722 DocID: secretKey, 2723 NextRotateTime: (*nextRotateTime).Round(time.Second).UTC(), 2724 OwnerTag: owner, 2725 }, 2726 }}, nil 2727 } else if err != nil { 2728 return nil, errors.Trace(err) 2729 } 2730 return []txn.Op{{ 2731 C: secretRotateC, 2732 Id: secretKey, 2733 Assert: txn.DocExists, 2734 Update: bson.M{"$set": bson.M{"next-rotate-time": nextRotateTime.Round(time.Second).UTC()}}, 2735 }}, nil 2736 } 2737 2738 // SecretRotated records when the given secret was rotated. 2739 func (st *State) SecretRotated(uri *secrets.URI, next time.Time) error { 2740 secretRotateCollection, closer := st.db().GetCollection(secretRotateC) 2741 defer closer() 2742 2743 secretKey := uri.ID 2744 buildTxn := func(attempt int) ([]txn.Op, error) { 2745 if err := st.checkExists(uri); err != nil { 2746 return nil, errors.Trace(err) 2747 } 2748 2749 var currentDoc secretRotationDoc 2750 err := secretRotateCollection.FindId(secretKey).One(¤tDoc) 2751 if errors.Cause(err) == mgo.ErrNotFound { 2752 return nil, errors.NotFoundf("rotation info for secret %q", uri.String()) 2753 } 2754 if err != nil { 2755 return nil, errors.Trace(err) 2756 } 2757 // If next rotate time is sooner than our proposed time, keep the existing value. 2758 if attempt > 0 && currentDoc.NextRotateTime.Before(next) { 2759 return nil, jujutxn.ErrNoOperations 2760 } 2761 ops := []txn.Op{{ 2762 C: secretRotateC, 2763 Id: secretKey, 2764 Assert: bson.D{{"txn-revno", currentDoc.TxnRevno}}, 2765 Update: bson.M{"$set": bson.M{ 2766 "next-rotate-time": next, 2767 }}, 2768 }} 2769 return ops, nil 2770 } 2771 return st.db().Run(buildTxn) 2772 } 2773 2774 // WatchSecretsRotationChanges returns a watcher for rotation updates to secrets 2775 // with the specified owner. 2776 func (st *State) WatchSecretsRotationChanges(ownerTags []names.Tag) (SecretsTriggerWatcher, error) { 2777 if len(ownerTags) == 0 { 2778 return nil, errors.New("missing secret owners") 2779 } 2780 owners := make([]string, len(ownerTags)) 2781 for i, owner := range ownerTags { 2782 owners[i] = owner.String() 2783 } 2784 return newSecretsRotationWatcher(st, owners), nil 2785 } 2786 2787 // SecretsTriggerWatcher defines a watcher for changes to secret 2788 // event trigger config. 2789 type SecretsTriggerWatcher interface { 2790 Watcher 2791 Changes() corewatcher.SecretTriggerChannel 2792 } 2793 2794 type rotateWatcherDetails struct { 2795 txnRevNo int64 2796 URI *secrets.URI 2797 } 2798 2799 type secretsRotationWatcher struct { 2800 commonWatcher 2801 out chan []corewatcher.SecretTriggerChange 2802 2803 owners []string 2804 known map[string]rotateWatcherDetails 2805 } 2806 2807 func newSecretsRotationWatcher(backend modelBackend, owners []string) *secretsRotationWatcher { 2808 w := &secretsRotationWatcher{ 2809 commonWatcher: newCommonWatcher(backend), 2810 out: make(chan []corewatcher.SecretTriggerChange), 2811 known: make(map[string]rotateWatcherDetails), 2812 owners: owners, 2813 } 2814 w.tomb.Go(func() error { 2815 defer close(w.out) 2816 return w.loop() 2817 }) 2818 return w 2819 } 2820 2821 // Changes returns a channel that will receive changes when the next rotate time 2822 // for a secret changes. 2823 func (w *secretsRotationWatcher) Changes() corewatcher.SecretTriggerChannel { 2824 return w.out 2825 } 2826 2827 func (w *secretsRotationWatcher) initial() ([]corewatcher.SecretTriggerChange, error) { 2828 var details []corewatcher.SecretTriggerChange 2829 2830 var doc secretRotationDoc 2831 secretRotateCollection, closer := w.db.GetCollection(secretRotateC) 2832 defer closer() 2833 2834 iter := secretRotateCollection.Find(bson.D{secretOwnerTerm(w.owners)}).Iter() 2835 for iter.Next(&doc) { 2836 uriStr := w.backend.localID(doc.DocID) 2837 uri, err := secrets.ParseURI(uriStr) 2838 if err != nil { 2839 _ = iter.Close() 2840 return nil, errors.Annotatef(err, "invalid secret URI %q", uriStr) 2841 } 2842 w.known[doc.DocID] = rotateWatcherDetails{ 2843 txnRevNo: doc.TxnRevno, 2844 URI: uri, 2845 } 2846 details = append(details, corewatcher.SecretTriggerChange{ 2847 URI: uri, 2848 NextTriggerTime: doc.NextRotateTime.UTC(), 2849 }) 2850 } 2851 return details, errors.Trace(iter.Close()) 2852 } 2853 2854 func (w *secretsRotationWatcher) merge(details []corewatcher.SecretTriggerChange, change watcher.Change) ([]corewatcher.SecretTriggerChange, error) { 2855 changeID := change.Id.(string) 2856 knownDetails, known := w.known[changeID] 2857 2858 doc := secretRotationDoc{} 2859 if change.Revno >= 0 { 2860 // Record added or updated. 2861 secretsRotationColl, closer := w.db.GetCollection(secretRotateC) 2862 defer closer() 2863 err := secretsRotationColl.Find(bson.D{{"_id", change.Id}, secretOwnerTerm(w.owners)}).One(&doc) 2864 if err != nil && err != mgo.ErrNotFound { 2865 return nil, errors.Trace(err) 2866 } 2867 // Changed but no longer in the collection so ignore. 2868 if err != nil { 2869 return details, nil 2870 } 2871 } else if known { 2872 // Record deleted. 2873 delete(w.known, changeID) 2874 deletedDetails := corewatcher.SecretTriggerChange{ 2875 URI: knownDetails.URI, 2876 } 2877 for i, detail := range details { 2878 if detail.URI.ID == changeID { 2879 details[i] = deletedDetails 2880 return details, nil 2881 } 2882 } 2883 details = append(details, deletedDetails) 2884 return details, nil 2885 } 2886 if doc.TxnRevno > knownDetails.txnRevNo { 2887 uriStr := w.backend.localID(doc.DocID) 2888 uri, err := secrets.ParseURI(uriStr) 2889 if err != nil { 2890 return nil, errors.Annotatef(err, "invalid secret URI %q", uriStr) 2891 } 2892 w.known[changeID] = rotateWatcherDetails{ 2893 txnRevNo: doc.TxnRevno, 2894 URI: uri, 2895 } 2896 details = append(details, corewatcher.SecretTriggerChange{ 2897 URI: uri, 2898 NextTriggerTime: doc.NextRotateTime.UTC(), 2899 }) 2900 } 2901 return details, nil 2902 } 2903 2904 func (w *secretsRotationWatcher) loop() (err error) { 2905 ch := make(chan watcher.Change) 2906 w.watcher.WatchCollection(secretRotateC, ch) 2907 defer w.watcher.UnwatchCollection(secretRotateC, ch) 2908 details, err := w.initial() 2909 if err != nil { 2910 return err 2911 } 2912 out := w.out 2913 for { 2914 select { 2915 case <-w.tomb.Dying(): 2916 return tomb.ErrDying 2917 case <-w.watcher.Dead(): 2918 return stateWatcherDeadError(w.watcher.Err()) 2919 case change, ok := <-ch: 2920 if !ok { 2921 return tomb.ErrDying 2922 } 2923 if details, err = w.merge(details, change); err != nil { 2924 return err 2925 } 2926 if len(details) > 0 { 2927 out = w.out 2928 } 2929 case out <- details: 2930 out = nil 2931 details = nil 2932 } 2933 } 2934 } 2935 2936 // WatchSecretRevisionsExpiryChanges returns a watcher for expiry time updates to 2937 // secret revisions with the specified owner. 2938 func (st *State) WatchSecretRevisionsExpiryChanges(ownerTags []names.Tag) (SecretsTriggerWatcher, error) { 2939 if len(ownerTags) == 0 { 2940 return nil, errors.New("missing secret owners") 2941 } 2942 owners := make([]string, len(ownerTags)) 2943 for i, owner := range ownerTags { 2944 owners[i] = owner.String() 2945 } 2946 return newSecretsExpiryWatcher(st, owners), nil 2947 } 2948 2949 type expiryWatcherDetails struct { 2950 txnRevNo int64 2951 uri *secrets.URI 2952 revision int 2953 willExpire bool 2954 } 2955 2956 type secretsExpiryWatcher struct { 2957 commonWatcher 2958 out chan []corewatcher.SecretTriggerChange 2959 2960 owners []string 2961 known map[string]expiryWatcherDetails 2962 } 2963 2964 func newSecretsExpiryWatcher(backend modelBackend, owners []string) *secretsExpiryWatcher { 2965 w := &secretsExpiryWatcher{ 2966 commonWatcher: newCommonWatcher(backend), 2967 out: make(chan []corewatcher.SecretTriggerChange), 2968 known: make(map[string]expiryWatcherDetails), 2969 owners: owners, 2970 } 2971 w.tomb.Go(func() error { 2972 defer close(w.out) 2973 return w.loop() 2974 }) 2975 return w 2976 } 2977 2978 // Changes returns a channel that will receive changes when the next expiry time 2979 // for a secret revision changes. 2980 func (w *secretsExpiryWatcher) Changes() corewatcher.SecretTriggerChannel { 2981 return w.out 2982 } 2983 2984 func (w *secretsExpiryWatcher) initial() ([]corewatcher.SecretTriggerChange, error) { 2985 var details []corewatcher.SecretTriggerChange 2986 2987 var doc secretRevisionDoc 2988 secretRevisionCollection, closer := w.db.GetCollection(secretRevisionsC) 2989 defer closer() 2990 2991 iter := secretRevisionCollection.Find(bson.D{secretOwnerTerm(w.owners)}).Iter() 2992 for iter.Next(&doc) { 2993 uriStr, _ := splitSecretRevision(w.backend.localID(doc.DocID)) 2994 uri, err := secrets.ParseURI(uriStr) 2995 if err != nil { 2996 _ = iter.Close() 2997 return nil, errors.Annotatef(err, "invalid secret URI %q", uriStr) 2998 } 2999 willExpire := doc.ExpireTime != nil 3000 w.known[doc.DocID] = expiryWatcherDetails{ 3001 txnRevNo: doc.TxnRevno, 3002 uri: uri, 3003 revision: doc.Revision, 3004 willExpire: willExpire, 3005 } 3006 if !willExpire { 3007 continue 3008 } 3009 details = append(details, corewatcher.SecretTriggerChange{ 3010 URI: uri, 3011 Revision: doc.Revision, 3012 NextTriggerTime: doc.ExpireTime.UTC(), 3013 }) 3014 } 3015 return details, errors.Trace(iter.Close()) 3016 } 3017 3018 func (w *secretsExpiryWatcher) merge(details []corewatcher.SecretTriggerChange, change watcher.Change) ([]corewatcher.SecretTriggerChange, error) { 3019 changeID := change.Id.(string) 3020 knownDetails, known := w.known[changeID] 3021 3022 doc := secretRevisionDoc{} 3023 if change.Revno >= 0 { 3024 secretRevisionCollection, closer := w.db.GetCollection(secretRevisionsC) 3025 defer closer() 3026 err := secretRevisionCollection.Find(bson.D{{"_id", change.Id}, secretOwnerTerm(w.owners)}).One(&doc) 3027 if err != nil && err != mgo.ErrNotFound { 3028 return nil, errors.Trace(err) 3029 } 3030 // Changed but no longer in the collection so ignore. 3031 if err != nil { 3032 return details, nil 3033 } 3034 } else if known { 3035 // Record deleted. 3036 delete(w.known, changeID) 3037 deletedDetails := corewatcher.SecretTriggerChange{ 3038 URI: knownDetails.uri, 3039 Revision: knownDetails.revision, 3040 } 3041 // If an earlier event was received for the same 3042 // revision, update what we know here. 3043 for i, detail := range details { 3044 if detail.URI.ID == knownDetails.uri.ID { 3045 if knownDetails.willExpire { 3046 details[i] = deletedDetails 3047 } else { 3048 details = append(details[:i], details[i+1:]...) 3049 } 3050 return details, nil 3051 } 3052 } 3053 // Only send an update if a deleted revision was 3054 // previously going to expire. 3055 if knownDetails.willExpire { 3056 details = append(details, deletedDetails) 3057 } 3058 return details, nil 3059 } 3060 if doc.TxnRevno > knownDetails.txnRevNo { 3061 uriStr, _ := splitSecretRevision(w.backend.localID(doc.DocID)) 3062 uri, err := secrets.ParseURI(uriStr) 3063 if err != nil { 3064 return nil, errors.Annotatef(err, "invalid secret URI %q", uriStr) 3065 } 3066 willExpire := doc.ExpireTime != nil 3067 w.known[changeID] = expiryWatcherDetails{ 3068 txnRevNo: doc.TxnRevno, 3069 uri: uri, 3070 revision: doc.Revision, 3071 willExpire: willExpire, 3072 } 3073 // The event we send depends on if the revision is set to expire. 3074 if willExpire { 3075 details = append(details, corewatcher.SecretTriggerChange{ 3076 URI: uri, 3077 Revision: doc.Revision, 3078 NextTriggerTime: doc.ExpireTime.UTC(), 3079 }) 3080 } else if knownDetails.willExpire { 3081 details = append(details, corewatcher.SecretTriggerChange{ 3082 URI: uri, 3083 Revision: doc.Revision, 3084 }) 3085 } 3086 } 3087 return details, nil 3088 } 3089 3090 func (w *secretsExpiryWatcher) loop() (err error) { 3091 ch := make(chan watcher.Change) 3092 w.watcher.WatchCollection(secretRevisionsC, ch) 3093 defer w.watcher.UnwatchCollection(secretRevisionsC, ch) 3094 details, err := w.initial() 3095 if err != nil { 3096 return err 3097 } 3098 out := w.out 3099 for { 3100 select { 3101 case <-w.tomb.Dying(): 3102 return tomb.ErrDying 3103 case <-w.watcher.Dead(): 3104 return stateWatcherDeadError(w.watcher.Err()) 3105 case change, ok := <-ch: 3106 if !ok { 3107 return tomb.ErrDying 3108 } 3109 if details, err = w.merge(details, change); err != nil { 3110 return err 3111 } 3112 if len(details) > 0 { 3113 out = w.out 3114 } 3115 case out <- details: 3116 out = nil 3117 details = nil 3118 } 3119 } 3120 }