publish work: optionally create publish root permanodes+claims on start

Change-Id: I8659e977cea8872443ff2b311a7d0d8a2804c269
This commit is contained in:
Brad Fitzpatrick 2011-06-23 12:11:01 -07:00
parent 494d57cb4a
commit 23ff0a071f
6 changed files with 98 additions and 18 deletions

View File

@ -1 +1 @@
6g version weekly.2011-06-16 8838+
6g version weekly.2011-06-16 8858+

View File

@ -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/"
}
},

View File

@ -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
}
}
}

View File

@ -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{})

View File

@ -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
}

View File

@ -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()
}