github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/asserts/batch_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2019 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_test 21 22 import ( 23 "bytes" 24 "fmt" 25 "time" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/asserts/assertstest" 31 ) 32 33 type batchSuite struct { 34 storeSigning *assertstest.StoreStack 35 dev1Acct *asserts.Account 36 37 db *asserts.Database 38 } 39 40 var _ = Suite(&batchSuite{}) 41 42 func (s *batchSuite) SetUpTest(c *C) { 43 s.storeSigning = assertstest.NewStoreStack("can0nical", nil) 44 45 s.dev1Acct = assertstest.NewAccount(s.storeSigning, "developer1", nil, "") 46 err := s.storeSigning.Add(s.dev1Acct) 47 c.Assert(err, IsNil) 48 49 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 50 Backstore: asserts.NewMemoryBackstore(), 51 Trusted: s.storeSigning.Trusted, 52 }) 53 c.Assert(err, IsNil) 54 s.db = db 55 } 56 57 func (s *batchSuite) snapDecl(c *C, name string, extraHeaders map[string]interface{}) *asserts.SnapDeclaration { 58 headers := map[string]interface{}{ 59 "series": "16", 60 "snap-id": name + "-id", 61 "snap-name": name, 62 "publisher-id": s.dev1Acct.AccountID(), 63 "timestamp": time.Now().Format(time.RFC3339), 64 } 65 for h, v := range extraHeaders { 66 headers[h] = v 67 } 68 decl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 69 c.Assert(err, IsNil) 70 err = s.storeSigning.Add(decl) 71 c.Assert(err, IsNil) 72 return decl.(*asserts.SnapDeclaration) 73 } 74 75 func (s *batchSuite) TestAddStream(c *C) { 76 b := &bytes.Buffer{} 77 enc := asserts.NewEncoder(b) 78 // wrong order is ok 79 err := enc.Encode(s.dev1Acct) 80 c.Assert(err, IsNil) 81 enc.Encode(s.storeSigning.StoreAccountKey("")) 82 c.Assert(err, IsNil) 83 84 batch := asserts.NewBatch(nil) 85 refs, err := batch.AddStream(b) 86 c.Assert(err, IsNil) 87 c.Check(refs, DeepEquals, []*asserts.Ref{ 88 {Type: asserts.AccountType, PrimaryKey: []string{s.dev1Acct.AccountID()}}, 89 {Type: asserts.AccountKeyType, PrimaryKey: []string{s.storeSigning.StoreAccountKey("").PublicKeyID()}}, 90 }) 91 92 // noop 93 err = batch.Add(s.storeSigning.StoreAccountKey("")) 94 c.Assert(err, IsNil) 95 96 err = batch.CommitTo(s.db, nil) 97 c.Assert(err, IsNil) 98 99 devAcct, err := s.db.Find(asserts.AccountType, map[string]string{ 100 "account-id": s.dev1Acct.AccountID(), 101 }) 102 c.Assert(err, IsNil) 103 c.Check(devAcct.(*asserts.Account).Username(), Equals, "developer1") 104 } 105 106 func (s *batchSuite) TestAddEmptyStream(c *C) { 107 b := &bytes.Buffer{} 108 109 batch := asserts.NewBatch(nil) 110 refs, err := batch.AddStream(b) 111 c.Assert(err, IsNil) 112 c.Check(refs, HasLen, 0) 113 } 114 115 func (s *batchSuite) TestConsiderPreexisting(c *C) { 116 // prereq store key 117 err := s.db.Add(s.storeSigning.StoreAccountKey("")) 118 c.Assert(err, IsNil) 119 120 batch := asserts.NewBatch(nil) 121 err = batch.Add(s.dev1Acct) 122 c.Assert(err, IsNil) 123 124 err = batch.CommitTo(s.db, nil) 125 c.Assert(err, IsNil) 126 127 devAcct, err := s.db.Find(asserts.AccountType, map[string]string{ 128 "account-id": s.dev1Acct.AccountID(), 129 }) 130 c.Assert(err, IsNil) 131 c.Check(devAcct.(*asserts.Account).Username(), Equals, "developer1") 132 } 133 134 func (s *batchSuite) TestAddStreamReturnsEffectivelyAddedRefs(c *C) { 135 batch := asserts.NewBatch(nil) 136 137 err := batch.Add(s.storeSigning.StoreAccountKey("")) 138 c.Assert(err, IsNil) 139 140 b := &bytes.Buffer{} 141 enc := asserts.NewEncoder(b) 142 // wrong order is ok 143 err = enc.Encode(s.dev1Acct) 144 c.Assert(err, IsNil) 145 // this was already added to the batch 146 enc.Encode(s.storeSigning.StoreAccountKey("")) 147 c.Assert(err, IsNil) 148 149 // effectively adds only the developer1 account 150 refs, err := batch.AddStream(b) 151 c.Assert(err, IsNil) 152 c.Check(refs, DeepEquals, []*asserts.Ref{ 153 {Type: asserts.AccountType, PrimaryKey: []string{s.dev1Acct.AccountID()}}, 154 }) 155 156 err = batch.CommitTo(s.db, nil) 157 c.Assert(err, IsNil) 158 159 devAcct, err := s.db.Find(asserts.AccountType, map[string]string{ 160 "account-id": s.dev1Acct.AccountID(), 161 }) 162 c.Assert(err, IsNil) 163 c.Check(devAcct.(*asserts.Account).Username(), Equals, "developer1") 164 } 165 166 func (s *batchSuite) TestCommitRefusesSelfSignedKey(c *C) { 167 aKey, _ := assertstest.GenerateKey(752) 168 aSignDB := assertstest.NewSigningDB("can0nical", aKey) 169 170 aKeyEncoded, err := asserts.EncodePublicKey(aKey.PublicKey()) 171 c.Assert(err, IsNil) 172 173 headers := map[string]interface{}{ 174 "authority-id": "can0nical", 175 "account-id": "can0nical", 176 "public-key-sha3-384": aKey.PublicKey().ID(), 177 "name": "default", 178 "since": time.Now().UTC().Format(time.RFC3339), 179 } 180 acctKey, err := aSignDB.Sign(asserts.AccountKeyType, headers, aKeyEncoded, "") 181 c.Assert(err, IsNil) 182 183 headers = map[string]interface{}{ 184 "authority-id": "can0nical", 185 "brand-id": "can0nical", 186 "repair-id": "2", 187 "summary": "repair two", 188 "timestamp": time.Now().UTC().Format(time.RFC3339), 189 } 190 repair, err := aSignDB.Sign(asserts.RepairType, headers, []byte("#script"), "") 191 c.Assert(err, IsNil) 192 193 batch := asserts.NewBatch(nil) 194 195 err = batch.Add(repair) 196 c.Assert(err, IsNil) 197 198 err = batch.Add(acctKey) 199 c.Assert(err, IsNil) 200 201 // this must fail 202 err = batch.CommitTo(s.db, nil) 203 c.Assert(err, ErrorMatches, `circular assertions are not expected:.*`) 204 } 205 206 func (s *batchSuite) TestAddUnsupported(c *C) { 207 restore := asserts.MockMaxSupportedFormat(asserts.SnapDeclarationType, 111) 208 defer restore() 209 210 batch := asserts.NewBatch(nil) 211 212 var a asserts.Assertion 213 (func() { 214 restore := asserts.MockMaxSupportedFormat(asserts.SnapDeclarationType, 999) 215 defer restore() 216 headers := map[string]interface{}{ 217 "format": "999", 218 "revision": "1", 219 "series": "16", 220 "snap-id": "snap-id-1", 221 "snap-name": "foo", 222 "publisher-id": s.dev1Acct.AccountID(), 223 "timestamp": time.Now().Format(time.RFC3339), 224 } 225 var err error 226 a, err = s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 227 c.Assert(err, IsNil) 228 })() 229 230 err := batch.Add(a) 231 c.Check(err, ErrorMatches, `proposed "snap-declaration" assertion has format 999 but 111 is latest supported`) 232 } 233 234 func (s *batchSuite) TestAddUnsupportedIgnore(c *C) { 235 restore := asserts.MockMaxSupportedFormat(asserts.SnapDeclarationType, 111) 236 defer restore() 237 238 var uRef *asserts.Ref 239 unsupported := func(ref *asserts.Ref, _ error) error { 240 uRef = ref 241 return nil 242 } 243 244 batch := asserts.NewBatch(unsupported) 245 246 var a asserts.Assertion 247 (func() { 248 restore := asserts.MockMaxSupportedFormat(asserts.SnapDeclarationType, 999) 249 defer restore() 250 headers := map[string]interface{}{ 251 "format": "999", 252 "revision": "1", 253 "series": "16", 254 "snap-id": "snap-id-1", 255 "snap-name": "foo", 256 "publisher-id": s.dev1Acct.AccountID(), 257 "timestamp": time.Now().Format(time.RFC3339), 258 } 259 var err error 260 a, err = s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 261 c.Assert(err, IsNil) 262 })() 263 264 err := batch.Add(a) 265 c.Check(err, IsNil) 266 c.Check(uRef, DeepEquals, &asserts.Ref{ 267 Type: asserts.SnapDeclarationType, 268 PrimaryKey: []string{"16", "snap-id-1"}, 269 }) 270 } 271 272 func (s *batchSuite) TestCommitPartial(c *C) { 273 // Commit does add any successful assertion until the first error 274 275 // store key already present 276 err := s.db.Add(s.storeSigning.StoreAccountKey("")) 277 c.Assert(err, IsNil) 278 279 batch := asserts.NewBatch(nil) 280 281 snapDeclFoo := s.snapDecl(c, "foo", nil) 282 283 err = batch.Add(snapDeclFoo) 284 c.Assert(err, IsNil) 285 err = batch.Add(s.dev1Acct) 286 c.Assert(err, IsNil) 287 288 // too old 289 rev := 1 290 headers := map[string]interface{}{ 291 "snap-id": "foo-id", 292 "snap-sha3-384": makeDigest(rev), 293 "snap-size": fmt.Sprintf("%d", len(fakeSnap(rev))), 294 "snap-revision": fmt.Sprintf("%d", rev), 295 "developer-id": s.dev1Acct.AccountID(), 296 "timestamp": time.Time{}.Format(time.RFC3339), 297 } 298 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 299 c.Assert(err, IsNil) 300 301 err = batch.Add(snapRev) 302 c.Assert(err, IsNil) 303 304 err = batch.CommitTo(s.db, &asserts.CommitOptions{Precheck: false}) 305 c.Check(err, ErrorMatches, `(?ms).*validity.*`) 306 307 // snap-declaration was added anyway 308 _, err = s.db.Find(asserts.SnapDeclarationType, map[string]string{ 309 "series": "16", 310 "snap-id": "foo-id", 311 }) 312 c.Assert(err, IsNil) 313 } 314 315 func (s *batchSuite) TestCommitMissing(c *C) { 316 // store key already present 317 err := s.db.Add(s.storeSigning.StoreAccountKey("")) 318 c.Assert(err, IsNil) 319 320 batch := asserts.NewBatch(nil) 321 322 snapDeclFoo := s.snapDecl(c, "foo", nil) 323 324 err = batch.Add(snapDeclFoo) 325 c.Assert(err, IsNil) 326 327 err = batch.CommitTo(s.db, nil) 328 c.Check(err, ErrorMatches, `cannot resolve prerequisite assertion: account.*`) 329 } 330 331 func (s *batchSuite) TestPrecheckPartial(c *C) { 332 // store key already present 333 err := s.db.Add(s.storeSigning.StoreAccountKey("")) 334 c.Assert(err, IsNil) 335 336 batch := asserts.NewBatch(nil) 337 338 snapDeclFoo := s.snapDecl(c, "foo", nil) 339 340 err = batch.Add(snapDeclFoo) 341 c.Assert(err, IsNil) 342 err = batch.Add(s.dev1Acct) 343 c.Assert(err, IsNil) 344 345 // too old 346 rev := 1 347 headers := map[string]interface{}{ 348 "snap-id": "foo-id", 349 "snap-sha3-384": makeDigest(rev), 350 "snap-size": fmt.Sprintf("%d", len(fakeSnap(rev))), 351 "snap-revision": fmt.Sprintf("%d", rev), 352 "developer-id": s.dev1Acct.AccountID(), 353 "timestamp": time.Time{}.Format(time.RFC3339), 354 } 355 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 356 c.Assert(err, IsNil) 357 358 err = batch.Add(snapRev) 359 c.Assert(err, IsNil) 360 361 err = batch.CommitTo(s.db, &asserts.CommitOptions{Precheck: true}) 362 c.Check(err, ErrorMatches, `(?ms).*validity.*`) 363 364 // nothing was added 365 _, err = s.db.Find(asserts.SnapDeclarationType, map[string]string{ 366 "series": "16", 367 "snap-id": "foo-id", 368 }) 369 c.Assert(asserts.IsNotFound(err), Equals, true) 370 } 371 372 func (s *batchSuite) TestPrecheckHappy(c *C) { 373 // store key already present 374 err := s.db.Add(s.storeSigning.StoreAccountKey("")) 375 c.Assert(err, IsNil) 376 377 batch := asserts.NewBatch(nil) 378 379 snapDeclFoo := s.snapDecl(c, "foo", nil) 380 381 err = batch.Add(snapDeclFoo) 382 c.Assert(err, IsNil) 383 err = batch.Add(s.dev1Acct) 384 c.Assert(err, IsNil) 385 386 rev := 1 387 revDigest := makeDigest(rev) 388 headers := map[string]interface{}{ 389 "snap-id": "foo-id", 390 "snap-sha3-384": revDigest, 391 "snap-size": fmt.Sprintf("%d", len(fakeSnap(rev))), 392 "snap-revision": fmt.Sprintf("%d", rev), 393 "developer-id": s.dev1Acct.AccountID(), 394 "timestamp": time.Now().Format(time.RFC3339), 395 } 396 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 397 c.Assert(err, IsNil) 398 399 err = batch.Add(snapRev) 400 c.Assert(err, IsNil) 401 402 // test precheck on its own 403 err = batch.DoPrecheck(s.db) 404 c.Assert(err, IsNil) 405 406 // nothing was added yet 407 _, err = s.db.Find(asserts.SnapDeclarationType, map[string]string{ 408 "series": "16", 409 "snap-id": "foo-id", 410 }) 411 c.Assert(asserts.IsNotFound(err), Equals, true) 412 413 // commit (with precheck) 414 err = batch.CommitTo(s.db, &asserts.CommitOptions{Precheck: true}) 415 c.Assert(err, IsNil) 416 417 _, err = s.db.Find(asserts.SnapRevisionType, map[string]string{ 418 "snap-sha3-384": revDigest, 419 }) 420 c.Check(err, IsNil) 421 } 422 423 func (s *batchSuite) TestFetch(c *C) { 424 err := s.db.Add(s.storeSigning.StoreAccountKey("")) 425 c.Assert(err, IsNil) 426 427 s.snapDecl(c, "foo", nil) 428 429 rev := 10 430 revDigest := makeDigest(rev) 431 headers := map[string]interface{}{ 432 "snap-id": "foo-id", 433 "snap-sha3-384": revDigest, 434 "snap-size": fmt.Sprintf("%d", len(fakeSnap(rev))), 435 "snap-revision": fmt.Sprintf("%d", rev), 436 "developer-id": s.dev1Acct.AccountID(), 437 "timestamp": time.Now().Format(time.RFC3339), 438 } 439 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 440 c.Assert(err, IsNil) 441 442 err = s.storeSigning.Add(snapRev) 443 c.Assert(err, IsNil) 444 ref := snapRev.Ref() 445 446 batch := asserts.NewBatch(nil) 447 448 // retrieve from storeSigning 449 retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) { 450 return ref.Resolve(s.storeSigning.Find) 451 } 452 // fetching the snap-revision 453 fetching := func(f asserts.Fetcher) error { 454 return f.Fetch(ref) 455 } 456 457 err = batch.Fetch(s.db, retrieve, fetching) 458 c.Assert(err, IsNil) 459 460 // nothing was added yet 461 _, err = s.db.Find(asserts.SnapDeclarationType, map[string]string{ 462 "series": "16", 463 "snap-id": "foo-id", 464 }) 465 c.Assert(asserts.IsNotFound(err), Equals, true) 466 467 // commit 468 err = batch.CommitTo(s.db, nil) 469 c.Assert(err, IsNil) 470 471 _, err = s.db.Find(asserts.SnapRevisionType, map[string]string{ 472 "snap-sha3-384": revDigest, 473 }) 474 c.Check(err, IsNil) 475 }