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 }