// handleMasterQuery allows an app to register the master query that defines the // domain limiting all subsequent search queries. func (a *Handler) handleMasterQuery(w http.ResponseWriter, r *http.Request) { if !(a.auth.AllowedAccess(r)&auth.OpAll == auth.OpAll) { auth.SendUnauthorized(w, r) return } if r.Method != http.MethodPost { http.Error(w, "not a POST", http.StatusMethodNotAllowed) return } if a.sh == nil { http.Error(w, "app proxy has no search handler", 500) return } if refresh, _ := strconv.ParseBool(r.FormValue("refresh")); refresh { if err := a.refreshDomainBlobs(); err != nil { if err == errRefreshSuppress { http.Error(w, "too many refresh requests", http.StatusTooManyRequests) } else { http.Error(w, fmt.Sprintf("%v", err), 500) } return } w.Write([]byte("OK")) return } sq := new(search.SearchQuery) if err := sq.FromHTTP(r); err != nil { http.Error(w, fmt.Sprintf("error reading master query: %v", err), 500) return } var masterQuery search.SearchQuery = *(sq) des := *(masterQuery.Describe) masterQuery.Describe = &des sr, err := a.sh.Query(sq) if err != nil { http.Error(w, fmt.Sprintf("error running master query: %v", err), 500) return } a.masterQueryMu.Lock() defer a.masterQueryMu.Unlock() a.masterQuery = &masterQuery a.domainBlobs = make(map[blob.Ref]bool, len(sr.Describe.Meta)) for _, v := range sr.Describe.Meta { a.domainBlobs[v.BlobRef] = true } a.domainBlobsRefresh = time.Now() w.Write([]byte("OK")) }
// 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 (c *Client) Query(req *search.SearchQuery) (*search.SearchResult, error) { sr, err := c.SearchRoot() if err != nil { return nil, err } url := sr + req.URLSuffix() body, err := json.MarshalIndent(req, "", "\t") if err != nil { return nil, err } hreq := c.newRequest("POST", url, bytes.NewReader(body)) hres, err := c.expect2XX(hreq) if err != nil { return nil, err } res := new(search.SearchResult) if err := httputil.DecodeJSON(hres, res); err != nil { return nil, err } return res, nil }
func (c *Client) Search(req *search.SearchQuery) (*search.SearchResult, error) { sr, err := c.SearchRoot() if err != nil { return nil, err } url := sr + req.URLSuffix() body, err := json.MarshalIndent(req, "", "\t") if err != nil { return nil, err } hreq := c.newRequest("POST", url, bytes.NewReader(body)) hres, err := c.doReqGated(hreq) if err != nil { return nil, err } defer hres.Body.Close() res := new(search.SearchResult) if err := json.NewDecoder(hres.Body).Decode(res); err != nil { return nil, err } return res, nil }
func (a *Handler) refreshDomainBlobs() error { a.masterQueryMu.Lock() defer a.masterQueryMu.Unlock() if time.Now().Before(a.domainBlobsRefresh.Add(time.Minute)) { // suppress refresh request to no more than once per minute return errRefreshSuppress } if a.masterQuery == nil { return errors.New("no master query") } var sq search.SearchQuery = *(a.masterQuery) des := *(sq.Describe) sq.Describe = &des sr, err := a.sh.Query(&sq) if err != nil { return fmt.Errorf("error running master query: %v", err) } a.domainBlobs = make(map[blob.Ref]bool, len(sr.Describe.Meta)) for _, v := range sr.Describe.Meta { a.domainBlobs[v.BlobRef] = true } a.domainBlobsRefresh = time.Now() return nil }