func (user *userImpl) FilterToAvailableChannels(channels base.Set) ch.TimedSet { output := ch.TimedSet{} for channel, _ := range channels { if channel == "*" { return user.InheritedChannels().Copy() } output.AddChannel(channel, user.CanSeeChannelSince(channel)) } return output }
// Recomputes the set of roles a User has been granted access to by sync() functions. // This is part of the ChannelComputer interface defined by the Authenticator. func (context *DatabaseContext) ComputeRolesForUser(user auth.User) (channels.TimedSet, error) { var vres struct { Rows []struct { Value channels.TimedSet } } opts := map[string]interface{}{"stale": false, "key": user.Name()} if verr := context.Bucket.ViewCustom("sync_gateway", "role_access", opts, &vres); verr != nil { return nil, verr } // Merge the TimedSets from the view result: var result channels.TimedSet for _, row := range vres.Rows { if result == nil { result = row.Value } else { result.Add(row.Value) } } return result, nil }
func (auth *Authenticator) rebuildRoles(user User) error { var roles ch.TimedSet if auth.channelComputer != nil { var err error roles, err = auth.channelComputer.ComputeRolesForUser(user) if err != nil { base.Warn("channelComputer.ComputeRolesForUser failed on user %s: %v", user.Name(), err) return err } } if roles == nil { roles = ch.TimedSet{} // it mustn't be nil; nil means it's unknown } if explicit := user.ExplicitRoles(); explicit != nil { roles.Add(explicit) } base.LogTo("Access", "Computed roles for %q: %s", user.Name(), roles) user.setRolesSince(roles) return nil }
// Recomputes the set of channels a User/Role has been granted access to by sync() functions. // This is part of the ChannelComputer interface defined by the Authenticator. func (context *DatabaseContext) ComputeChannelsForPrincipal(princ auth.Principal) (channels.TimedSet, error) { key := princ.Name() if _, ok := princ.(auth.User); !ok { key = "role:" + key // Roles are identified in access view by a "role:" prefix } var vres struct { Rows []struct { Value channels.TimedSet } } opts := map[string]interface{}{"stale": false, "key": key} if verr := context.Bucket.ViewCustom("sync_gateway", "access", opts, &vres); verr != nil { return nil, verr } channelSet := channels.TimedSet{} for _, row := range vres.Rows { channelSet.Add(row.Value) } return channelSet, nil }
// 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") } } } // Get the set of channels the user has access to; nil if user is admin or has access to "*" var availableChannels channels.TimedSet if h.user != nil { availableChannels = h.user.InheritedChannels() if availableChannels == nil { panic("no channels for user?") } if availableChannels.Contains("*") { 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 for ch, _ := range channelMap { if availableChannels == nil || availableChannels.Contains(ch) { result = append(result, ch) } } return result } // Subroutine that writes a response entry for a document: totalRows := 0 writeDoc := func(doc db.IDAndRev, channels []string) error { 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 { ID string `json:"id"` Key string `json:"key"` Value allDocsRowValue `json:"value"` Doc db.Body `json:"doc,omitempty"` UpdateSeq uint64 `json:"update_seq,omitempty"` } row := allDocsRow{ID: doc.DocID, Key: doc.DocID} // Filter channels to ones available to user, and bail out if inaccessible: if explicitDocIDs == nil { if channels = filterChannels(channels); channels == nil { return nil } } 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 || body["_removed"] != nil { return nil } if explicitDocIDs != nil { if channels = filterChannelSet(channelSet); channels == nil { return nil } doc.RevID = body["_rev"].(string) } if includeDocs { row.Doc = body } if includeAccess && (access != nil || roleAccess != nil) { row.Value.Access = map[string]base.Set{} for userName, channels := range access { row.Value.Access[userName] = channels.AsSet() } for roleName, channels := range roleAccess { row.Value.Access["role:"+roleName] = channels.AsSet() } } } row.Value.Rev = doc.RevID if includeSeqs { row.UpdateSeq = doc.Sequence } if includeChannels { row.Value.Channels = channels } if totalRows > 0 { h.response.Write([]byte(",\n")) } totalRows++ h.addJSON(row) return nil } // 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 { for _, docID := range explicitDocIDs { writeDoc(db.IDAndRev{DocID: docID, RevID: "", Sequence: 0}, nil) } } else { if err := h.db.ForEachDocID(writeDoc); err != nil { return err } } h.response.Write([]byte(fmt.Sprintf("],\n"+`"total_rows":%d,"update_seq":%d}`, totalRows, lastSeq))) return nil }