func (m *DeviceManager) doGenerateDeviceKey(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() device, err := auth.Device(st) if err != nil { return err } if device.KeyID != "" { // nothing to do return nil } keyPair, err := rsa.GenerateKey(rand.Reader, keyLength) if err != nil { return fmt.Errorf("cannot generate device key pair: %v", err) } privKey := asserts.RSAPrivateKey(keyPair) err = m.keypairMgr.Put(privKey) if err != nil { return fmt.Errorf("cannot store device key pair: %v", err) } device.KeyID = privKey.PublicKey().ID() err = auth.SetDevice(st, device) if err != nil { return err } t.SetStatus(state.DoneStatus) return nil }
func (s *authContextSetupSuite) TestStoreID(c *C) { st := s.o.State() st.Lock() defer st.Unlock() st.Unlock() storeID, err := s.ac.StoreID("fallback") st.Lock() c.Assert(err, IsNil) c.Check(storeID, Equals, "fallback") // setup model in system state auth.SetDevice(st, &auth.DeviceState{ Brand: s.serial.BrandID(), Model: s.serial.Model(), Serial: s.serial.Serial(), }) err = assertstate.Add(st, s.model) c.Assert(err, IsNil) st.Unlock() storeID, err = s.ac.StoreID("fallback") st.Lock() c.Assert(err, IsNil) c.Check(storeID, Equals, "my-brand-store-id") }
func (s *authContextSetupSuite) TestDeviceSessionRequest(c *C) { st := s.o.State() st.Lock() defer st.Unlock() st.Unlock() _, _, err := s.ac.DeviceSessionRequest("NONCE") st.Lock() c.Check(err, Equals, auth.ErrNoSerial) // setup serial and key in system state err = assertstate.Add(st, s.serial) c.Assert(err, IsNil) kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) c.Assert(err, IsNil) err = kpMgr.Put(deviceKey) c.Assert(err, IsNil) auth.SetDevice(st, &auth.DeviceState{ Brand: s.serial.BrandID(), Model: s.serial.Model(), Serial: s.serial.Serial(), KeyID: deviceKey.PublicKey().ID(), }) st.Unlock() req, encSerial, err := s.ac.DeviceSessionRequest("NONCE") st.Lock() c.Assert(err, IsNil) c.Check(bytes.HasPrefix(req, []byte("type: device-session-request\n")), Equals, true) c.Check(encSerial, DeepEquals, asserts.Encode(s.serial)) }
func importAssertionsFromSeed(st *state.State) error { device, err := auth.Device(st) if err != nil { return err } // set device,model from the model assertion assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions") dc, err := ioutil.ReadDir(assertSeedDir) if err != nil { return fmt.Errorf("cannot read assert seed dir: %s", err) } // FIXME: remove this check once asserts are mandatory if len(dc) == 0 { return nil } // collect var modelRef *asserts.Ref batch := assertstate.NewBatch() for _, fi := range dc { fn := filepath.Join(assertSeedDir, fi.Name()) refs, err := readAsserts(fn, batch) if err != nil { return fmt.Errorf("cannot read assertions: %s", err) } for _, ref := range refs { if ref.Type == asserts.ModelType { if modelRef != nil && modelRef.Unique() != ref.Unique() { return fmt.Errorf("cannot add more than one model assertion") } modelRef = ref } } } // verify we have one model assertion if modelRef == nil { return fmt.Errorf("need a model assertion") } if err := batch.Commit(st); err != nil { return err } a, err := modelRef.Resolve(assertstate.DB(st).Find) if err != nil { return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err) } modelAssertion := a.(*asserts.Model) // set device,model from the model assertion device.Brand = modelAssertion.BrandID() device.Model = modelAssertion.Model() if err := auth.SetDevice(st, device); err != nil { return err } return nil }
func (as *authSuite) TestAuthContextUpdateDeviceAuthOtherUpdate(c *C) { as.state.Lock() device, _ := auth.Device(as.state) otherUpdateDevice := *device otherUpdateDevice.SessionMacaroon = "othe-session-macaroon" otherUpdateDevice.KeyID = "KEYID" err := auth.SetDevice(as.state, &otherUpdateDevice) as.state.Unlock() c.Check(err, IsNil) sessionMacaroon := "the-device-macaroon" authContext := auth.NewAuthContext(as.state, nil) curDevice, err := authContext.UpdateDeviceAuth(device, sessionMacaroon) c.Assert(err, IsNil) as.state.Lock() deviceFromState, err := auth.Device(as.state) as.state.Unlock() c.Check(err, IsNil) c.Check(deviceFromState, DeepEquals, curDevice) c.Check(curDevice, DeepEquals, &auth.DeviceState{ KeyID: "KEYID", SessionMacaroon: sessionMacaroon, }) }
func (s *deviceMgrSuite) TestDoRequestSerialIdempotentAfterGotSerial(c *C) { privKey, _ := assertstest.GenerateKey(1024) mockServer := s.mockServer(c, "REQID-1") defer mockServer.Close() mockRequestIDURL := mockServer.URL + "/identity/api/v1/request-id" restore := devicestate.MockRequestIDURL(mockRequestIDURL) defer restore() mockSerialRequestURL := mockServer.URL + "/identity/api/v1/devices" restore = devicestate.MockSerialRequestURL(mockSerialRequestURL) defer restore() restore = devicestate.MockRepeatRequestSerial("after-got-serial") defer restore() s.state.Lock() defer s.state.Unlock() // setup state as done by first-boot/Ensure/doGenerateDeviceKey auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", KeyID: privKey.PublicKey().ID(), }) s.mgr.KeypairManager().Put(privKey) t := s.state.NewTask("request-serial", "test") chg := s.state.NewChange("become-operational", "...") chg.AddTask(t) s.state.Unlock() s.mgr.Ensure() s.mgr.Wait() s.state.Lock() c.Check(chg.Status(), Equals, state.DoingStatus) device, err := auth.Device(s.state) c.Check(err, IsNil) _, err = s.db.Find(asserts.SerialType, map[string]string{ "brand-id": "canonical", "model": "pc", "serial": "9999", }) c.Assert(err, Equals, asserts.ErrNotFound) s.state.Unlock() s.mgr.Ensure() s.mgr.Wait() s.state.Lock() // Repeated handler run but set original serial. c.Check(chg.Status(), Equals, state.DoneStatus) device, err = auth.Device(s.state) c.Check(err, IsNil) c.Check(device.Serial, Equals, "9999") }
func (as *authSuite) TestAuthContextDevice(c *C) { device := &auth.DeviceState{Brand: "some-brand"} as.state.Lock() err := auth.SetDevice(as.state, device) as.state.Unlock() c.Check(err, IsNil) authContext := auth.NewAuthContext(as.state, nil) deviceFromState, err := authContext.Device() c.Check(err, IsNil) c.Check(deviceFromState, DeepEquals, device) }
func (as *authSuite) TestSetDevice(c *C) { as.state.Lock() device, err := auth.Device(as.state) as.state.Unlock() c.Check(err, IsNil) c.Check(device, DeepEquals, &auth.DeviceState{}) as.state.Lock() err = auth.SetDevice(as.state, &auth.DeviceState{Brand: "some-brand"}) c.Check(err, IsNil) device, err = auth.Device(as.state) as.state.Unlock() c.Check(err, IsNil) c.Check(device, DeepEquals, &auth.DeviceState{Brand: "some-brand"}) }
func (s *deviceMgrSuite) TestDeviceAssertionsDeviceSessionRequest(c *C) { // nothing there _, _, err := s.mgr.DeviceSessionRequest("NONCE-1") c.Check(err, Equals, state.ErrNoState) // setup state as done by device initialisation s.state.Lock() devKey, _ := assertstest.GenerateKey(1024) encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey()) c.Check(err, IsNil) seriala, err := s.storeSigning.Sign(asserts.SerialType, map[string]interface{}{ "brand-id": "canonical", "model": "pc", "serial": "8989", "device-key": string(encDevKey), "device-key-sha3-384": devKey.PublicKey().ID(), "timestamp": time.Now().Format(time.RFC3339), }, nil, "") c.Assert(err, IsNil) err = assertstate.Add(s.state, seriala) c.Assert(err, IsNil) auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", Serial: "8989", KeyID: devKey.PublicKey().ID(), }) s.mgr.KeypairManager().Put(devKey) s.state.Unlock() sessReq, serial, err := s.mgr.DeviceSessionRequest("NONCE-1") c.Assert(err, IsNil) c.Check(serial.Serial(), Equals, "8989") // correctly signed with device key err = asserts.SignatureCheck(sessReq, devKey.PublicKey()) c.Check(err, IsNil) c.Check(sessReq.BrandID(), Equals, "canonical") c.Check(sessReq.Model(), Equals, "pc") c.Check(sessReq.Serial(), Equals, "8989") c.Check(sessReq.Nonce(), Equals, "NONCE-1") }
// alreadyFirstbooted recovers already first booted devices with the old method appropriately func (m *DeviceManager) alreadyFirstbooted() error { device, err := auth.Device(m.state) if err != nil { return err } // recover key-id if device.Brand != "" && device.Model != "" { serials, err := assertstate.DB(m.state).FindMany(asserts.SerialType, map[string]string{ "brand-id": device.Brand, "model": device.Model, }) if err != nil && err != asserts.ErrNotFound { return err } if len(serials) == 1 { // we can recover the key id from the assertion serial := serials[0].(*asserts.Serial) keyID := serial.DeviceKey().ID() device.KeyID = keyID device.Serial = serial.Serial() err := auth.SetDevice(m.state, device) if err != nil { return err } // best effort to cleanup abandoned keys pat := filepath.Join(dirs.SnapDeviceDir, "private-keys-v1", "*") keyFns, err := filepath.Glob(pat) if err != nil { panic(fmt.Sprintf("invalid glob for device keys: %v", err)) } for _, keyFn := range keyFns { if filepath.Base(keyFn) == keyID { continue } os.Remove(keyFn) } } } m.state.Set("seeded", true) return nil }
func (s *deviceMgrSuite) TestCheckKernel(c *C) { release.OnClassic = false s.state.Lock() defer s.state.Unlock() // nothing is setup kernelInfo := snaptest.MockInfo(c, `type: kernel name: lnrk`, nil) err := devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, snapstate.Flags{}) c.Check(err, ErrorMatches, `cannot install kernel without model assertion`) // setup model assertion model, err := s.storeSigning.Sign(asserts.ModelType, map[string]interface{}{ "series": "16", "brand-id": "canonical", "model": "pc", "gadget": "pc", "kernel": "krnl", "architecture": "amd64", "timestamp": time.Now().Format(time.RFC3339), }, nil, "") c.Assert(err, IsNil) err = assertstate.Add(s.state, model) c.Assert(err, IsNil) err = auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", }) c.Assert(err, IsNil) err = devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, snapstate.Flags{}) c.Check(err, ErrorMatches, `cannot install kernel "lnrk", model assertion requests "krnl"`) // install krnl kernel krnlKernelInfo := snaptest.MockInfo(c, `type: kernel name: krnl`, nil) err = devicestate.CheckGadgetOrKernel(s.state, krnlKernelInfo, nil, snapstate.Flags{}) c.Check(err, IsNil) }
func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkError(c *C) { release.OnClassic = false s.state.Lock() // seeded s.state.Set("seeded", true) // has serial auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", Serial: "8989", }) s.state.Unlock() bootloader := boottest.NewMockBootloader("mock", c.MkDir()) bootloader.GetErr = fmt.Errorf("bootloader err") partition.ForceBootloader(bootloader) defer partition.ForceBootloader(nil) s.mgr.SetBootOkRan(false) err := s.mgr.Ensure() c.Assert(err, ErrorMatches, "devicemgr: bootloader err") }
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 }
func (ms *mgrsSuite) TestInstallKernelSnapUpdatesBootloader(c *C) { bootloader := boottest.NewMockBootloader("mock", c.MkDir()) partition.ForceBootloader(bootloader) defer partition.ForceBootloader(nil) restore := release.MockOnClassic(false) defer restore() brandAcct := assertstest.NewAccount(ms.storeSigning, "my-brand", map[string]interface{}{ "account-id": "my-brand", "verification": "certified", }, "") brandAccKey := assertstest.NewAccountKey(ms.storeSigning, brandAcct, nil, brandPrivKey.PublicKey(), "") brandSigning := assertstest.NewSigningDB("my-brand", brandPrivKey) model, err := brandSigning.Sign(asserts.ModelType, map[string]interface{}{ "series": "16", "authority-id": "my-brand", "brand-id": "my-brand", "model": "my-model", "architecture": "amd64", "store": "my-brand-store-id", "gadget": "gadget", "kernel": "krnl", "timestamp": time.Now().Format(time.RFC3339), }, nil, "") c.Assert(err, IsNil) const packageKernel = ` name: krnl version: 4.0-1 type: kernel` files := [][]string{ {"kernel.img", "I'm a kernel"}, {"initrd.img", "...and I'm an initrd"}, {"meta/kernel.yaml", "version: 4.2"}, } snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) st := ms.o.State() st.Lock() defer st.Unlock() // setup model assertion err = assertstate.Add(st, ms.storeSigning.StoreAccountKey("")) c.Assert(err, IsNil) err = assertstate.Add(st, brandAcct) c.Assert(err, IsNil) err = assertstate.Add(st, brandAccKey) c.Assert(err, IsNil) auth.SetDevice(st, &auth.DeviceState{ Brand: "my-brand", Model: "my-model", }) err = assertstate.Add(st, model) c.Assert(err, IsNil) ts, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "krnl"}, snapPath, "", snapstate.Flags{}) c.Assert(err, IsNil) chg := st.NewChange("install-snap", "...") chg.AddAll(ts) st.Unlock() err = ms.o.Settle() st.Lock() c.Assert(err, IsNil) c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) c.Assert(bootloader.BootVars, DeepEquals, map[string]string{ "snap_try_kernel": "krnl_x1.snap", "snap_mode": "try", }) }
func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlRecover(c *C) { release.OnClassic = false restore := devicestate.MockPopulateStateFromSeed(func(*state.State) (ts []*state.TaskSet, err error) { return nil, errors.New("should not be called") }) defer restore() s.state.Lock() defer s.state.Unlock() s.setupCore(c, "ubuntu-core", ` name: ubuntu-core type: os version: ubuntu-core `, "") // have a model assertion model, err := s.storeSigning.Sign(asserts.ModelType, map[string]interface{}{ "series": "16", "brand-id": "canonical", "model": "pc", "gadget": "pc", "kernel": "kernel", "architecture": "amd64", "timestamp": time.Now().Format(time.RFC3339), }, nil, "") c.Assert(err, IsNil) err = assertstate.Add(s.state, model) c.Assert(err, IsNil) // have a serial assertion devKey, _ := assertstest.GenerateKey(752) encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey()) keyID := devKey.PublicKey().ID() c.Assert(err, IsNil) serial, err := s.storeSigning.Sign(asserts.SerialType, map[string]interface{}{ "brand-id": "canonical", "model": "pc", "serial": "8989", "device-key": string(encDevKey), "device-key-sha3-384": keyID, "timestamp": time.Now().Format(time.RFC3339), }, nil, "") c.Assert(err, IsNil) err = assertstate.Add(s.state, serial) c.Assert(err, IsNil) // forgotten key id and serial auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", }) // put key on disk err = s.mgr.KeypairManager().Put(devKey) c.Assert(err, IsNil) // extra unused stuff junk1 := filepath.Join(dirs.SnapDeviceDir, "private-keys-v1", "junkjunk1") err = ioutil.WriteFile(junk1, nil, 0644) c.Assert(err, IsNil) junk2 := filepath.Join(dirs.SnapDeviceDir, "private-keys-v1", "junkjunk2") err = ioutil.WriteFile(junk2, nil, 0644) c.Assert(err, IsNil) // double check pat := filepath.Join(dirs.SnapDeviceDir, "private-keys-v1", "*") onDisk, err := filepath.Glob(pat) c.Assert(err, IsNil) c.Check(onDisk, HasLen, 3) s.state.Unlock() err = s.mgr.EnsureSeedYaml() s.state.Lock() c.Assert(err, IsNil) c.Check(s.state.Changes(), HasLen, 0) var seeded bool err = s.state.Get("seeded", &seeded) c.Assert(err, IsNil) c.Check(seeded, Equals, true) device, err := auth.Device(s.state) c.Assert(err, IsNil) c.Check(device, DeepEquals, &auth.DeviceState{ Brand: "canonical", Model: "pc", KeyID: keyID, Serial: "8989", }) // key is still there _, err = s.mgr.KeypairManager().Get(keyID) c.Assert(err, IsNil) onDisk, err = filepath.Glob(pat) c.Assert(err, IsNil) // junk was removed c.Check(onDisk, HasLen, 1) }
func (s *deviceMgrSuite) TestFullDeviceRegistrationPollHappy(c *C) { r1 := devicestate.MockKeyLength(752) defer r1() mockServer := s.mockServer(c, "REQID-POLL") defer mockServer.Close() mockRequestIDURL := mockServer.URL + "/identity/api/v1/request-id" r2 := devicestate.MockRequestIDURL(mockRequestIDURL) defer r2() mockSerialRequestURL := mockServer.URL + "/identity/api/v1/devices" r3 := devicestate.MockSerialRequestURL(mockSerialRequestURL) defer r3() // immediately r4 := devicestate.MockRetryInterval(0) defer r4() // setup state as will be done by first-boot s.state.Lock() defer s.state.Unlock() s.setupGadget(c, ` name: gadget type: gadget version: gadget `, "") auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", }) // runs the whole device registration process with polling s.state.Unlock() s.settle() s.state.Lock() var becomeOperational *state.Change for _, chg := range s.state.Changes() { if chg.Kind() == "become-operational" { becomeOperational = chg break } } c.Assert(becomeOperational, NotNil) c.Check(becomeOperational.Status().Ready(), Equals, true) c.Check(becomeOperational.Err(), IsNil) device, err := auth.Device(s.state) c.Assert(err, IsNil) c.Check(device.Brand, Equals, "canonical") c.Check(device.Model, Equals, "pc") c.Check(device.Serial, Equals, "10002") a, err := s.db.Find(asserts.SerialType, map[string]string{ "brand-id": "canonical", "model": "pc", "serial": "10002", }) c.Assert(err, IsNil) serial := a.(*asserts.Serial) privKey, err := s.mgr.KeypairManager().Get(serial.DeviceKey().ID()) c.Assert(err, IsNil) c.Check(privKey, NotNil) c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) }
func (s *deviceMgrSuite) TestDeviceAssertionsModelAndSerial(c *C) { // nothing in the state s.state.Lock() _, err := devicestate.Model(s.state) s.state.Unlock() c.Check(err, Equals, state.ErrNoState) s.state.Lock() _, err = devicestate.Serial(s.state) s.state.Unlock() c.Check(err, Equals, state.ErrNoState) _, err = s.mgr.Model() c.Check(err, Equals, state.ErrNoState) _, err = s.mgr.Serial() c.Check(err, Equals, state.ErrNoState) // just brand and model s.state.Lock() auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", }) s.state.Unlock() _, err = s.mgr.Model() c.Check(err, Equals, state.ErrNoState) _, err = s.mgr.Serial() c.Check(err, Equals, state.ErrNoState) // have a model assertion model, err := s.storeSigning.Sign(asserts.ModelType, map[string]interface{}{ "series": "16", "brand-id": "canonical", "model": "pc", "gadget": "pc", "kernel": "kernel", "architecture": "amd64", "timestamp": time.Now().Format(time.RFC3339), }, nil, "") c.Assert(err, IsNil) s.state.Lock() err = assertstate.Add(s.state, model) s.state.Unlock() c.Assert(err, IsNil) mod, err := s.mgr.Model() c.Assert(err, IsNil) c.Assert(mod.BrandID(), Equals, "canonical") s.state.Lock() mod, err = devicestate.Model(s.state) s.state.Unlock() c.Assert(err, IsNil) c.Assert(mod.BrandID(), Equals, "canonical") _, err = s.mgr.Serial() c.Check(err, Equals, state.ErrNoState) // have a serial as well s.state.Lock() auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", Serial: "8989", }) s.state.Unlock() _, err = s.mgr.Model() c.Assert(err, IsNil) _, err = s.mgr.Serial() c.Check(err, Equals, state.ErrNoState) // have a serial assertion devKey, _ := assertstest.GenerateKey(752) encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey()) c.Assert(err, IsNil) serial, err := s.storeSigning.Sign(asserts.SerialType, map[string]interface{}{ "brand-id": "canonical", "model": "pc", "serial": "8989", "device-key": string(encDevKey), "device-key-sha3-384": devKey.PublicKey().ID(), "timestamp": time.Now().Format(time.RFC3339), }, nil, "") c.Assert(err, IsNil) s.state.Lock() err = assertstate.Add(s.state, serial) s.state.Unlock() c.Assert(err, IsNil) _, err = s.mgr.Model() c.Assert(err, IsNil) ser, err := s.mgr.Serial() c.Assert(err, IsNil) c.Check(ser.Serial(), Equals, "8989") s.state.Lock() ser, err = devicestate.Serial(s.state) s.state.Unlock() c.Assert(err, IsNil) c.Check(ser.Serial(), Equals, "8989") }
func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyPrepareDeviceHook(c *C) { r1 := devicestate.MockKeyLength(752) defer r1() mockServer := s.mockServer(c, "REQID-1") defer mockServer.Close() r2 := hookstate.MockRunHook(func(ctx *hookstate.Context, _ *tomb.Tomb) ([]byte, error) { c.Assert(ctx.HookName(), Equals, "prepare-device") // snapctl set the registration params _, _, err := ctlcmd.Run(ctx, []string{"set", fmt.Sprintf("device-service.url=%q", mockServer.URL+"/identity/api/v1/")}) c.Assert(err, IsNil) h, err := json.Marshal(map[string]string{ "x-extra-header": "extra", }) c.Assert(err, IsNil) _, _, err = ctlcmd.Run(ctx, []string{"set", fmt.Sprintf("device-service.headers=%s", string(h))}) c.Assert(err, IsNil) _, _, err = ctlcmd.Run(ctx, []string{"set", fmt.Sprintf("registration.proposed-serial=%q", "Y9999")}) c.Assert(err, IsNil) d, err := yaml.Marshal(map[string]string{ "mac": "00:00:00:00:ff:00", }) c.Assert(err, IsNil) _, _, err = ctlcmd.Run(ctx, []string{"set", fmt.Sprintf("registration.body=%q", d)}) c.Assert(err, IsNil) return nil, nil }) defer r2() // setup state as will be done by first-boot // & have a gadget with a prepare-device hook s.state.Lock() defer s.state.Unlock() s.setupGadget(c, ` name: gadget type: gadget version: gadget hooks: prepare-device: `, "") auth.SetDevice(s.state, &auth.DeviceState{ Brand: "canonical", Model: "pc", }) // runs the whole device registration process s.state.Unlock() s.settle() s.state.Lock() var becomeOperational *state.Change for _, chg := range s.state.Changes() { if chg.Kind() == "become-operational" { becomeOperational = chg break } } c.Assert(becomeOperational, NotNil) c.Check(becomeOperational.Status().Ready(), Equals, true) c.Check(becomeOperational.Err(), IsNil) device, err := auth.Device(s.state) c.Assert(err, IsNil) c.Check(device.Brand, Equals, "canonical") c.Check(device.Model, Equals, "pc") c.Check(device.Serial, Equals, "Y9999") a, err := s.db.Find(asserts.SerialType, map[string]string{ "brand-id": "canonical", "model": "pc", "serial": "Y9999", }) c.Assert(err, IsNil) serial := a.(*asserts.Serial) var details map[string]interface{} err = yaml.Unmarshal(serial.Body(), &details) c.Assert(err, IsNil) c.Check(details, DeepEquals, map[string]interface{}{ "mac": "00:00:00:00:ff:00", }) privKey, err := s.mgr.KeypairManager().Get(serial.DeviceKey().ID()) c.Assert(err, IsNil) c.Check(privKey, NotNil) c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) }