func assertsFindMany(c *Command, r *http.Request, user *auth.UserState) Response { assertTypeName := muxVars(r)["assertType"] assertType := asserts.Type(assertTypeName) if assertType == nil { return BadRequest("invalid assert type: %q", assertTypeName) } headers := map[string]string{} q := r.URL.Query() for k := range q { headers[k] = q.Get(k) } state := c.d.overlord.State() state.Lock() db := assertstate.DB(state) state.Unlock() assertions, err := db.FindMany(assertType, headers) if err == asserts.ErrNotFound { return AssertResponse(nil, true) } else if err != nil { return InternalError("searching assertions failed: %v", err) } return AssertResponse(assertions, true) }
func downloadAssertion(typeName string, headers map[string]string) ([]asserts.Assertion, error) { var user *auth.UserState // FIXME: set auth context var authContext auth.AuthContext at := asserts.Type(typeName) if at == nil { return nil, fmt.Errorf("cannot find assertion type %q", typeName) } primaryKeys := make([]string, len(at.PrimaryKey)) for i, k := range at.PrimaryKey { pk, ok := headers[k] if !ok { return nil, fmt.Errorf("missing primary header %q to query remote assertion", k) } primaryKeys[i] = pk } sto := storeNew(nil, authContext) as, err := sto.Assertion(at, primaryKeys, user) if err != nil { return nil, err } return []asserts.Assertion{as}, nil }
// Sign produces the text of a signed assertion as specified by opts. func Sign(opts *Options, keypairMgr asserts.KeypairManager) ([]byte, error) { var headers map[string]interface{} err := json.Unmarshal(opts.Statement, &headers) if err != nil { return nil, fmt.Errorf("cannot parse the assertion input as JSON: %v", err) } typCand, ok := headers["type"] if !ok { return nil, fmt.Errorf("missing assertion type header") } typStr, ok := typCand.(string) if !ok { return nil, fmt.Errorf("assertion type must be a string, not: %v", typCand) } typ := asserts.Type(typStr) if typ == nil { return nil, fmt.Errorf("invalid assertion type: %v", headers["type"]) } var body []byte if bodyCand, ok := headers["body"]; ok { bodyStr, ok := bodyCand.(string) if !ok { return nil, fmt.Errorf("body if specified must be a string") } body = []byte(bodyStr) delete(headers, "body") } adb, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ KeypairManager: keypairMgr, }) if err != nil { return nil, err } // TODO: teach Sign to cross check keyID and authority-id // against an account-key a, err := adb.Sign(typ, headers, body, opts.KeyID) if err != nil { return nil, err } return asserts.Encode(a), nil }
func (s *Store) assertionsEndpoint(w http.ResponseWriter, req *http.Request) { assertPath := strings.TrimPrefix(req.URL.Path, "/assertions/") bs, err := s.collectAssertions() if err != nil { http.Error(w, fmt.Sprintf("internal error collecting assertions: %v", err), http.StatusInternalServerError) return } comps := strings.Split(assertPath, "/") if len(comps) == 0 { http.Error(w, "missing assertion type", http.StatusBadRequest) return } typ := asserts.Type(comps[0]) if typ == nil { http.Error(w, fmt.Sprintf("unknown assertion type: %s", comps[0]), http.StatusBadRequest) return } if len(typ.PrimaryKey) != len(comps)-1 { http.Error(w, fmt.Sprintf("wrong primary key length: %v", comps), http.StatusBadRequest) return } a, err := s.retrieveAssertion(bs, typ, comps[1:]) if isAssertNotFound(err) { w.Header().Set("Content-Type", "application/problem+json") w.WriteHeader(404) w.Write([]byte(`{"status": 404}`)) return } if err != nil { http.Error(w, fmt.Sprintf("cannot retrieve assertion %v: %v", comps, err), http.StatusBadRequest) return } w.Header().Set("Content-Type", asserts.MediaType) w.WriteHeader(http.StatusOK) w.Write(asserts.Encode(a)) }
func (as *assertsSuite) TestWithAuthority(c *C) { withAuthority := []string{ "account", "account-key", "snap-declaration", "snap-build", "snap-revision", "model", "serial", "system-user", "validation", } c.Check(withAuthority, HasLen, asserts.NumAssertionType-4) // excluding device-session-request, serial-request, serial-proof, account-key-request for _, name := range withAuthority { typ := asserts.Type(name) _, err := asserts.AssembleAndSignInTest(typ, nil, nil, testPrivKey1) c.Check(err, ErrorMatches, `"authority-id" header is mandatory`) } }
func (ms *mgrsSuite) mockStore(c *C) *httptest.Server { var baseURL string fillHit := func() string { snapf, err := snap.Open(ms.serveSnapPath) if err != nil { panic(err) } info, err := snap.ReadInfoFromSnapFile(snapf, nil) if err != nil { panic(err) } hit := strings.Replace(fooSearchHit, "@URL@", baseURL+"/snap", -1) hit = strings.Replace(hit, "@ICON@", baseURL+"/icon", -1) hit = strings.Replace(hit, "@VERSION@", info.Version, -1) hit = strings.Replace(hit, "@REVISION@", ms.serveRevision, -1) return hit } mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/assertions/") { comps := strings.Split(r.URL.Path, "/") ref := &asserts.Ref{ Type: asserts.Type(comps[2]), PrimaryKey: comps[3:], } a, err := ref.Resolve(ms.storeSigning.Find) if err == asserts.ErrNotFound { w.Header().Set("Content-Type", "application/json") w.WriteHeader(404) w.Write([]byte(`{"status": 404}`)) return } if err != nil { panic(err) } w.Header().Set("Content-Type", asserts.MediaType) w.WriteHeader(200) w.Write(asserts.Encode(a)) return } switch r.URL.Path { case "/details/foo": w.WriteHeader(http.StatusOK) io.WriteString(w, fillHit()) case "/metadata": w.WriteHeader(http.StatusOK) output := `{ "_embedded": { "clickindex:package": [@HIT@] } }` output = strings.Replace(output, "@HIT@", fillHit(), 1) io.WriteString(w, output) case "/snap": snapR, err := os.Open(ms.serveSnapPath) if err != nil { panic(err) } io.Copy(w, snapR) default: panic("unexpected url path: " + r.URL.Path) } })) c.Assert(mockServer, NotNil) baseURL = mockServer.URL detailsURL, err := url.Parse(baseURL + "/details/") c.Assert(err, IsNil) bulkURL, err := url.Parse(baseURL + "/metadata") c.Assert(err, IsNil) assertionsURL, err := url.Parse(baseURL + "/assertions/") c.Assert(err, IsNil) storeCfg := store.Config{ DetailsURI: detailsURL, BulkURI: bulkURL, AssertionsURI: assertionsURL, } mStore := store.New(&storeCfg, nil) st := ms.o.State() st.Lock() snapstate.ReplaceStore(ms.o.State(), mStore) st.Unlock() return mockServer }
func (as *assertsSuite) TestTypeMaxSupportedFormat(c *C) { c.Check(asserts.Type("test-only").MaxSupportedFormat(), Equals, 1) }
func (as *assertsSuite) TestUnknown(c *C) { c.Check(asserts.Type(""), IsNil) c.Check(asserts.Type("unknown"), IsNil) }
func (as *assertsSuite) TestType(c *C) { c.Check(asserts.Type("test-only"), Equals, asserts.TestOnlyType) }
func (ms *mgrsSuite) mockStore(c *C) *httptest.Server { var baseURL string fillHit := func(name string) string { snapf, err := snap.Open(ms.serveSnapPath[name]) if err != nil { panic(err) } info, err := snap.ReadInfoFromSnapFile(snapf, nil) if err != nil { panic(err) } hit := strings.Replace(searchHit, "@URL@", baseURL+"/snap/"+name, -1) hit = strings.Replace(hit, "@NAME@", name, -1) hit = strings.Replace(hit, "@SNAPID@", fakeSnapID(name), -1) hit = strings.Replace(hit, "@ICON@", baseURL+"/icon", -1) hit = strings.Replace(hit, "@VERSION@", info.Version, -1) hit = strings.Replace(hit, "@REVISION@", ms.serveRevision[name], -1) return hit } mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { comps := strings.Split(r.URL.Path, "/") if len(comps) == 0 { panic("unexpected url path: " + r.URL.Path) } switch comps[1] { case "assertions": ref := &asserts.Ref{ Type: asserts.Type(comps[2]), PrimaryKey: comps[3:], } a, err := ref.Resolve(ms.storeSigning.Find) if err == asserts.ErrNotFound { w.Header().Set("Content-Type", "application/json") w.WriteHeader(404) w.Write([]byte(`{"status": 404}`)) return } if err != nil { panic(err) } w.Header().Set("Content-Type", asserts.MediaType) w.WriteHeader(200) w.Write(asserts.Encode(a)) return case "details": w.WriteHeader(http.StatusOK) io.WriteString(w, fillHit(comps[2])) case "metadata": dec := json.NewDecoder(r.Body) var input struct { Snaps []struct { SnapID string `json:"snap_id"` Revision int `json:"revision"` } `json:"snaps"` } err := dec.Decode(&input) if err != nil { panic(err) } var hits []json.RawMessage for _, s := range input.Snaps { name := ms.serveIDtoName[s.SnapID] if snap.R(s.Revision) == snap.R(ms.serveRevision[name]) { continue } hits = append(hits, json.RawMessage(fillHit(name))) } w.WriteHeader(http.StatusOK) output, err := json.Marshal(map[string]interface{}{ "_embedded": map[string]interface{}{ "clickindex:package": hits, }, }) if err != nil { panic(err) } w.Write(output) case "snap": snapR, err := os.Open(ms.serveSnapPath[comps[2]]) if err != nil { panic(err) } io.Copy(w, snapR) default: panic("unexpected url path: " + r.URL.Path) } })) c.Assert(mockServer, NotNil) baseURL = mockServer.URL detailsURL, err := url.Parse(baseURL + "/details/") c.Assert(err, IsNil) bulkURL, err := url.Parse(baseURL + "/metadata") c.Assert(err, IsNil) assertionsURL, err := url.Parse(baseURL + "/assertions/") c.Assert(err, IsNil) storeCfg := store.Config{ DetailsURI: detailsURL, BulkURI: bulkURL, AssertionsURI: assertionsURL, } mStore := store.New(&storeCfg, nil) st := ms.o.State() st.Lock() snapstate.ReplaceStore(ms.o.State(), mStore) st.Unlock() return mockServer }