func (h *handler) invoke(method handlerMethod) error { if LogRequests { log.Printf("%s %s", h.rq.Method, h.rq.URL) } h.setHeader("Server", VersionString) // Authenticate all paths other than "/_session": path := h.rq.URL.Path if path != "/_session" && path != "/_browserid" { if err := h.checkAuth(); err != nil { return err } } // If there is a "db" path variable, look up the database: if dbname, ok := h.PathVars()["db"]; ok { var err error if dbname == h.context.dbcontext.Name { h.db, err = db.GetDatabase(h.context.dbcontext, h.user) } else { err = &base.HTTPError{http.StatusNotFound, "no such database"} } if err != nil { return err } } return method(h) // Call the actual handler code }
// Top-level handler call. It's passed a pointer to the specific method to run. func (h *handler) invoke(method handlerMethod) error { base.LogTo("HTTP", "%s %s", h.rq.Method, h.rq.URL) h.setHeader("Server", VersionString) // If there is a "db" path variable, look up the database context: if dbname, ok := h.PathVars()["db"]; ok { h.context = h.server.databases[dbname] if h.context == nil { return &base.HTTPError{http.StatusNotFound, "no such database"} } } // Authenticate; admin handlers can ignore missing credentials if err := h.checkAuth(); err != nil { if !h.admin { return err } } // Now look up the database: if h.context != nil { var err error h.db, err = db.GetDatabase(h.context.dbcontext, h.user) if err != nil { return err } } return method(h) // Call the actual handler code }
// Top-level handler call. It's passed a pointer to the specific method to run. func (h *handler) invoke(method handlerMethod) error { h.setHeader("Server", VersionString) // If there is a "db" path variable, look up the database context: var dbContext *db.DatabaseContext if dbname, ok := h.PathVars()["db"]; ok { dbContext = h.server.Database(dbname) if dbContext == nil { h.logRequestLine() return &base.HTTPError{http.StatusNotFound, "no such database '" + dbname + "'"} } } // Authenticate, if not on admin port: if h.privs != adminPrivs { if err := h.checkAuth(dbContext); err != nil { h.logRequestLine() return err } } h.logRequestLine() // Now set the request's Database (i.e. context + user) if dbContext != nil { var err error h.db, err = db.GetDatabase(dbContext, h.user) if err != nil { return err } } return method(h) // Call the actual handler code }
// Top-level handler call. It's passed a pointer to the specific method to run. func (h *handler) invoke(method handlerMethod) error { h.setHeader("Server", VersionString) // If there is a "db" path variable, look up the database context: var dbContext *db.DatabaseContext if dbname := h.PathVar("db"); dbname != "" { var err error if dbContext, err = h.server.GetDatabase(dbname); err != nil { h.logRequestLine() return err } } // Authenticate, if not on admin port: if h.privs != adminPrivs { if err := h.checkAuth(dbContext); err != nil { h.logRequestLine() return err } } h.logRequestLine() // Now set the request's Database (i.e. context + user) if dbContext != nil { var err error h.db, err = db.GetDatabase(dbContext, h.user) if err != nil { return err } } return method(h) // Call the actual handler code }
// Initialize REST handlers. Call this once on launch. func InitREST(bucket *couchbase.Bucket, dbName string, serverURL string) *context { if dbName == "" { dbName = bucket.Name } dbcontext, err := db.NewDatabaseContext(dbName, bucket) if err != nil { return nil } newdb, err := db.GetDatabase(dbcontext, nil) if err != nil { return nil } newdb.ReadDesignDocument() if dbcontext.ChannelMapper == nil { log.Printf("Channel mapper undefined; using default") // Always have a channel mapper object even if it does nothing: dbcontext.ChannelMapper, _ = channels.NewDefaultChannelMapper() } if dbcontext.Validator == nil { log.Printf("Validator undefined; no validation") } c := &context{ dbcontext: dbcontext, auth: auth.NewAuthenticator(bucket), serverURL: serverURL, } http.Handle("/", createHandler(c)) return c }
// Top-level handler call. It's passed a pointer to the specific method to run. func (h *handler) invoke(method handlerMethod) error { restExpvars.Add("requests_total", 1) restExpvars.Add("requests_active", 1) defer restExpvars.Add("requests_active", -1) var err error if h.server.config.CompressResponses == nil || *h.server.config.CompressResponses { if encoded := NewEncodedResponseWriter(h.response, h.rq); encoded != nil { h.response = encoded defer encoded.Close() } } switch h.rq.Header.Get("Content-Encoding") { case "": h.requestBody = h.rq.Body case "gzip": if h.requestBody, err = gzip.NewReader(h.rq.Body); err != nil { return err } h.rq.Header.Del("Content-Encoding") // to prevent double decoding later on default: return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Unsupported Content-Encoding; use gzip") } h.setHeader("Server", VersionString) // If there is a "db" path variable, look up the database context: var dbContext *db.DatabaseContext if dbname := h.PathVar("db"); dbname != "" { if dbContext, err = h.server.GetDatabase(dbname); err != nil { h.logRequestLine() return err } } // Authenticate, if not on admin port: if h.privs != adminPrivs { if err = h.checkAuth(dbContext); err != nil { h.logRequestLine() return err } } h.logRequestLine() // Now set the request's Database (i.e. context + user) if dbContext != nil { h.db, err = db.GetDatabase(dbContext, h.user) if err != nil { return err } } return method(h) // Call the actual handler code }
func TestChannelAccessChanges(t *testing.T) { // base.ParseLogFlags([]string{"Cache", "Changes+", "CRUD"}) rt := restTester{syncFn: `function(doc) {access(doc.owner, doc._id);channel(doc.channel)}`} a := rt.ServerContext().Database("db").Authenticator() guest, err := a.GetUser("") assert.Equals(t, err, nil) guest.SetDisabled(false) err = a.Save(guest) assert.Equals(t, err, nil) // Create users: alice, err := a.NewUser("alice", "letmein", channels.SetOf("zero")) a.Save(alice) zegpold, err := a.NewUser("zegpold", "letmein", channels.SetOf("zero")) a.Save(zegpold) // Create some docs that give users access: response := rt.send(request("PUT", "/db/alpha", `{"owner":"alice"}`)) // seq=1 assertStatus(t, response, 201) var body db.Body json.Unmarshal(response.Body.Bytes(), &body) assert.Equals(t, body["ok"], true) alphaRevID := body["rev"].(string) assertStatus(t, rt.send(request("PUT", "/db/beta", `{"owner":"boadecia"}`)), 201) // seq=2 assertStatus(t, rt.send(request("PUT", "/db/delta", `{"owner":"alice"}`)), 201) // seq=3 assertStatus(t, rt.send(request("PUT", "/db/gamma", `{"owner":"zegpold"}`)), 201) // seq=4 assertStatus(t, rt.send(request("PUT", "/db/a1", `{"channel":"alpha"}`)), 201) // seq=5 assertStatus(t, rt.send(request("PUT", "/db/b1", `{"channel":"beta"}`)), 201) // seq=6 assertStatus(t, rt.send(request("PUT", "/db/d1", `{"channel":"delta"}`)), 201) // seq=7 assertStatus(t, rt.send(request("PUT", "/db/g1", `{"channel":"gamma"}`)), 201) // seq=8 // Check the _changes feed: var changes struct { Results []db.ChangeEntry } response = rt.send(requestByUser("GET", "/db/_changes", "", "zegpold")) log.Printf("_changes looks like: %s", response.Body.Bytes()) err = json.Unmarshal(response.Body.Bytes(), &changes) assert.Equals(t, err, nil) assert.Equals(t, len(changes.Results), 1) since := changes.Results[0].Seq assert.Equals(t, changes.Results[0].ID, "g1") assert.Equals(t, since, db.SequenceID{Seq: 8}) // Check user access: alice, _ = a.GetUser("alice") assert.DeepEquals(t, alice.Channels(), channels.TimedSet{"zero": 0x1, "alpha": 0x1, "delta": 0x3}) zegpold, _ = a.GetUser("zegpold") assert.DeepEquals(t, zegpold.Channels(), channels.TimedSet{"zero": 0x1, "gamma": 0x4}) // Update a document to revoke access to alice and grant it to zegpold: str := fmt.Sprintf(`{"owner":"zegpold", "_rev":%q}`, alphaRevID) assertStatus(t, rt.send(request("PUT", "/db/alpha", str)), 201) // seq=9 // Check user access again: alice, _ = a.GetUser("alice") assert.DeepEquals(t, alice.Channels(), channels.TimedSet{"zero": 0x1, "delta": 0x3}) zegpold, _ = a.GetUser("zegpold") assert.DeepEquals(t, zegpold.Channels(), channels.TimedSet{"zero": 0x1, "alpha": 0x9, "gamma": 0x4}) // Look at alice's _changes feed: changes.Results = nil response = rt.send(requestByUser("GET", "/db/_changes", "", "alice")) log.Printf("//////// _changes for alice looks like: %s", response.Body.Bytes()) json.Unmarshal(response.Body.Bytes(), &changes) assert.Equals(t, len(changes.Results), 1) assert.Equals(t, changes.Results[0].ID, "d1") // The complete _changes feed for zegpold contains docs a1 and g1: changes.Results = nil response = rt.send(requestByUser("GET", "/db/_changes", "", "zegpold")) log.Printf("//////// _changes for zegpold looks like: %s", response.Body.Bytes()) json.Unmarshal(response.Body.Bytes(), &changes) assert.Equals(t, len(changes.Results), 2) assert.Equals(t, changes.Results[0].ID, "g1") assert.Equals(t, changes.Results[0].Seq, db.SequenceID{Seq: 8}) assert.Equals(t, changes.Results[1].ID, "a1") assert.Equals(t, changes.Results[1].Seq, db.SequenceID{Seq: 5, TriggeredBy: 9}) // Changes feed with since=gamma:8 would ordinarily be empty, but zegpold got access to channel // alpha after sequence 8, so the pre-existing docs in that channel are included: response = rt.send(requestByUser("GET", fmt.Sprintf("/db/_changes?since=%s", since), "", "zegpold")) log.Printf("_changes looks like: %s", response.Body.Bytes()) changes.Results = nil json.Unmarshal(response.Body.Bytes(), &changes) assert.Equals(t, len(changes.Results), 1) assert.Equals(t, changes.Results[0].ID, "a1") // What happens if we call access() with a nonexistent username? assertStatus(t, rt.send(request("PUT", "/db/epsilon", `{"owner":"waldo"}`)), 201) // Finally, throw a wrench in the works by changing the sync fn. Note that normally this wouldn't // be changed while the database is in use (only when it's re-opened) but for testing purposes // we do it now because we can't close and re-open an ephemeral Walrus database. dbc := rt.ServerContext().Database("db") db, _ := db.GetDatabase(dbc, nil) changed, err := db.UpdateSyncFun(`function(doc) {access("alice", "beta");channel("beta");}`) assert.Equals(t, err, nil) assert.True(t, changed) changeCount, err := db.UpdateAllDocChannels(true, false) assert.Equals(t, err, nil) assert.Equals(t, changeCount, 9) changes.Results = nil response = rt.send(requestByUser("GET", "/db/_changes", "", "alice")) log.Printf("_changes looks like: %s", response.Body.Bytes()) json.Unmarshal(response.Body.Bytes(), &changes) expectedIDs := []string{"beta", "delta", "gamma", "a1", "b1", "d1", "g1", "alpha", "epsilon"} assert.Equals(t, len(changes.Results), len(expectedIDs)) for i, expectedID := range expectedIDs { assert.Equals(t, changes.Results[i].ID, expectedID) } // Check accumulated statistics: assert.Equals(t, db.ChangesClientStats.TotalCount(), uint32(5)) assert.Equals(t, db.ChangesClientStats.MaxCount(), uint32(1)) db.ChangesClientStats.Reset() assert.Equals(t, db.ChangesClientStats.TotalCount(), uint32(0)) assert.Equals(t, db.ChangesClientStats.MaxCount(), uint32(0)) }
// Adds a database to the ServerContext given its configuration. func (sc *ServerContext) AddDatabaseFromConfig(config *DbConfig) (*db.DatabaseContext, error) { server := "http://localhost:8091" pool := "default" bucketName := config.name if config.Server != nil { server = *config.Server } if config.Pool != nil { pool = *config.Pool } if config.Bucket != nil { bucketName = *config.Bucket } dbName := config.name if dbName == "" { dbName = bucketName } base.Log("Opening db /%s as bucket %q, pool %q, server <%s>", dbName, bucketName, pool, server) if err := db.ValidateDatabaseName(dbName); err != nil { return nil, err } var importDocs, autoImport bool switch config.ImportDocs { case nil, false: case true: importDocs = true case "continuous": importDocs = true autoImport = true default: return nil, fmt.Errorf("Unrecognized value for ImportDocs: %#v", config.ImportDocs) } // Connect to the bucket and add the database: spec := base.BucketSpec{ Server: server, PoolName: pool, BucketName: bucketName, } if config.Username != "" { spec.Auth = config } bucket, err := db.ConnectToBucket(spec) if err != nil { return nil, err } dbcontext, err := db.NewDatabaseContext(dbName, bucket, autoImport) if err != nil { return nil, err } syncFn := "" if config.Sync != nil { syncFn = *config.Sync } if err := sc.applySyncFunction(dbcontext, syncFn); err != nil { return nil, err } if importDocs { db, _ := db.GetDatabase(dbcontext, nil) if _, err := db.UpdateAllDocChannels(false, true); err != nil { return nil, err } } if config.RevsLimit != nil && *config.RevsLimit > 0 { dbcontext.RevsLimit = *config.RevsLimit } if dbcontext.ChannelMapper == nil { base.Log("Using default sync function 'channel(doc.channels)' for database %q", dbName) } // Create default users & roles: if err := sc.installPrincipals(dbcontext, config.Roles, "role"); err != nil { return nil, err } else if err := sc.installPrincipals(dbcontext, config.Users, "user"); err != nil { return nil, err } // Install bucket-shadower if any: if shadow := config.Shadow; shadow != nil { if err := sc.startShadowing(dbcontext, shadow); err != nil { base.Warn("Database %q: unable to connect to external bucket for shadowing: %v", dbName, err) } } // Register it so HTTP handlers can find it: if err := sc.registerDatabase(dbcontext); err != nil { dbcontext.Close() return nil, err } sc.setDatabaseConfig(config.name, config) return dbcontext, nil }