gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/account_key.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package asserts
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  	"time"
    26  )
    27  
    28  var validAccountKeyName = regexp.MustCompile(`^(?:[a-z0-9]+-?)*[a-z](?:-?[a-z0-9])*$`)
    29  
    30  // AccountKey holds an account-key assertion, asserting a public key
    31  // belonging to the account.
    32  type AccountKey struct {
    33  	assertionBase
    34  	sinceUntil
    35  	pubKey PublicKey
    36  }
    37  
    38  type sinceUntil struct {
    39  	since time.Time
    40  	until time.Time
    41  }
    42  
    43  func checkSinceUntilWhat(m map[string]interface{}, what string) (*sinceUntil, error) {
    44  	since, err := checkRFC3339DateWhat(m, "since", what)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	until, err := checkRFC3339DateWithDefaultWhat(m, "until", what, time.Time{})
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	if !until.IsZero() && until.Before(since) {
    54  		return nil, fmt.Errorf("'until' time cannot be before 'since' time")
    55  	}
    56  
    57  	return &sinceUntil{
    58  		since: since,
    59  		until: until,
    60  	}, nil
    61  }
    62  
    63  // AccountID returns the account-id of this account-key.
    64  func (ak *AccountKey) AccountID() string {
    65  	return ak.HeaderString("account-id")
    66  }
    67  
    68  // Name returns the name of the account key.
    69  func (ak *AccountKey) Name() string {
    70  	return ak.HeaderString("name")
    71  }
    72  
    73  func IsValidAccountKeyName(name string) bool {
    74  	return validAccountKeyName.MatchString(name)
    75  }
    76  
    77  // Since returns the time when the account key starts being valid.
    78  func (ak *AccountKey) Since() time.Time {
    79  	return ak.since
    80  }
    81  
    82  // Until returns the time when the account key stops being valid. A zero time means the key is valid forever.
    83  func (ak *AccountKey) Until() time.Time {
    84  	return ak.until
    85  }
    86  
    87  // PublicKeyID returns the key id used for lookup of the account key.
    88  func (ak *AccountKey) PublicKeyID() string {
    89  	return ak.pubKey.ID()
    90  }
    91  
    92  // isValidAt returns whether the since-until constraint is valid at 'when' time.
    93  func (su *sinceUntil) isValidAt(when time.Time) bool {
    94  	valid := when.After(su.since) || when.Equal(su.since)
    95  	if valid && !su.until.IsZero() {
    96  		valid = when.Before(su.until)
    97  	}
    98  	return valid
    99  }
   100  
   101  // isValidAssumingCurTimeWithin returns whether the since-until constraint  is
   102  // possibly valid if the current time is known to be within [earliest,
   103  // latest]. That means the intersection of possible current times and
   104  // validity is not empty.
   105  // If latest is zero, then current time is assumed to be >=earliest.
   106  // If earliest == latest this is equivalent to isKeyValidAt().
   107  func (su *sinceUntil) isValidAssumingCurTimeWithin(earliest, latest time.Time) bool {
   108  	if !latest.IsZero() {
   109  		// impossible input => false
   110  		if latest.Before(earliest) {
   111  			return false
   112  		}
   113  		if latest.Before(su.since) {
   114  			return false
   115  		}
   116  	}
   117  	if !su.until.IsZero() {
   118  		if earliest.After(su.until) || earliest.Equal(su.until) {
   119  			return false
   120  		}
   121  	}
   122  	return true
   123  }
   124  
   125  // publicKey returns the underlying public key of the account key.
   126  func (ak *AccountKey) publicKey() PublicKey {
   127  	return ak.pubKey
   128  }
   129  
   130  func checkPublicKey(ab *assertionBase, keyIDName string) (PublicKey, error) {
   131  	pubKey, err := DecodePublicKey(ab.Body())
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	keyID, err := checkNotEmptyString(ab.headers, keyIDName)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	if keyID != pubKey.ID() {
   140  		return nil, fmt.Errorf("public key does not match provided key id")
   141  	}
   142  	return pubKey, nil
   143  }
   144  
   145  // Implement further consistency checks.
   146  func (ak *AccountKey) checkConsistency(db RODatabase, acck *AccountKey) error {
   147  	if !db.IsTrustedAccount(ak.AuthorityID()) {
   148  		return fmt.Errorf("account-key assertion for %q is not signed by a directly trusted authority: %s", ak.AccountID(), ak.AuthorityID())
   149  	}
   150  	_, err := db.Find(AccountType, map[string]string{
   151  		"account-id": ak.AccountID(),
   152  	})
   153  	if IsNotFound(err) {
   154  		return fmt.Errorf("account-key assertion for %q does not have a matching account assertion", ak.AccountID())
   155  	}
   156  	if err != nil {
   157  		return err
   158  	}
   159  	// XXX: Make this unconditional once account-key assertions are required to have a name.
   160  	if ak.Name() != "" {
   161  		// Check that we don't end up with multiple keys with
   162  		// different IDs but the same account-id and name.
   163  		// Note that this is a non-transactional check-then-add, so
   164  		// is not a hard guarantee.  Backstores that can implement a
   165  		// unique constraint should do so.
   166  		assertions, err := db.FindMany(AccountKeyType, map[string]string{
   167  			"account-id": ak.AccountID(),
   168  			"name":       ak.Name(),
   169  		})
   170  		if err != nil && !IsNotFound(err) {
   171  			return err
   172  		}
   173  		for _, assertion := range assertions {
   174  			existingAccKey := assertion.(*AccountKey)
   175  			if ak.PublicKeyID() != existingAccKey.PublicKeyID() {
   176  				return fmt.Errorf("account-key assertion for %q with ID %q has the same name %q as existing ID %q", ak.AccountID(), ak.PublicKeyID(), ak.Name(), existingAccKey.PublicKeyID())
   177  			}
   178  		}
   179  	}
   180  	return nil
   181  }
   182  
   183  // expected interface is implemented
   184  var _ consistencyChecker = (*AccountKey)(nil)
   185  
   186  // Prerequisites returns references to this account-key's prerequisite assertions.
   187  func (ak *AccountKey) Prerequisites() []*Ref {
   188  	return []*Ref{
   189  		{Type: AccountType, PrimaryKey: []string{ak.AccountID()}},
   190  	}
   191  }
   192  
   193  func assembleAccountKey(assert assertionBase) (Assertion, error) {
   194  	_, err := checkNotEmptyString(assert.headers, "account-id")
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	// XXX: We should require name to be present after backfilling existing assertions.
   200  	_, ok := assert.headers["name"]
   201  	if ok {
   202  		_, err = checkStringMatches(assert.headers, "name", validAccountKeyName)
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  	}
   207  
   208  	sinceUntil, err := checkSinceUntilWhat(assert.headers, "header")
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	pubk, err := checkPublicKey(&assert, "public-key-sha3-384")
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  
   218  	// ignore extra headers for future compatibility
   219  	return &AccountKey{
   220  		assertionBase: assert,
   221  		sinceUntil:    *sinceUntil,
   222  		pubKey:        pubk,
   223  	}, nil
   224  }
   225  
   226  // AccountKeyRequest holds an account-key-request assertion, which is a self-signed request to prove that the requester holds the private key and wishes to create an account-key assertion for it.
   227  type AccountKeyRequest struct {
   228  	assertionBase
   229  	sinceUntil
   230  	pubKey PublicKey
   231  }
   232  
   233  // AccountID returns the account-id of this account-key-request.
   234  func (akr *AccountKeyRequest) AccountID() string {
   235  	return akr.HeaderString("account-id")
   236  }
   237  
   238  // Name returns the name of the account key.
   239  func (akr *AccountKeyRequest) Name() string {
   240  	return akr.HeaderString("name")
   241  }
   242  
   243  // Since returns the time when the requested account key starts being valid.
   244  func (akr *AccountKeyRequest) Since() time.Time {
   245  	return akr.since
   246  }
   247  
   248  // Until returns the time when the requested account key stops being valid. A zero time means the key is valid forever.
   249  func (akr *AccountKeyRequest) Until() time.Time {
   250  	return akr.until
   251  }
   252  
   253  // PublicKeyID returns the underlying public key ID of the requested account key.
   254  func (akr *AccountKeyRequest) PublicKeyID() string {
   255  	return akr.pubKey.ID()
   256  }
   257  
   258  // signKey returns the underlying public key of the requested account key.
   259  func (akr *AccountKeyRequest) signKey() PublicKey {
   260  	return akr.pubKey
   261  }
   262  
   263  // Implement further consistency checks.
   264  func (akr *AccountKeyRequest) checkConsistency(db RODatabase, acck *AccountKey) error {
   265  	_, err := db.Find(AccountType, map[string]string{
   266  		"account-id": akr.AccountID(),
   267  	})
   268  	if IsNotFound(err) {
   269  		return fmt.Errorf("account-key-request assertion for %q does not have a matching account assertion", akr.AccountID())
   270  	}
   271  	if err != nil {
   272  		return err
   273  	}
   274  	return nil
   275  }
   276  
   277  // expected interfaces are implemented
   278  var (
   279  	_ consistencyChecker = (*AccountKeyRequest)(nil)
   280  	_ customSigner       = (*AccountKeyRequest)(nil)
   281  )
   282  
   283  // Prerequisites returns references to this account-key-request's prerequisite assertions.
   284  func (akr *AccountKeyRequest) Prerequisites() []*Ref {
   285  	return []*Ref{
   286  		{Type: AccountType, PrimaryKey: []string{akr.AccountID()}},
   287  	}
   288  }
   289  
   290  func assembleAccountKeyRequest(assert assertionBase) (Assertion, error) {
   291  	_, err := checkNotEmptyString(assert.headers, "account-id")
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	_, err = checkStringMatches(assert.headers, "name", validAccountKeyName)
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  
   301  	sinceUntil, err := checkSinceUntilWhat(assert.headers, "header")
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	pubk, err := checkPublicKey(&assert, "public-key-sha3-384")
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	// ignore extra headers for future compatibility
   312  	return &AccountKeyRequest{
   313  		assertionBase: assert,
   314  		sinceUntil:    *sinceUntil,
   315  		pubKey:        pubk,
   316  	}, nil
   317  }