diff --git a/.last_go_version b/.last_go_version index 93f164194..e25441e59 100644 --- a/.last_go_version +++ b/.last_go_version @@ -1 +1 @@ -6g version weekly.2011-06-16 8838+ +6g version weekly.2011-06-16 8858+ diff --git a/config/dev-server-config.json b/config/dev-server-config.json index d0f5d639d..9545e5e1a 100644 --- a/config/dev-server-config.json +++ b/config/dev-server-config.json @@ -14,9 +14,9 @@ "handlerArgs": { "rootName": "dev-blog-root", "blobRoot": "/bs/", - "createPermanodeIfNeeded": true, "searchRoot": "/my-search/", - "cache": "/cache/" + "cache": "/cache/", + "devBootstrapPermanodeUsing": "/sighelper/" } }, @@ -25,9 +25,9 @@ "handlerArgs": { "rootName": "dev-pics-root", "blobRoot": "/bs/", - "createPermanodeIfNeeded": true, "searchRoot": "/my-search/", - "cache": "/cache/" + "cache": "/cache/", + "devBootstrapPermanodeUsing": "/sighelper/" } }, diff --git a/lib/go/camli/mysqlindexer/receive.go b/lib/go/camli/mysqlindexer/receive.go index 9c933a8de..7c50d8ae7 100644 --- a/lib/go/camli/mysqlindexer/receive.go +++ b/lib/go/camli/mysqlindexer/receive.go @@ -216,13 +216,14 @@ func (mi *Indexer) populateClaim(client *mysql.Client, blobRef *blobref.BlobRef, } if verifiedKeyId != "" { - // TODO: limit this to only certain attributes (for now, just "camliRoot") once search handler - // is working and the UI permits setting camliRoot. - if err = execSQL(client, "INSERT IGNORE INTO signerattrvalue (keyid, attr, value, claimdate, blobref, permanode) "+ - "VALUES (?, ?, ?, ?, ?, ?)", - verifiedKeyId, camli.Attribute, camli.Value, - camli.ClaimDate, blobRef.String(), camli.Permanode); err != nil { - return + switch camli.Attribute { + case "camliRoot": + if err = execSQL(client, "INSERT IGNORE INTO signerattrvalue (keyid, attr, value, claimdate, blobref, permanode) "+ + "VALUES (?, ?, ?, ?, ?, ?)", + verifiedKeyId, camli.Attribute, camli.Value, + camli.ClaimDate, blobRef.String(), camli.Permanode); err != nil { + return + } } } diff --git a/lib/go/camli/search/handler.go b/lib/go/camli/search/handler.go index e0498693e..6d2a68591 100644 --- a/lib/go/camli/search/handler.go +++ b/lib/go/camli/search/handler.go @@ -67,6 +67,15 @@ func newHandlerFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (http.Handl }, nil } +// TODO: figure out a plan for an owner having multiple active public keys, or public +// key rotation +func (h *Handler) Owner() *blobref.BlobRef { + return h.owner +} + +func (h *Handler) Index() Index { + return h.index +} func jsonMap() map[string]interface{} { return make(map[string]interface{}) diff --git a/server/go/camlistored/publish.go b/server/go/camlistored/publish.go index 8464c251d..2baf3fa29 100644 --- a/server/go/camlistored/publish.go +++ b/server/go/camlistored/publish.go @@ -20,10 +20,13 @@ import ( "fmt" "html" "http" + "log" "os" "camli/blobserver" + "camli/client" // just for NewUploadHandleFromString. move elsewhere? "camli/jsonconfig" + "camli/schema" "camli/search" ) @@ -46,7 +49,7 @@ func newPublishFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Han blobRoot := conf.RequiredString("blobRoot") searchRoot := conf.RequiredString("searchRoot") cachePrefix := conf.OptionalString("cache", "") - createPermanode := conf.OptionalBool("createPermanodeIfNeeded", false) + bootstrapSignRoot := conf.OptionalString("devBootstrapPermanodeUsing", "") if err = conf.Validate(); err != nil { return } @@ -65,10 +68,22 @@ func newPublishFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Han if err != nil { return nil, fmt.Errorf("publish handler's searchRoot of %q error: %v", searchRoot, err) } - pub.Search = si.(*search.Handler) // TODO: don't crash here if wrong type; return error + var ok bool + pub.Search, ok = si.(*search.Handler) + if !ok { + return nil, fmt.Errorf("publish handler's searchRoot of %q is of type %T, expecting a search handler", + searchRoot, si) + } - if createPermanode { - // ... TODO + if bootstrapSignRoot != "" { + if t := ld.GetHandlerType(bootstrapSignRoot); t != "jsonsign" { + return nil, fmt.Errorf("publish handler's devBootstrapPermanodeUsing must be of type jsonsign") + } + h, _ := ld.GetHandler(bootstrapSignRoot) + jsonSign := h.(*JSONSignHandler) + if err := pub.bootstrapPermanode(jsonSign); err != nil { + return nil, fmt.Errorf("error bootstrapping permanode: %v", err) + } } if cachePrefix != "" { @@ -86,6 +101,45 @@ func (pub *PublishHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) base := req.Header.Get("X-PrefixHandler-PathBase") suffix := req.Header.Get("X-PrefixHandler-PathSuffix") - fmt.Fprintf(rw, "I am publish handler at base %q, serving root %q, suffix %q", - base, pub.RootName, html.EscapeString(suffix)) + pn, err := pub.Search.Index().PermanodeOfSignerAttrValue(pub.Search.Owner(), "camliRoot", pub.RootName) + if err != nil { + rw.WriteHeader(404) + fmt.Fprintf(rw, "Error: publish handler at base %q, serving root name %q has no configured permanode", + base, pub.RootName) + return + } + + fmt.Fprintf(rw, "I am publish handler at base %q, serving root %q (permanode=%s), suffix %q", + base, pub.RootName, pn, html.EscapeString(suffix)) +} + +func (pub *PublishHandler) bootstrapPermanode(jsonSign *JSONSignHandler) os.Error { + if pn, err := pub.Search.Index().PermanodeOfSignerAttrValue(pub.Search.Owner(), "camliRoot", pub.RootName); err == nil { + log.Printf("Publish root %q using existing permanode %s", pub.RootName, pn) + return nil + } + log.Printf("Publish root %q needs a permanode + claim", pub.RootName) + + // Step 1: create a permanode + pn, err := jsonSign.SignMap(schema.NewUnsignedPermanode()) + if err != nil { + return fmt.Errorf("error creating new permanode: %v", err) + } + ph := client.NewUploadHandleFromString(pn) + _, err = pub.Storage.ReceiveBlob(ph.BlobRef, ph.Contents) + if err != nil { + return fmt.Errorf("error uploading permanode: %v", err) + } + + // Step 2: addd a claim that the new permanode is the desired root. + claim, err := jsonSign.SignMap(schema.NewSetAttributeClaim(ph.BlobRef, "camliRoot", pub.RootName)) + if err != nil { + return fmt.Errorf("error creating claim: %v", err) + } + ch := client.NewUploadHandleFromString(claim) + _, err = pub.Storage.ReceiveBlob(ch.BlobRef, ch.Contents) + if err != nil { + return fmt.Errorf("error uploading claim: %v", err) + } + return nil } diff --git a/server/go/camlistored/sig.go b/server/go/camlistored/sig.go index 54ea3f9ba..d039a4656 100644 --- a/server/go/camlistored/sig.go +++ b/server/go/camlistored/sig.go @@ -32,6 +32,7 @@ import ( "camli/httputil" "camli/jsonconfig" "camli/jsonsign" + "camli/schema" ) var _ = log.Printf @@ -229,3 +230,18 @@ func (h *JSONSignHandler) handleSign(rw http.ResponseWriter, req *http.Request) } rw.Write([]byte(signedJson)) } + +func (h *JSONSignHandler) SignMap(m map[string]interface{}) (string, os.Error) { + m["camliSigner"] = h.pubKeyBlobRef.String() + unsigned, err := schema.MapToCamliJson(m) + if err != nil { + return "", err + } + sreq := &jsonsign.SignRequest{ + UnsignedJson: unsigned, + Fetcher: h.pubKeyFetcher, + ServerMode: true, + SecretKeyringPath: h.secretRing, + } + return sreq.Sign() +}