func (safs *signAddFindSuite) TestAddUnsupportedFormat(c *C) { const unsupported = "type: test-only\n" + "format: 77\n" + "authority-id: canonical\n" + "primary-key: a\n" + "payload: unsupported\n" + "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + "AXNpZw==" headers := map[string]interface{}{ "authority-id": "canonical", "primary-key": "a", "format": "77", "payload": "unsupported", } aUnsupp, err := asserts.Decode([]byte(unsupported)) c.Assert(err, IsNil) c.Assert(aUnsupp.SupportedFormat(), Equals, false) err = safs.db.Add(aUnsupp) c.Assert(err, FitsTypeOf, &asserts.UnsupportedFormatError{}) c.Check(err.(*asserts.UnsupportedFormatError).Update, Equals, false) c.Check(err, ErrorMatches, `proposed "test-only" assertion has format 77 but 1 is latest supported`) c.Check(asserts.IsUnaccceptedUpdate(err), Equals, false) headers = map[string]interface{}{ "authority-id": "canonical", "primary-key": "a", "format": "1", "payload": "supported", } aSupp, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey0) c.Assert(err, IsNil) err = safs.db.Add(aSupp) c.Assert(err, IsNil) err = safs.db.Add(aUnsupp) c.Assert(err, FitsTypeOf, &asserts.UnsupportedFormatError{}) c.Check(err.(*asserts.UnsupportedFormatError).Update, Equals, true) c.Check(err, ErrorMatches, `proposed "test-only" assertion has format 77 but 1 is latest supported \(current not updated\)`) c.Check(asserts.IsUnaccceptedUpdate(err), Equals, true) }
func (s *isUnacceptedUpdateSuite) TestIsUnacceptedUpdate(c *C) { tests := []struct { err error keptCurrent bool }{ {&asserts.UnsupportedFormatError{}, false}, {&asserts.UnsupportedFormatError{Update: true}, true}, {&asserts.RevisionError{Used: 1, Current: 1}, true}, {&asserts.RevisionError{Used: 1, Current: 5}, true}, {&asserts.RevisionError{Used: 3, Current: 1}, false}, {errors.New("other error"), false}, {asserts.ErrNotFound, false}, } for _, t := range tests { c.Check(asserts.IsUnaccceptedUpdate(t.err), Equals, t.keptCurrent, Commentf("%v", t.err)) } }
// commit does a best effort of adding all the fetched assertions to the system database. func (f *fetcher) commit() error { var errs []error for _, a := range f.fetched { err := f.db.Add(a) if asserts.IsUnaccceptedUpdate(err) { if _, ok := err.(*asserts.UnsupportedFormatError); ok { // we kept the old one, but log the issue logger.Noticef("Cannot update assertion: %v", err) } // be idempotent // system db has already the same or newer continue } if err != nil { errs = append(errs, err) } } if len(errs) != 0 { return &commitError{errs: errs} } return nil }
func (safs *signAddFindSuite) TestAddSuperseding(c *C) { headers := map[string]interface{}{ "authority-id": "canonical", "primary-key": "a", } a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID) c.Assert(err, IsNil) err = safs.db.Add(a1) c.Assert(err, IsNil) retrieved1, err := safs.db.Find(asserts.TestOnlyType, map[string]string{ "primary-key": "a", }) c.Assert(err, IsNil) c.Check(retrieved1, NotNil) c.Check(retrieved1.Revision(), Equals, 0) headers["revision"] = "1" a2, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID) c.Assert(err, IsNil) err = safs.db.Add(a2) c.Assert(err, IsNil) retrieved2, err := safs.db.Find(asserts.TestOnlyType, map[string]string{ "primary-key": "a", }) c.Assert(err, IsNil) c.Check(retrieved2, NotNil) c.Check(retrieved2.Revision(), Equals, 1) err = safs.db.Add(a1) c.Check(err, ErrorMatches, "revision 0 is older than current revision 1") c.Check(asserts.IsUnaccceptedUpdate(err), Equals, true) }
func (m *DeviceManager) doRequestSerial(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() cfg, err := getSerialRequestConfig(t) if err != nil { return err } device, err := auth.Device(st) if err != nil { return err } privKey, err := m.keyPair() if err == state.ErrNoState { return fmt.Errorf("internal error: cannot find device key pair") } if err != nil { return err } // make this idempotent, look if we have already a serial assertion // for privKey serials, err := assertstate.DB(st).FindMany(asserts.SerialType, map[string]string{ "brand-id": device.Brand, "model": device.Model, "device-key-sha3-384": privKey.PublicKey().ID(), }) if err != nil && err != asserts.ErrNotFound { return err } if len(serials) == 1 { // means we saved the assertion but didn't get to the end of the task device.Serial = serials[0].(*asserts.Serial).Serial() err := auth.SetDevice(st, device) if err != nil { return err } t.SetStatus(state.DoneStatus) return nil } if len(serials) > 1 { return fmt.Errorf("internal error: multiple serial assertions for the same device key") } serial, err := getSerial(t, privKey, device, cfg) if err == errPoll { t.Logf("Will poll for device serial assertion in 60 seconds") return &state.Retry{After: retryInterval} } if err != nil { // errors & retries return err } sto := snapstate.Store(st) // try to fetch the signing key of the serial st.Unlock() a, errAcctKey := sto.Assertion(asserts.AccountKeyType, []string{serial.SignKeyID()}, nil) st.Lock() if errAcctKey == nil { err := assertstate.Add(st, a) if err != nil { if !asserts.IsUnaccceptedUpdate(err) { return err } } } // add the serial assertion to the system assertion db err = assertstate.Add(st, serial) if err != nil { // if we had failed to fetch the signing key, retry in a bit if errAcctKey != nil { t.Errorf("cannot fetch signing key for the serial: %v", errAcctKey) return &state.Retry{After: retryInterval} } return err } if repeatRequestSerial == "after-add-serial" { // For testing purposes, ensure a crash in this state works. return &state.Retry{} } device.Serial = serial.Serial() err = auth.SetDevice(st, device) if err != nil { return err } t.SetStatus(state.DoneStatus) return nil }