Ejemplo n.º 1
0
// Cleans up the Value property, and removes rows that aren't visible to the current user
func filterViewResult(input walrus.ViewResult, user auth.User) (result walrus.ViewResult) {
	checkChannels := false
	var visibleChannels ch.TimedSet
	if user != nil {
		visibleChannels = user.InheritedChannels()
		checkChannels = !visibleChannels.Contains("*")
	}
	result.TotalRows = input.TotalRows
	result.Rows = make([]*walrus.ViewRow, 0, len(input.Rows)/2)
	for _, row := range input.Rows {
		value := row.Value.([]interface{})
		// value[0] is the array of channels; value[1] is the actual value
		if !checkChannels || channelsIntersect(visibleChannels, value[0].([]interface{})) {
			// Add this row:
			stripSyncProperty(row)
			result.Rows = append(result.Rows, &walrus.ViewRow{
				Key:   row.Key,
				Value: value[1],
				ID:    row.ID,
				Doc:   row.Doc,
			})
		}
	}
	return
}
Ejemplo n.º 2
0
// Is any item of channels found in visibleChannels?
func channelsIntersect(visibleChannels ch.TimedSet, channels []interface{}) bool {
	for _, channel := range channels {
		if visibleChannels.Contains(channel.(string)) || channel == "*" {
			return true
		}
	}
	return false
}
Ejemplo n.º 3
0
// Cleans up the Value property, and removes rows that aren't visible to the current user
func filterViewResult(input sgbucket.ViewResult, user auth.User, applyChannelFiltering bool) (result sgbucket.ViewResult) {
	hasStarChannel := false
	var visibleChannels ch.TimedSet
	if user != nil {
		visibleChannels = user.InheritedChannels()
		hasStarChannel = !visibleChannels.Contains("*")
		if !applyChannelFiltering {
			return // this is an error
		}
	}
	result.TotalRows = input.TotalRows
	result.Rows = make([]*sgbucket.ViewRow, 0, len(input.Rows)/2)
	for _, row := range input.Rows {
		if applyChannelFiltering {
			value, ok := row.Value.([]interface{})
			if ok {
				// value[0] is the array of channels; value[1] is the actual value
				if !hasStarChannel || channelsIntersect(visibleChannels, value[0].([]interface{})) {
					// Add this row:
					stripSyncProperty(row)
					result.Rows = append(result.Rows, &sgbucket.ViewRow{
						Key:   row.Key,
						Value: value[1],
						ID:    row.ID,
						Doc:   row.Doc,
					})
				}
			}
		} else {
			// Add the raw row:
			stripSyncProperty(row)
			result.Rows = append(result.Rows, &sgbucket.ViewRow{
				Key:   row.Key,
				Value: row.Value,
				ID:    row.ID,
				Doc:   row.Doc,
			})
		}

	}
	result.TotalRows = len(result.Rows)
	return
}
Ejemplo n.º 4
0
// HTTP handler for _all_docs
func (h *handler) handleAllDocs() error {
	// http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API
	includeDocs := h.getBoolQuery("include_docs")
	includeChannels := h.getBoolQuery("channels")
	includeAccess := h.getBoolQuery("access") && h.user == nil
	includeRevs := h.getBoolQuery("revs")
	includeSeqs := h.getBoolQuery("update_seq")

	// Get the doc IDs if this is a POST request:
	var explicitDocIDs []string
	if h.rq.Method == "POST" {
		input, err := h.readJSON()
		if err == nil {
			keys, ok := input["keys"].([]interface{})
			explicitDocIDs = make([]string, len(keys))
			for i := 0; i < len(keys); i++ {
				if explicitDocIDs[i], ok = keys[i].(string); !ok {
					break
				}
			}
			if !ok {
				err = base.HTTPErrorf(http.StatusBadRequest, "Bad/missing keys")
			}
		}
	} else if h.rq.Method == "GET" {
		keys := h.getQuery("keys")
		if len(keys) > 0 {
			var queryKeys []string
			err := json.Unmarshal([]byte(keys), &queryKeys)
			if err != nil {
				err = base.HTTPErrorf(http.StatusBadRequest, "Bad keys")
			}
			if len(queryKeys) > 0 {
				explicitDocIDs = queryKeys
			}
		}
	}

	// Get the set of channels the user has access to; nil if user is admin or has access to user "*"
	var availableChannels channels.TimedSet
	if h.user != nil {
		availableChannels = h.user.InheritedChannels()
		if availableChannels == nil {
			panic("no channels for user?")
		}
		if availableChannels.Contains(channels.UserStarChannel) {
			availableChannels = nil
		}
	}

	// Subroutines that filter a channel list down to the ones that the user has access to:
	filterChannels := func(channels []string) []string {
		if availableChannels == nil {
			return channels
		}
		dst := 0
		for _, ch := range channels {
			if availableChannels.Contains(ch) {
				channels[dst] = ch
				dst++
			}
		}
		if dst == 0 {
			return nil
		}
		return channels[0:dst]
	}
	filterChannelSet := func(channelMap channels.ChannelMap) []string {
		var result []string
		if availableChannels == nil {
			result = []string{}
		}
		for ch, rm := range channelMap {
			if availableChannels == nil || availableChannels.Contains(ch) {
				//Do not include channels doc removed from in this rev
				if rm == nil {
					result = append(result, ch)
				}
			}
		}
		return result
	}

	type allDocsRowValue struct {
		Rev      string              `json:"rev"`
		Channels []string            `json:"channels,omitempty"`
		Access   map[string]base.Set `json:"access,omitempty"` // for admins only
	}
	type allDocsRow struct {
		Key       string           `json:"key"`
		ID        string           `json:"id,omitempty"`
		Value     *allDocsRowValue `json:"value,omitempty"`
		Doc       db.Body          `json:"doc,omitempty"`
		UpdateSeq uint64           `json:"update_seq,omitempty"`
		Error     string           `json:"error,omitempty"`
		Status    int              `json:"status,omitempty"`
	}

	// Subroutine that creates a response row for a document:
	totalRows := 0
	createRow := func(doc db.IDAndRev, channels []string) *allDocsRow {
		row := &allDocsRow{Key: doc.DocID}
		value := allDocsRowValue{}

		// Filter channels to ones available to user, and bail out if inaccessible:
		if explicitDocIDs == nil {
			if channels = filterChannels(channels); channels == nil {
				return nil // silently skip this doc
			}
		}

		if explicitDocIDs != nil || includeDocs || includeAccess {
			// Fetch the document body and other metadata that lives with it:
			body, channelSet, access, roleAccess, _, _, err := h.db.GetRevAndChannels(doc.DocID, doc.RevID, includeRevs)
			if err != nil {
				row.Status, _ = base.ErrorAsHTTPStatus(err)
				return row
			} else if body["_removed"] != nil {
				row.Status = http.StatusForbidden
				return row
			}
			if explicitDocIDs != nil {
				if channels = filterChannelSet(channelSet); channels == nil {
					row.Status = http.StatusForbidden
					return row
				}
				doc.RevID = body["_rev"].(string)
			}
			if includeDocs {
				row.Doc = body
			}
			if includeAccess && (access != nil || roleAccess != nil) {
				value.Access = map[string]base.Set{}
				for userName, channels := range access {
					value.Access[userName] = channels.AsSet()
				}
				for roleName, channels := range roleAccess {
					value.Access["role:"+roleName] = channels.AsSet()
				}
			}
		}

		row.Value = &value
		row.ID = doc.DocID
		value.Rev = doc.RevID
		if includeSeqs {
			row.UpdateSeq = doc.Sequence
		}
		if includeChannels {
			row.Value.Channels = channels
		}
		return row
	}

	// Subroutine that writes a response entry for a document:
	writeDoc := func(doc db.IDAndRev, channels []string) bool {
		row := createRow(doc, channels)
		if row != nil {
			if row.Status >= 300 {
				row.Error = base.CouchHTTPErrorName(row.Status)
			}
			if totalRows > 0 {
				h.response.Write([]byte(","))
			}
			totalRows++
			h.addJSON(row)
			return true
		}
		return false
	}

	var options db.ForEachDocIDOptions
	options.Startkey = h.getQuery("startkey")
	options.Endkey = h.getQuery("endkey")
	options.Limit = h.getIntQuery("limit", 0)

	// Now it's time to actually write the response!
	lastSeq, _ := h.db.LastSequence()
	h.setHeader("Content-Type", "application/json")
	h.response.Write([]byte(`{"rows":[` + "\n"))

	if explicitDocIDs != nil {
		count := uint64(0)
		for _, docID := range explicitDocIDs {
			writeDoc(db.IDAndRev{DocID: docID, RevID: "", Sequence: 0}, nil)
			count++
			if options.Limit > 0 && count == options.Limit {
				break
			}

		}
	} else {
		if err := h.db.ForEachDocID(writeDoc, options); err != nil {
			return err
		}
	}

	h.response.Write([]byte(fmt.Sprintf("],\n"+`"total_rows":%d,"update_seq":%d}`,
		totalRows, lastSeq)))
	return nil
}