func (h *Handler) handleVerify(rw http.ResponseWriter, req *http.Request) { req.ParseForm() sjson := req.FormValue("sjson") if sjson == "" { http.Error(rw, "missing \"sjson\" parameter", http.StatusBadRequest) return } // TODO: use a different fetcher here that checks memory, disk, // the internet, etc. fetcher := h.pubKeyFetcher var res camtypes.VerifyResponse vreq := jsonsign.NewVerificationRequest(sjson, fetcher) if vreq.Verify() { res.SignatureValid = true res.SignerKeyId = vreq.SignerKeyId res.VerifiedData = vreq.PayloadMap } else { res.SignatureValid = false res.ErrorMessage = vreq.Err.Error() } rw.WriteHeader(http.StatusOK) // no HTTP response code fun, error info in JSON httputil.ReturnJSON(rw, &res) }
func (a *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == a.masterqueryURLPath { a.handleMasterQuery(w, r) return } if a.configURLPath != "" && r.URL.Path == a.configURLPath { if a.auth.AllowedAccess(r)&auth.OpGet == auth.OpGet { camhttputil.ReturnJSON(w, a.appConfig) } else { auth.SendUnauthorized(w, r) } return } trimmedPath := strings.TrimPrefix(r.URL.Path, a.prefix) if strings.HasPrefix(trimmedPath, "/search") { a.handleSearch(w, r) return } if a.proxy == nil { http.Error(w, "no proxy for the app", 500) return } a.proxy.ServeHTTP(w, r) }
func (sh *Handler) serveFiles(rw http.ResponseWriter, req *http.Request) { var ret camtypes.FileSearchResponse defer httputil.ReturnJSON(rw, &ret) br, ok := blob.Parse(req.FormValue("wholedigest")) if !ok { ret.Error = "Missing or invalid 'wholedigest' param" ret.ErrorType = "input" return } files, err := sh.index.ExistingFileSchemas(br) if err != nil { ret.Error = err.Error() ret.ErrorType = "server" return } // the ui code expects an object if files == nil { files = []blob.Ref{} } ret.Files = files return }
func (h *Handler) handleVerify(rw http.ResponseWriter, req *http.Request) { req.ParseForm() sjson := req.FormValue("sjson") if sjson == "" { http.Error(rw, "missing \"sjson\" parameter", http.StatusBadRequest) return } m := make(map[string]interface{}) // TODO: use a different fetcher here that checks memory, disk, // the internet, etc. fetcher := h.pubKeyFetcher vreq := jsonsign.NewVerificationRequest(sjson, fetcher) if vreq.Verify() { m["signatureValid"] = 1 m["signerKeyId"] = vreq.SignerKeyId m["verifiedData"] = vreq.PayloadMap } else { errStr := vreq.Err.Error() m["signatureValid"] = 0 m["errorMessage"] = errStr } rw.WriteHeader(http.StatusOK) // no HTTP response code fun, error info in JSON httputil.ReturnJSON(rw, m) }
func handleVerify(conn http.ResponseWriter, req *http.Request) { if !(req.Method == "POST" && req.URL.Path == "/camli/sig/verify") { httputil.BadRequestError(conn, "Inconfigured handler.") return } req.ParseForm() sjson := req.FormValue("sjson") if sjson == "" { httputil.BadRequestError(conn, "Missing sjson parameter.") return } m := make(map[string]interface{}) vreq := jsonsign.NewVerificationRequest(sjson, pubKeyFetcher) if vreq.Verify() { m["signatureValid"] = 1 m["verifiedData"] = vreq.PayloadMap } else { m["signatureValid"] = 0 m["errorMessage"] = vreq.Err.Error() } conn.WriteHeader(http.StatusOK) // no HTTP response code fun, error info in JSON httputil.ReturnJSON(conn, m) }
func (sh *StatusHandler) serveStatus(rw http.ResponseWriter, req *http.Request) { res := &statusResponse{ Version: buildinfo.Version(), } httputil.ReturnJSON(rw, res) }
func (sh *Handler) serveSignerAttrValue(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) signer := httputil.MustGetBlobRef(req, "signer") attr := httputil.MustGet(req, "attr") value := httputil.MustGet(req, "value") pn, err := sh.index.PermanodeOfSignerAttrValue(signer, attr, value) if err != nil { httputil.ServeJSONError(rw, err) return } dr := sh.NewDescribeRequest() dr.Describe(pn, 2) metaMap, err := dr.metaMap() if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, &SignerAttrValueResponse{ Permanode: pn, Meta: metaMap, }) }
func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { base := req.Header.Get("X-PrefixHandler-PathBase") subPath := req.Header.Get("X-PrefixHandler-PathSuffix") switch req.Method { case "GET": switch subPath { case "": http.Redirect(rw, req, base+"camli/sig/discovery", http.StatusFound) return case h.pubKeyBlobRefServeSuffix: h.pubKeyHandler.ServeHTTP(rw, req) return case "camli/sig/sign": fallthrough case "camli/sig/verify": http.Error(rw, "POST required", 400) return case "camli/sig/discovery": httputil.ReturnJSON(rw, h.DiscoveryMap(base)) return } case "POST": switch subPath { case "camli/sig/sign": h.handleSign(rw, req) return case "camli/sig/verify": h.handleVerify(rw, req) return } } http.Error(rw, "Unsupported path or method.", http.StatusBadRequest) }
func (sh *Handler) serveFiles(rw http.ResponseWriter, req *http.Request) { ret := jsonMap() defer httputil.ReturnJSON(rw, ret) br, ok := blob.Parse(req.FormValue("wholedigest")) if !ok { ret["error"] = "Missing or invalid 'wholedigest' param" ret["errorType"] = "input" return } files, err := sh.index.ExistingFileSchemas(br) if err != nil { ret["error"] = err.Error() ret["errorType"] = "server" return } strList := []string{} for _, br := range files { strList = append(strList, br.String()) } ret["files"] = strList return }
func (sh *Handler) serveSignerPaths(rw http.ResponseWriter, req *http.Request) { ret := jsonMap() defer httputil.ReturnJSON(rw, ret) defer setPanicError(ret) signer := blobref.MustParse(mustGet(req, "signer")) target := blobref.MustParse(mustGet(req, "target")) paths, err := sh.index.PathsOfSignerTarget(signer, target) if err != nil { ret["error"] = err.Error() } else { jpaths := []map[string]interface{}{} for _, path := range paths { jpaths = append(jpaths, map[string]interface{}{ "claimRef": path.Claim.String(), "baseRef": path.Base.String(), "suffix": path.Suffix, }) } ret["paths"] = jpaths dr := sh.NewDescribeRequest() for _, path := range paths { dr.Describe(path.Base, 2) } dr.PopulateJSON(ret) } }
func (fth *FileTreeHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if req.Method != "GET" && req.Method != "HEAD" { http.Error(rw, "Invalid method", 400) return } ret := make(map[string]interface{}) defer httputil.ReturnJSON(rw, ret) de, err := schema.NewDirectoryEntryFromBlobRef(fth.storageSeekFetcher(), fth.file) dir, err := de.Directory() if err != nil { http.Error(rw, "Error reading directory", 500) log.Printf("Error reading directory from blobref %s: %v\n", fth.file, err) return } entries, err := dir.Readdir(-1) if err != nil { http.Error(rw, "Error reading directory", 500) log.Printf("reading dir from blobref %s: %v\n", fth.file, err) return } children := make([]map[string]interface{}, 0) for _, v := range entries { child := map[string]interface{}{ "name": v.FileName(), "type": v.CamliType(), "blobRef": v.BlobRef(), } children = append(children, child) } ret["children"] = children }
func (sh *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { suffix := httputil.PathSuffix(req) handlers := getHandler switch { case httputil.IsGet(req): // use default from above case req.Method == "POST": handlers = postHandler default: handlers = nil } fn := handlers[strings.TrimPrefix(suffix, "camli/search/")] if fn != nil { fn(sh, rw, req) return } // TODO: discovery for the endpoints & better error message with link to discovery info ret := camtypes.SearchErrorResponse{ Error: "Unsupported search path or method", ErrorType: "input", } httputil.ReturnJSON(rw, &ret) }
func handleRemove(conn http.ResponseWriter, req *http.Request, storage blobserver.Storage) { if w, ok := storage.(blobserver.ContextWrapper); ok { storage = w.WrapContext(req) } if req.Method != "POST" { log.Fatalf("Invalid method; handlers misconfigured") } configer, ok := storage.(blobserver.Configer) if !ok { conn.WriteHeader(http.StatusForbidden) fmt.Fprintf(conn, "Remove handler's blobserver.Storage isn't a blobserver.Configer; can't remove") return } if !configer.Config().IsQueue { conn.WriteHeader(http.StatusForbidden) fmt.Fprintf(conn, "Can only remove blobs from a queue.\n") return } n := 0 toRemove := make([]*blobref.BlobRef, 0) toRemoveStr := make([]string, 0) for { n++ if n > maxRemovesPerRequest { httputil.BadRequestError(conn, fmt.Sprintf("Too many removes in this request; max is %d", maxRemovesPerRequest)) return } key := fmt.Sprintf("blob%v", n) value := req.FormValue(key) if value == "" { break } ref := blobref.Parse(value) if ref == nil { httputil.BadRequestError(conn, "Bogus blobref for key "+key) return } toRemove = append(toRemove, ref) toRemoveStr = append(toRemoveStr, ref.String()) } err := storage.RemoveBlobs(toRemove) if err != nil { conn.WriteHeader(http.StatusInternalServerError) log.Printf("Server error during remove: %v", err) fmt.Fprintf(conn, "Server error") return } reply := make(map[string]interface{}, 0) reply["removed"] = toRemoveStr httputil.ReturnJSON(conn, reply) }
func (sh *Handler) serveRecentPermanodes(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var rr RecentRequest rr.fromHTTP(req) res, err := sh.GetRecentPermanodes(&rr) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, res) }
// servePermanodesWithAttr uses the indexer to search for the permanodes matching // the request. // The valid values for the "attr" key in the request (i.e the only attributes // for a permanode which are actually indexed as such) are "tag" and "title". func (sh *Handler) servePermanodesWithAttr(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var wr WithAttrRequest wr.fromHTTP(req) res, err := sh.GetPermanodesWithAttr(&wr) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, res) }
func (sh *Handler) serveClaims(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var cr ClaimsRequest cr.fromHTTP(req) res, err := sh.GetClaims(&cr) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, res) }
// Unlike the index interface's EdgesTo method, the "edgesto" Handler // here additionally filters out since-deleted permanode edges. func (sh *Handler) serveEdgesTo(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var er EdgesRequest er.fromHTTP(req) res, err := sh.EdgesTo(&er) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, res) }
func (sh *Handler) serveDescribe(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var dr DescribeRequest dr.fromHTTP(req) res, err := sh.Describe(&dr) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, res) }
func (sh *Handler) serveSignerPaths(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var sr SignerPathsRequest sr.fromHTTP(req) res, err := sh.GetSignerPaths(&sr) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, res) }
func (sh *Handler) serveDescribe(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) br := httputil.MustGetBlobRef(req, "blobref") dr := sh.NewDescribeRequest() dr.Describe(br, 4) metaMap, err := dr.metaMapThumbs(thumbnailSize(req)) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, &DescribeResponse{metaMap}) }
func (ui *UIHandler) serveUploadHelper(rw http.ResponseWriter, req *http.Request) { if ui.root.Storage == nil { httputil.ServeJSONError(rw, httputil.ServerError("No BlobRoot configured")) return } mr, err := req.MultipartReader() if err != nil { httputil.ServeJSONError(rw, httputil.ServerError("reading body: "+err.Error())) return } var got []*uploadHelperGotItem var modTime types.Time3339 for { part, err := mr.NextPart() if err == io.EOF { break } if err != nil { httputil.ServeJSONError(rw, httputil.ServerError("reading body: "+err.Error())) break } if part.FormName() == "modtime" { payload, err := ioutil.ReadAll(part) if err != nil { log.Printf("ui uploadhelper: unable to read part for modtime: %v", err) continue } modTime = types.ParseTime3339OrZero(string(payload)) continue } fileName := part.FileName() if fileName == "" { continue } br, err := schema.WriteFileFromReaderWithModTime(ui.root.Storage, fileName, modTime.Time(), part) if err != nil { httputil.ServeJSONError(rw, httputil.ServerError("writing to blobserver: "+err.Error())) return } got = append(got, &uploadHelperGotItem{ FileName: part.FileName(), ModTime: modTime, FormName: part.FormName(), FileRef: br, }) } httputil.ReturnJSON(rw, &uploadHelperResponse{Got: got}) }
func handleRemove(rw http.ResponseWriter, req *http.Request, storage blobserver.Storage) { if req.Method != "POST" { log.Fatalf("Invalid method; handlers misconfigured") } configer, ok := storage.(blobserver.Configer) if !ok { rw.WriteHeader(http.StatusForbidden) fmt.Fprintf(rw, "Remove handler's blobserver.Storage isn't a blobserver.Configer; can't remove") return } if !configer.Config().Deletable { rw.WriteHeader(http.StatusForbidden) fmt.Fprintf(rw, "storage does not permit deletes.\n") return } n := 0 toRemove := make([]blob.Ref, 0) for { n++ if n > maxRemovesPerRequest { httputil.BadRequestError(rw, fmt.Sprintf("Too many removes in this request; max is %d", maxRemovesPerRequest)) return } key := fmt.Sprintf("blob%v", n) value := req.FormValue(key) if value == "" { break } ref, ok := blob.Parse(value) if !ok { httputil.BadRequestError(rw, "Bogus blobref for key "+key) return } toRemove = append(toRemove, ref) } err := storage.RemoveBlobs(toRemove) if err != nil { rw.WriteHeader(http.StatusInternalServerError) log.Printf("Server error during remove: %v", err) fmt.Fprintf(rw, "Server error") return } httputil.ReturnJSON(rw, &RemoveResponse{Removed: toRemove}) }
func (a *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if camhttputil.PathSuffix(req) == "config.json" { if a.auth.AllowedAccess(req)&auth.OpGet == auth.OpGet { camhttputil.ReturnJSON(rw, a.appConfig) } else { auth.SendUnauthorized(rw, req) } return } if a.proxy == nil { http.Error(rw, "no proxy for the app", 500) return } a.proxy.ServeHTTP(rw, req) }
func (ui *UIHandler) serveUploadHelper(rw http.ResponseWriter, req *http.Request) { ret := make(map[string]interface{}) defer httputil.ReturnJSON(rw, ret) if ui.root.Storage == nil { ret["error"] = "No BlobRoot configured" ret["errorType"] = "server" return } mr, err := req.MultipartReader() if err != nil { ret["error"] = "reading body: " + err.Error() ret["errorType"] = "server" return } got := make([]map[string]interface{}, 0) for { part, err := mr.NextPart() if err == io.EOF { break } if err != nil { ret["error"] = "reading body: " + err.Error() ret["errorType"] = "server" break } fileName := part.FileName() if fileName == "" { continue } br, err := schema.WriteFileFromReader(ui.root.Storage, fileName, part) if err == nil { got = append(got, map[string]interface{}{ "filename": part.FileName(), "formname": part.FormName(), "fileref": br.String(), }) } else { ret["error"] = "writing to blobserver: " + err.Error() return } } ret["got"] = got }
func (sh *Handler) serveQuery(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var sq SearchQuery if err := sq.fromHTTP(req); err != nil { httputil.ServeJSONError(rw, err) return } sr, err := sh.Query(&sq) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, sr) }
func (sh *Handler) serveSignerAttrValue(rw http.ResponseWriter, req *http.Request) { ret := jsonMap() defer httputil.ReturnJSON(rw, ret) defer setPanicError(ret) signer := blobref.MustParse(mustGet(req, "signer")) attr := mustGet(req, "attr") value := mustGet(req, "value") pn, err := sh.index.PermanodeOfSignerAttrValue(signer, attr, value) if err != nil { ret["error"] = err.Error() } else { ret["permanode"] = pn.String() dr := sh.NewDescribeRequest() dr.Describe(pn, 2) dr.PopulateJSON(ret) } }
func (hh *HelpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { suffix := httputil.PathSuffix(req) if !httputil.IsGet(req) { http.Error(rw, "Illegal help method.", http.StatusMethodNotAllowed) return } switch suffix { case "": if clientConfig := req.FormValue("clientConfig"); clientConfig != "" { if clientConfigOnly, err := strconv.ParseBool(clientConfig); err == nil && clientConfigOnly { httputil.ReturnJSON(rw, hh.clientConfig) return } } hh.serveHelpHTML(rw, req) default: http.Error(rw, "Illegal help path.", http.StatusNotFound) } }
func (sh *Handler) serveRecentPermanodes(rw http.ResponseWriter, req *http.Request) { ret := jsonMap() defer httputil.ReturnJSON(rw, ret) ch := make(chan *Result) errch := make(chan error) go func() { errch <- sh.index.GetRecentPermanodes(ch, sh.owner, 50) }() dr := sh.NewDescribeRequest() recent := jsonMapList() for res := range ch { dr.Describe(res.BlobRef, 2) jm := jsonMap() jm["blobref"] = res.BlobRef.String() jm["owner"] = res.Signer.String() t := time.Unix(res.LastModTime, 0).UTC() jm["modtime"] = t.Format(time.RFC3339) recent = append(recent, jm) } err := <-errch if err != nil { // TODO: return error status code ret["error"] = err.Error() return } ret["recent"] = recent thumbSize := 0 if req.FormValue("thumbnails") != "" { thumbSize = 50 if i, _ := strconv.Atoi(req.FormValue("thumbnails")); i >= 25 && i < 800 { thumbSize = i } } dr.populateJSONThumbnails(ret, thumbSize) }
// handleSearch runs the requested search query against the search handler, and // if the results are within the domain allowed by the master query, forwards them // back to the client. func (a *Handler) handleSearch(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { camhttputil.BadRequestError(w, camhttputil.InvalidMethodError{}.Error()) return } if a.sh == nil { http.Error(w, "app proxy has no search handler", 500) return } a.masterQueryMu.RLock() if a.masterQuery == nil { http.Error(w, "search is not allowed", http.StatusForbidden) a.masterQueryMu.RUnlock() return } a.masterQueryMu.RUnlock() var sq search.SearchQuery if err := sq.FromHTTP(r); err != nil { camhttputil.ServeJSONError(w, err) return } sr, err := a.sh.Query(&sq) if err != nil { camhttputil.ServeJSONError(w, err) return } // check this search is in the allowed domain if !a.allowProxySearchResponse(sr) { // there's a chance our domainBlobs cache is expired so let's // refresh it and retry, but no more than once per minute. if err := a.refreshDomainBlobs(); err != nil { http.Error(w, "search scope is forbidden", http.StatusForbidden) return } if !a.allowProxySearchResponse(sr) { http.Error(w, "search scope is forbidden", http.StatusForbidden) return } } camhttputil.ReturnJSON(w, sr) }
func (sh *Handler) serveDescribe(rw http.ResponseWriter, req *http.Request) { defer httputil.RecoverJSON(rw, req) var dr DescribeRequest dr.fromHTTP(req) sh.initDescribeRequest(&dr) if dr.BlobRef.Valid() { dr.Describe(dr.BlobRef, dr.depth()) } for _, br := range dr.BlobRefs { dr.Describe(br, dr.depth()) } metaMap, err := dr.metaMapThumbs(thumbnailSize(req)) if err != nil { httputil.ServeJSONError(rw, err) return } httputil.ReturnJSON(rw, &DescribeResponse{metaMap}) }