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
}
Exemple #2
0
// 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
}
Exemple #3
0
// 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
}
Exemple #4
0
// 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
}
Exemple #5
0
// 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
}
Exemple #6
0
// 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
}
Exemple #7
0
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
}