github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/core/secrets/secret.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the LGPLv3, see LICENCE file for details.
     3  
     4  package secrets
     5  
     6  import (
     7  	"fmt"
     8  	"net/url"
     9  	"regexp"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/rs/xid"
    15  )
    16  
    17  // SecretConfig is used when creating a secret.
    18  type SecretConfig struct {
    19  	RotatePolicy   *RotatePolicy
    20  	NextRotateTime *time.Time
    21  	ExpireTime     *time.Time
    22  	Description    *string
    23  	Label          *string
    24  	Params         map[string]interface{}
    25  }
    26  
    27  // Validate returns an error if params are invalid.
    28  func (c *SecretConfig) Validate() error {
    29  	if c.RotatePolicy != nil && !c.RotatePolicy.IsValid() {
    30  		return errors.NotValidf("secret rotate policy %q", c.RotatePolicy)
    31  	}
    32  	if c.RotatePolicy.WillRotate() && c.NextRotateTime == nil {
    33  		return errors.New("cannot specify a secret rotate policy without a next rotate time")
    34  	}
    35  	if !c.RotatePolicy.WillRotate() && c.NextRotateTime != nil {
    36  		return errors.New("cannot specify a secret rotate time without a rotate policy")
    37  	}
    38  	return nil
    39  }
    40  
    41  // URI represents a reference to a secret.
    42  type URI struct {
    43  	SourceUUID string
    44  	ID         string
    45  }
    46  
    47  const (
    48  	idSnippet   = `[0-9a-z]{20}`
    49  	uuidSnippet = `[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`
    50  
    51  	// SecretScheme is the URL prefix for a secret.
    52  	SecretScheme = "secret"
    53  )
    54  
    55  var validUUID = regexp.MustCompile(uuidSnippet)
    56  
    57  var secretURIParse = regexp.MustCompile(`^` +
    58  	fmt.Sprintf(`((?P<source>%s)/)?(?P<id>%s)`, uuidSnippet, idSnippet) +
    59  	`$`)
    60  
    61  // ParseURI parses the specified string into a URI.
    62  func ParseURI(str string) (*URI, error) {
    63  	u, err := url.Parse(str)
    64  	if err != nil {
    65  		return nil, errors.Trace(err)
    66  	}
    67  	if u.Scheme == "" {
    68  		u.Scheme = SecretScheme
    69  	} else if u.Scheme != SecretScheme {
    70  		return nil, errors.NotValidf("secret URI scheme %q", u.Scheme)
    71  	}
    72  	if u.Host != "" && !validUUID.MatchString(u.Host) {
    73  		return nil, errors.NotValidf("host controller UUID %q", u.Host)
    74  	}
    75  
    76  	idStr := strings.TrimLeft(u.Path, "/")
    77  	if idStr == "" {
    78  		idStr = u.Opaque
    79  	}
    80  	valid := secretURIParse.MatchString(idStr)
    81  	if !valid {
    82  		return nil, errors.NotValidf("secret URI %q", str)
    83  	}
    84  	sourceUUID := secretURIParse.ReplaceAllString(idStr, "$source")
    85  	if sourceUUID == "" {
    86  		sourceUUID = u.Host
    87  	}
    88  	idPart := secretURIParse.ReplaceAllString(idStr, "$id")
    89  	id, err := xid.FromString(idPart)
    90  	if err != nil {
    91  		return nil, errors.NotValidf("secret URI %q", str)
    92  	}
    93  	result := &URI{
    94  		SourceUUID: sourceUUID,
    95  		ID:         id.String(),
    96  	}
    97  	return result, nil
    98  }
    99  
   100  // NewURI returns a new secret URI.
   101  func NewURI() *URI {
   102  	return &URI{
   103  		ID: xid.New().String(),
   104  	}
   105  }
   106  
   107  // WithSource returns a secret URI with the source.
   108  func (u *URI) WithSource(uuid string) *URI {
   109  	u.SourceUUID = uuid
   110  	return u
   111  }
   112  
   113  // IsLocal returns true if this URI is local
   114  // to the specified uuid.
   115  func (u *URI) IsLocal(sourceUUID string) bool {
   116  	return u.SourceUUID == "" || u.SourceUUID == sourceUUID
   117  }
   118  
   119  // Name generates the secret name.
   120  func (u URI) Name(revision int) string {
   121  	return fmt.Sprintf("%s-%d", u.ID, revision)
   122  }
   123  
   124  // String prints the URI as a string.
   125  func (u *URI) String() string {
   126  	if u == nil {
   127  		return ""
   128  	}
   129  	var fullPath []string
   130  	fullPath = append(fullPath, u.ID)
   131  	str := strings.Join(fullPath, "/")
   132  	if u.SourceUUID == "" {
   133  		urlValue := url.URL{
   134  			Scheme: SecretScheme,
   135  			Opaque: str,
   136  		}
   137  		return urlValue.String()
   138  	}
   139  	urlValue := url.URL{
   140  		Scheme: SecretScheme,
   141  		Host:   u.SourceUUID,
   142  		Path:   str,
   143  	}
   144  	return urlValue.String()
   145  }
   146  
   147  // SecretMetadata holds metadata about a secret.
   148  type SecretMetadata struct {
   149  	// Read only after creation.
   150  	URI *URI
   151  
   152  	// Version starts at 1 and is incremented
   153  	// whenever an incompatible change is made.
   154  	Version int
   155  
   156  	// These can be updated after creation.
   157  	Description  string
   158  	Label        string
   159  	RotatePolicy RotatePolicy
   160  
   161  	// Set by service on creation/update.
   162  
   163  	// OwnerTag is the entity which created the secret.
   164  	OwnerTag string
   165  
   166  	CreateTime time.Time
   167  	UpdateTime time.Time
   168  
   169  	// These are denormalised here for ease of access.
   170  
   171  	// LatestRevision is the most recent secret revision.
   172  	LatestRevision int
   173  	// LatestExpireTime is the expire time of the most recent revision.
   174  	LatestExpireTime *time.Time
   175  	// NextRotateTime is when the secret should be rotated.
   176  	NextRotateTime *time.Time
   177  
   178  	// AutoPrune is true if the secret revisions should be pruned when it's not been used.
   179  	AutoPrune bool
   180  
   181  	// Access is a list of access information for this secret.
   182  	Access []AccessInfo
   183  }
   184  
   185  // AccessInfo holds info about a secret access information.
   186  type AccessInfo struct {
   187  	Target string
   188  	Scope  string
   189  	Role   SecretRole
   190  }
   191  
   192  // SecretRevisionMetadata holds metadata about a secret revision.
   193  type SecretRevisionMetadata struct {
   194  	Revision    int
   195  	ValueRef    *ValueRef
   196  	BackendName *string
   197  	CreateTime  time.Time
   198  	UpdateTime  time.Time
   199  	ExpireTime  *time.Time
   200  }
   201  
   202  // SecretOwnerMetadata holds a secret metadata and any backend references of revisions.
   203  type SecretOwnerMetadata struct {
   204  	Metadata  SecretMetadata
   205  	Revisions []int
   206  }
   207  
   208  // SecretMetadataForDrain holds a secret metadata and any backend references of revisions for drain.
   209  type SecretMetadataForDrain struct {
   210  	Metadata  SecretMetadata
   211  	Revisions []SecretRevisionMetadata
   212  }
   213  
   214  // SecretConsumerMetadata holds metadata about a secret
   215  // for a consumer of the secret.
   216  type SecretConsumerMetadata struct {
   217  	// Label is used when notifying the consumer
   218  	// about changes to the secret.
   219  	Label string
   220  	// CurrentRevision is current revision the
   221  	// consumer wants to read.
   222  	CurrentRevision int
   223  	// LatestRevision is the latest secret revision.
   224  	LatestRevision int
   225  }
   226  
   227  // SecretRevisionInfo holds info used to read a secret vale.
   228  type SecretRevisionInfo struct {
   229  	Revision int
   230  	Label    string
   231  }
   232  
   233  // Filter is used when querying secrets.
   234  type Filter struct {
   235  	URI      *URI
   236  	Label    *string
   237  	Revision *int
   238  	OwnerTag *string
   239  }