diff --git a/pkg/blobserver/handlers/upload.go b/pkg/blobserver/handlers/upload.go index d5c7dd55b..ee625c6e4 100644 --- a/pkg/blobserver/handlers/upload.go +++ b/pkg/blobserver/handlers/upload.go @@ -109,33 +109,39 @@ func vivify(blobReceiver blobserver.BlobReceiveConfiger, fileblob blobref.SizedB return fmt.Errorf("Discovery: json decoding error: %v", err) } - unsigned := schema.NewHashPlannedPermanode(h) - unsigned.SetSigner(blobref.MustParse(publicKeyBlobRef)) - signed, err := sigHelper.Sign(unsigned) - if err != nil { - return fmt.Errorf("Signing permanode %v: %v", signed, err) - } - signedPerm := blobref.SHA1FromString(signed) - _, err = blobReceiver.ReceiveBlob(signedPerm, strings.NewReader(signed)) - if err != nil { - return fmt.Errorf("While uploading signed permanode %v: %v", signed, err) - } - - contentAttr := schema.NewSetAttributeClaim(signedPerm, "camliContent", fileblob.BlobRef.String()) + // The file schema must have a modtime to vivify, as the modtime is used for all three of: + // 1) the permanode's signature + // 2) the camliContent attribute claim's "claimDate" + // 3) the signature time of 2) claimDate, err := time.Parse(time.RFC3339, fr.FileSchema().UnixMtime) if err != nil { return fmt.Errorf("While parsing modtime for file %v: %v", fr.FileSchema().FileName, err) } - contentAttr.SetClaimDate(claimDate) - contentAttr.SetSigner(blobref.MustParse(publicKeyBlobRef)) - signed, err = sigHelper.Sign(contentAttr) + + permanodeBB := schema.NewHashPlannedPermanode(h) + permanodeBB.SetSigner(blobref.MustParse(publicKeyBlobRef)) + permanodeBB.SetClaimDate(claimDate) + permanodeSigned, err := sigHelper.Sign(permanodeBB) + if err != nil { + return fmt.Errorf("Signing permanode %v: %v", permanodeSigned, err) + } + permanodeRef := blobref.SHA1FromString(permanodeSigned) + _, err = blobReceiver.ReceiveBlob(permanodeRef, strings.NewReader(permanodeSigned)) + if err != nil { + return fmt.Errorf("While uploading signed permanode %v, %v: %v", permanodeRef, permanodeSigned, err) + } + + contentClaimBB := schema.NewSetAttributeClaim(permanodeRef, "camliContent", fileblob.BlobRef.String()) + contentClaimBB.SetSigner(blobref.MustParse(publicKeyBlobRef)) + contentClaimBB.SetClaimDate(claimDate) + contentClaimSigned, err := sigHelper.Sign(contentClaimBB) if err != nil { return fmt.Errorf("Signing camliContent claim: %v", err) } - signedClaim := blobref.SHA1FromString(signed) - _, err = blobReceiver.ReceiveBlob(signedClaim, strings.NewReader(signed)) + contentClaimRef := blobref.SHA1FromString(contentClaimSigned) + _, err = blobReceiver.ReceiveBlob(contentClaimRef, strings.NewReader(contentClaimSigned)) if err != nil { - return fmt.Errorf("While uploading signed camliContent claim %v: %v", signed, err) + return fmt.Errorf("While uploading signed camliContent claim %v, %v: %v", contentClaimRef, contentClaimSigned, err) } return nil } diff --git a/pkg/schema/blob.go b/pkg/schema/blob.go index 71d4c5d29..d664da902 100644 --- a/pkg/schema/blob.go +++ b/pkg/schema/blob.go @@ -260,15 +260,25 @@ func (bb *Builder) SetSymlinkTarget(target string) *Builder { return bb } +// IsClaimType returns whether this blob builder is for a type +// which should be signed. (a "claim" or "permanode") +func (bb *Builder) IsClaimType() bool { + switch bb.Type() { + case "claim", "permanode": + return true + } + return false +} + // SetClaimDate sets the "claimDate" on a claim. // It is a fatal error to call SetClaimDate if the Map isn't of Type "claim". func (bb *Builder) SetClaimDate(t time.Time) *Builder { - if t := bb.Type(); t != "claim" { + if !bb.IsClaimType() { // This is a little gross, using panic here, but I // don't want all callers to check errors. This is // really a programming error, not a runtime error // that would arise from e.g. random user data. - panic("SetClaimDate called on non-claim *Builder; camliType=" + t) + panic("SetClaimDate called on non-claim *Builder; camliType=" + bb.Type()) } bb.m["claimDate"] = RFC3339FromTime(t) return bb