func TestShadowerPush(t *testing.T) { //base.LogKeys["Shadow"] = true bucket := makeExternalBucket() defer bucket.Close() db := setupTestDB(t) defer tearDownTestDB(t, db) var err error db.Shadower, err = NewShadower(db.DatabaseContext, bucket, nil) assertNoError(t, err, "NewShadower") key1rev1, err := db.Put("key1", Body{"aaa": "bbb"}) assertNoError(t, err, "Put") _, err = db.Put("key2", Body{"ccc": "ddd"}) assertNoError(t, err, "Put") base.Log("Waiting for shadower to catch up...") var doc1, doc2 Body waitFor(t, func() bool { return bucket.Get("key1", &doc1) == nil && bucket.Get("key2", &doc2) == nil }) assert.DeepEquals(t, doc1, Body{"aaa": "bbb"}) assert.DeepEquals(t, doc2, Body{"ccc": "ddd"}) base.Log("Deleting local doc") db.DeleteDoc("key1", key1rev1) waitFor(t, func() bool { err = bucket.Get("key1", &doc1) return err != nil }) assert.True(t, base.IsDocNotFoundError(err)) }
// Looks up a User by email address. func (auth *Authenticator) GetUserByEmail(email string) (User, error) { var info userByEmailInfo _, err := auth.bucket.Get(docIDForUserEmail(email), &info) if base.IsDocNotFoundError(err) { return nil, nil } else if err != nil { return nil, err } return auth.GetUser(info.Username) }
func (auth *Authenticator) GetSession(sessionid string) (*LoginSession, error) { var session LoginSession _, err := auth.bucket.Get(docIDForSession(sessionid), &session) if err != nil { if base.IsDocNotFoundError(err) { err = nil } return nil, err } return &session, nil }
// Looks up the raw JSON data of a revision that's been archived to a separate doc. // If the revision isn't found (e.g. has been deleted by compaction) returns 404 error. func (db *DatabaseContext) getOldRevisionJSON(docid string, revid string) ([]byte, error) { data, _, err := db.Bucket.GetRaw(oldRevisionKey(docid, revid)) if base.IsDocNotFoundError(err) { base.LogTo("CRUD+", "No old revision %q / %q", docid, revid) err = base.HTTPErrorf(404, "missing") } if data != nil { base.LogTo("CRUD+", "Got old revision %q / %q --> %d bytes", docid, revid, len(data)) } return data, err }
// Gets the body of a revision's nearest ancestor, as raw JSON (without _id or _rev.) // If no ancestor has any JSON, returns nil but no error. func (db *Database) getAncestorJSON(doc *document, revid string) ([]byte, error) { for { if revid = doc.History.getParent(revid); revid == "" { return nil, nil } else if body, err := db.getRevisionJSON(doc, revid); body != nil { return body, nil } else if !base.IsDocNotFoundError(err) { return nil, err } } }
// HTTP handler for a PUT of an attachment func (h *handler) handlePutAttachment() error { docid := h.PathVar("docid") attachmentName := h.PathVar("attach") attachmentContentType := h.rq.Header.Get("Content-Type") if attachmentContentType == "" { attachmentContentType = "application/octet-stream" } revid := h.getQuery("rev") if revid == "" { revid = h.rq.Header.Get("If-Match") } attachmentData, err := h.readBody() if err != nil { return err } body, err := h.db.GetRev(docid, revid, false, nil) if err != nil && base.IsDocNotFoundError(err) { // couchdb creates empty body on attachment PUT // for non-existant doc id body = db.Body{} body["_rev"] = revid } else if err != nil { return err } else if body != nil { body["_rev"] = revid } // find attachment (if it existed) attachments := db.BodyAttachments(body) if attachments == nil { attachments = make(map[string]interface{}) } // create new attachment attachment := make(map[string]interface{}) attachment["data"] = attachmentData attachment["content_type"] = attachmentContentType //attach it attachments[attachmentName] = attachment body["_attachments"] = attachments newRev, err := h.db.Put(docid, body) if err != nil { return err } h.setHeader("Etag", strconv.Quote(newRev)) h.writeJSONStatus(http.StatusCreated, db.Body{"ok": true, "id": docid, "rev": newRev}) return nil }
// Given a document ID and a set of revision IDs, looks up which ones are not known. func (db *Database) RevDiff(docid string, revids []string) (missing, possible []string) { if strings.HasPrefix(docid, "_design/") && db.user != nil { return // Users can't upload design docs, so ignore them } doc, err := db.GetDoc(docid) if err != nil { if !base.IsDocNotFoundError(err) { base.Warn("RevDiff(%q) --> %T %v", docid, err, err) // If something goes wrong getting the doc, treat it as though it's nonexistent. } missing = revids return } revmap := doc.History found := make(map[string]bool) maxMissingGen := 0 for _, revid := range revids { if revmap.contains(revid) { found[revid] = true } else { if missing == nil { missing = make([]string, 0, 5) } gen, _ := parseRevID(revid) if gen > 0 { missing = append(missing, revid) if gen > maxMissingGen { maxMissingGen = gen } } } } if missing != nil { possible = make([]string, 0, 5) for revid, _ := range revmap { gen, _ := parseRevID(revid) if !found[revid] && gen < maxMissingGen { possible = append(possible, revid) } } if len(possible) == 0 { possible = nil } } return }
// Given a document ID and a set of revision IDs, looks up which ones are not known. Returns an // array of the unknown revisions, and an array of known revisions that might be recent ancestors. func (db *Database) RevDiff(docid string, revids []string) (missing, possible []string) { if strings.HasPrefix(docid, "_design/") && db.user != nil { return // Users can't upload design docs, so ignore them } doc, err := db.GetDoc(docid) if err != nil { if !base.IsDocNotFoundError(err) { base.Warn("RevDiff(%q) --> %T %v", docid, err, err) // If something goes wrong getting the doc, treat it as though it's nonexistent. } missing = revids return } // Check each revid to see if it's in the doc's rev tree: revtree := doc.History revidsSet := base.SetFromArray(revids) possibleSet := make(map[string]bool) for _, revid := range revids { if !revtree.contains(revid) { missing = append(missing, revid) // Look at the doc's leaves for a known possible ancestor: if gen, _ := ParseRevID(revid); gen > 1 { revtree.forEachLeaf(func(possible *RevInfo) { if !revidsSet.Contains(possible.ID) { possibleGen, _ := ParseRevID(possible.ID) if possibleGen < gen && possibleGen >= gen-100 { possibleSet[possible.ID] = true } else if possibleGen == gen && possible.Parent != "" { possibleSet[possible.Parent] = true // since parent is < gen } } }) } } } // Convert possibleSet to an array (possible) if len(possibleSet) > 0 { possible = make([]string, 0, len(possibleSet)) for revid, _ := range possibleSet { possible = append(possible, revid) } } return }
func (auth *Authenticator) AuthenticateCookie(rq *http.Request, response http.ResponseWriter) (User, error) { cookie, _ := rq.Cookie(CookieName) if cookie == nil { return nil, nil } var session LoginSession _, err := auth.bucket.Get(docIDForSession(cookie.Value), &session) if err != nil { if base.IsDocNotFoundError(err) { err = nil } return nil, err } // Don't need to check session.Expiration, because Couchbase will have nuked the document. //update the session Expiration if 10% or more of the current expiration time has elapsed //if the session does not contain a Ttl (probably created prior to upgrading SG), use //default value of 24Hours if session.Ttl == 0 { session.Ttl = kDefaultSessionTTL } duration := session.Ttl sessionTimeElapsed := int((time.Now().Add(duration).Sub(session.Expiration)).Seconds()) tenPercentOfTtl := int(duration.Seconds()) / 10 if sessionTimeElapsed > tenPercentOfTtl { session.Expiration = time.Now().Add(duration) ttlSec := int(duration.Seconds()) if err = auth.bucket.Set(docIDForSession(session.ID), ttlSec, session); err != nil { return nil, err } base.AddDbPathToCookie(rq, cookie) cookie.Expires = session.Expiration http.SetCookie(response, cookie) } user, err := auth.GetUser(session.Username) if user != nil && user.Disabled() { user = nil } return user, err }