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(&currentDoc)
  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  }