コード例 #1
0
ファイル: facebook.go プロジェクト: joeljeske/sync_gateway
func verifyFacebook(fbUrl, accessToken string) (*FacebookResponse, error) {

	params := url.Values{"fields": []string{"id,name,email"}, "access_token": []string{accessToken}}
	destUrl := fbUrl + "/me?" + params.Encode()

	res, err := http.Get(destUrl)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode >= 300 {
		return nil, base.HTTPErrorf(http.StatusUnauthorized,
			"Facebook verification server status %d", res.StatusCode)
	}

	decoder := json.NewDecoder(res.Body)

	var response FacebookResponse
	err = decoder.Decode(&response)
	if err != nil {
		return nil, base.HTTPErrorf(http.StatusBadGateway, "Invalid response from Facebook verifier")
	}

	return &response, nil

}
コード例 #2
0
/*
 * From OAuth 2.0 spec
 * Handle OAuth 2.0 Authorize request
 * This might display a login page with a form to collect user credentials
 * which is part of an internal authentication flow
 */
func (h *handler) handleOidcTestProviderAuthorize() error {
	if !h.db.DatabaseContext.Options.UnsupportedOptions.OidcTestProvider.Enabled {
		return base.HTTPErrorf(http.StatusForbidden, "OIDC test provider is not enabled")
	}

	requestParams := h.rq.URL.RawQuery

	base.LogTo("OIDC", "handleOidcTestProviderAuthorize() raw authorize request raw query params = %v", requestParams)

	scope := h.rq.URL.Query().Get("scope")
	if scope == "" {
		return base.HTTPErrorf(http.StatusBadRequest, "missing scope parameter")
	}

	err := validateAuthRequestScope(scope)
	if err != nil {
		return err
	}

	p := &Page{Title: "Oidc Test Provider", Query: requestParams}
	t := template.New("Test Login")
	if t, err := t.Parse(login_html); err != nil {
		return base.HTTPErrorf(http.StatusInternalServerError, err.Error())
	} else {
		t.Execute(h.response, p)
	}

	return nil
}
コード例 #3
0
ファイル: google.go プロジェクト: vladoatanasov/sync_gateway
func verifyGoogle(idToken string, allowedAppID []string) (*GoogleResponse, error) {
	destUrl := googleTokenInfoURL + idToken

	res, err := http.Get(destUrl)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	decoder := json.NewDecoder(res.Body)

	var response GoogleResponse
	err = decoder.Decode(&response)
	if err != nil {
		return nil, base.HTTPErrorf(http.StatusBadGateway, "Invalid response from Google token verifier")
	}

	if response.ErrorDescription != "" {
		return nil, base.HTTPErrorf(http.StatusUnauthorized, response.ErrorDescription)
	}

	if !isValidAud(response.Aud, allowedAppID) {
		return nil, base.HTTPErrorf(http.StatusUnauthorized, "Invalid application id, please add it in the config")
	}

	return &response, nil
}
コード例 #4
0
ファイル: session_api.go プロジェクト: arjunblue/sync_gateway
func (h *handler) makeSessionFromEmail(email string, createUserIfNeeded bool) error {

	// Email is verified. Look up the user and make a login session for her:
	user, err := h.db.Authenticator().GetUserByEmail(email)
	if err != nil {
		return err
	}
	if user == nil {
		// The email address is authentic but we have no user account for it.
		if !createUserIfNeeded {
			return base.HTTPErrorf(http.StatusUnauthorized, "No such user")
		}

		if len(email) < 1 {
			return base.HTTPErrorf(http.StatusBadRequest, "Cannot register new user: email is missing")
		}

		// Create a User with the given email address as username and a random password.
		user, err = h.db.Authenticator().RegisterNewUser(email, email)
		if err != nil {
			return err
		}
	}
	return h.makeSession(user)

}
コード例 #5
0
// Fetch a configuration for a database from the ConfigServer
func (sc *ServerContext) getDbConfigFromServer(dbName string) (*DbConfig, error) {
	if sc.config.ConfigServer == nil {
		return nil, base.HTTPErrorf(http.StatusNotFound, "not_found")
	}

	urlStr := *sc.config.ConfigServer
	if !strings.HasSuffix(urlStr, "/") {
		urlStr += "/"
	}
	urlStr += url.QueryEscape(dbName)
	res, err := sc.HTTPClient.Get(urlStr)
	if err != nil {
		return nil, base.HTTPErrorf(http.StatusBadGateway,
			"Error contacting config server: %v", err)
	} else if res.StatusCode >= 300 {
		return nil, base.HTTPErrorf(res.StatusCode, res.Status)
	}

	var config DbConfig
	j := json.NewDecoder(res.Body)
	if err = j.Decode(&config); err != nil {
		return nil, base.HTTPErrorf(http.StatusBadGateway,
			"Bad response from config server: %v", err)
	}

	if err = config.setup(dbName); err != nil {
		return nil, err
	}
	return &config, nil
}
コード例 #6
0
ファイル: attachment.go プロジェクト: joeljeske/sync_gateway
// Parses a JSON MIME body, unmarshaling it into "into".
func ReadJSONFromMIME(headers http.Header, input io.Reader, into interface{}) error {
	contentType := headers.Get("Content-Type")
	if contentType != "" && !strings.HasPrefix(contentType, "application/json") {
		return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Invalid content type %s", contentType)
	}

	switch headers.Get("Content-Encoding") {
	case "gzip":
		var err error
		if input, err = gzip.NewReader(input); err != nil {
			return err
		}
	case "":
		break
	default:
		return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Unsupported Content-Encoding; use gzip")
	}

	decoder := json.NewDecoder(input)
	if err := decoder.Decode(into); err != nil {
		base.Warn("Couldn't parse JSON in HTTP request: %v", err)
		return base.HTTPErrorf(http.StatusBadRequest, "Bad JSON")
	}
	return nil
}
コード例 #7
0
ファイル: design_doc.go プロジェクト: paulharter/sync_gateway
func (db *Database) QueryDesignDoc(ddocName string, viewName string, options map[string]interface{}) (*sgbucket.ViewResult, error) {

	// Regular users have limitations on what they can query
	if db.user != nil {
		// * Regular users can only query when user views are enabled
		if !db.GetUserViewsEnabled() {
			return nil, base.HTTPErrorf(http.StatusForbidden, "forbidden")
		}
		// * Admins can query any design doc including the internal ones
		// * Regular users can query non-internal design docs
		if isInternalDDoc(ddocName) {
			return nil, base.HTTPErrorf(http.StatusForbidden, "forbidden")
		}
	}

	result, err := db.Bucket.View(ddocName, viewName, options)
	if err != nil {
		return nil, err
	}
	if isInternalDDoc(ddocName) {
		if options["include_docs"] == true {
			for _, row := range result.Rows {
				stripSyncProperty(row)
			}
		}
	} else {
		applyChannelFiltering := options["reduce"] != true && db.GetUserViewsEnabled()
		result = filterViewResult(result, db.user, applyChannelFiltering)
	}
	return &result, nil
}
コード例 #8
0
ファイル: persona.go プロジェクト: guoyu07/sync_gateway
// POST /_persona creates a browserID-based login session and sets its cookie.
// It's API-compatible with the CouchDB plugin: <https://github.com/iriscouch/browserid_couchdb/>
func (h *handler) handlePersonaPOST() error {
	if len(h.rq.Header["Origin"]) > 0 {
		// CORS not allowed for login #115
		return base.HTTPErrorf(http.StatusBadRequest, "No CORS")
	}
	var params struct {
		Assertion string `json:"assertion"`
	}
	err := h.readJSONInto(&params)
	if err != nil {
		return err
	}

	origin := h.server.config.Persona.Origin
	if origin == "" {
		base.Warn("Can't accept Persona logins: Server URL not configured")
		return base.HTTPErrorf(http.StatusInternalServerError, "Server url not configured")
	}

	// OK, now verify it:
	base.Logf("Persona: Verifying assertion %q for %q", params.Assertion, origin)
	verifiedInfo, err := VerifyPersona(params.Assertion, origin)
	if err != nil {
		base.Logf("Persona: Failed verify: %v", err)
		return err
	}
	base.Logf("Persona: Logged in %q!", verifiedInfo.Email)

	createUserIfNeeded := h.server.config.Persona.Register
	return h.makeSessionFromEmail(verifiedInfo.Email, createUserIfNeeded)

}
コード例 #9
0
ファイル: persona.go プロジェクト: guoyu07/sync_gateway
// Verifies a Persona/BrowserID assertion received from a client, returning either the verification
// response (which includes the verified email address) or an error.
// The 'audience' parameter must be the same as the 'origin' parameter the client used when
// requesting the assertion, i.e. the root URL of this website.
func VerifyPersona(assertion string, audience string) (*PersonaResponse, error) {
	// See <https://developer.mozilla.org/en-US/docs/Persona/Remote_Verification_API>
	res, err := http.PostForm("https://verifier.login.persona.org/verify",
		url.Values{"assertion": {assertion}, "audience": {audience}})
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode >= 300 {
		return nil, base.HTTPErrorf(http.StatusBadGateway,
			"Persona verification server status %d", res.StatusCode)
	}
	responseBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, base.HTTPErrorf(http.StatusBadGateway, "Invalid response from Persona verifier")
	}
	var response PersonaResponse
	if err = json.Unmarshal(responseBody, &response); err != nil {
		return nil, base.HTTPErrorf(http.StatusBadGateway, "Invalid response from Persona verifier")
	}
	if response.Status != "okay" {
		return nil, base.HTTPErrorf(http.StatusUnauthorized, response.Reason)
	}
	return &response, nil
}
コード例 #10
0
/*
 * From OAuth 2.0 spec
 * Return public certificates for signing keys
 */
func (h *handler) handleOidcTestProviderCerts() error {
	if !h.db.DatabaseContext.Options.UnsupportedOptions.OidcTestProvider.Enabled {
		return base.HTTPErrorf(http.StatusForbidden, "OIDC test provider is not enabled")
	}

	base.LogTo("OIDC", "handleOidcTestProviderCerts() called")

	privateKey, err := privateKey()
	if err != nil {
		return base.HTTPErrorf(http.StatusInternalServerError, "Error getting private RSA Key")
	}

	oidcPrivateKey := key.PrivateKey{
		KeyID:      testProviderKeyIdentifier,
		PrivateKey: privateKey,
	}

	jwk := oidcPrivateKey.JWK()

	h.response.Write([]byte("{\r\n\"keys\":[\r\n"))

	if bytes, err := jwk.MarshalJSON(); err == nil {
		h.response.Write(bytes)
	}

	h.response.Write([]byte("\r\n]\r\n}"))

	return nil
}
コード例 #11
0
ファイル: bulk_api.go プロジェクト: dgcaron/sync_gateway
// HTTP handler for a POST to _bulk_docs
func (h *handler) handleBulkDocs() error {
	body, err := h.readJSON()
	if err != nil {
		return err
	}
	newEdits, ok := body["new_edits"].(bool)
	if !ok {
		newEdits = true
	}

	docs, ok := body["docs"].([]interface{})
	if !ok {
		err = base.HTTPErrorf(http.StatusBadRequest, "missing 'docs' property")
		return err
	}
	h.db.ReserveSequences(uint64(len(docs)))

	result := make([]db.Body, 0, len(docs))
	for _, item := range docs {
		doc := item.(map[string]interface{})
		docid, _ := doc["_id"].(string)
		var err error
		var revid string
		if newEdits {
			if docid != "" {
				revid, err = h.db.Put(docid, doc)
			} else {
				docid, revid, err = h.db.Post(doc)
			}
		} else {
			revisions := db.ParseRevisions(doc)
			if revisions == nil {
				err = base.HTTPErrorf(http.StatusBadRequest, "Bad _revisions")
			} else {
				revid = revisions[0]
				err = h.db.PutExistingRev(docid, doc, revisions)
			}
		}

		status := db.Body{}
		if docid != "" {
			status["id"] = docid
		}
		if err != nil {
			code, msg := base.ErrorAsHTTPStatus(err)
			status["status"] = code
			status["error"] = base.CouchHTTPErrorName(code)
			status["reason"] = msg
			base.Logf("\tBulkDocs: Doc %q --> %d %s (%v)", docid, code, msg, err)
			err = nil // wrote it to output already; not going to return it
		} else {
			status["rev"] = revid
		}
		result = append(result, status)
	}

	h.writeJSONStatus(http.StatusCreated, result)
	return nil
}
コード例 #12
0
ファイル: api.go プロジェクト: mindhash/sync_gateway
// Stub handler for hadling create DB on the public API returns HTTP status 412
// if the db exists, and 403 if it doesn't.
// fixes issue #562
func (h *handler) handleCreateTarget() error {
	dbname := h.PathVar("targetdb")
	if _, err := h.server.GetDatabase(dbname); err != nil {
		return base.HTTPErrorf(http.StatusForbidden, "Creating a DB over the public API is unsupported")
	} else {
		return base.HTTPErrorf(http.StatusPreconditionFailed, "Database already exists")
	}
}
コード例 #13
0
ファイル: sequence_id.go プロジェクト: joeljeske/sync_gateway
func parseClockSequenceID(str string, sequenceHasher *sequenceHasher) (s SequenceID, err error) {

	if str == "" {
		return SequenceID{
			SeqType: ClockSequenceType,
			Clock:   base.NewSequenceClockImpl(),
		}, nil
	}

	s.SeqType = ClockSequenceType
	components := strings.Split(str, ":")
	if len(components) == 1 {
		// Convert simple zero to empty clock, to handle clients sending zero to mean 'no previous since'
		if components[0] == "0" {
			s.Clock = base.NewSequenceClockImpl()
		} else {
			// Standard clock hash
			if s.Clock, err = sequenceHasher.GetClock(components[0]); err != nil {
				return SequenceID{}, err
			}
		}
	} else if len(components) == 2 {
		// TriggeredBy Clock Hash, and vb.seq sequence
		if s.TriggeredByClock, err = sequenceHasher.GetClock(components[0]); err != nil {
			return SequenceID{}, err
		}
		sequenceComponents := strings.Split(components[1], ".")
		if len(sequenceComponents) != 2 {
			base.Warn("Unexpected sequence format - ignoring and relying on triggered by")
			return
		} else {
			if vb64, err := strconv.ParseUint(sequenceComponents[0], 10, 16); err != nil {
				base.Warn("Unable to convert sequence %v to int.", sequenceComponents[0])
			} else {
				s.vbNo = uint16(vb64)
				s.Seq, err = strconv.ParseUint(sequenceComponents[1], 10, 64)
			}
		}

	} else if len(components) == 3 {
		// Low hash, and vb.seq sequence.  Use low hash as clock, ignore vb.seq
		if s.Clock, err = sequenceHasher.GetClock(components[0]); err != nil {
			return SequenceID{}, err
		}

	} else {
		err = base.HTTPErrorf(400, "Invalid sequence")
	}

	if err != nil {
		err = base.HTTPErrorf(400, "Invalid sequence")
	}
	return s, err
}
コード例 #14
0
ファイル: session_api.go プロジェクト: arjunblue/sync_gateway
// DELETE /_session logs out the current session
func (h *handler) handleSessionDELETE() error {
	if len(h.rq.Header["Origin"]) > 0 {
		// CORS not allowed for login #115
		return base.HTTPErrorf(http.StatusBadRequest, "No CORS")
	}
	cookie := h.db.Authenticator().DeleteSessionForCookie(h.rq)
	if cookie == nil {
		return base.HTTPErrorf(http.StatusNotFound, "no session")
	}
	http.SetCookie(h.response, cookie)
	return nil
}
コード例 #15
0
ファイル: doc_api.go プロジェクト: paulharter/sync_gateway
// HTTP handler for a GET of a specific doc attachment
func (h *handler) handleGetAttachment() error {
	docid := h.PathVar("docid")
	attachmentName := h.PathVar("attach")
	revid := h.getQuery("rev")
	body, err := h.db.GetRev(docid, revid, false, nil)
	if err != nil {
		return err
	}
	if body == nil {
		return kNotFoundError
	}
	meta, ok := db.BodyAttachments(body)[attachmentName].(map[string]interface{})
	if !ok {
		return base.HTTPErrorf(http.StatusNotFound, "missing attachment %s", attachmentName)
	}
	digest := meta["digest"].(string)
	data, err := h.db.GetAttachment(db.AttachmentKey(digest))
	if err != nil {
		return err
	}

	status, start, end := h.handleRange(uint64(len(data)))
	if status > 299 {
		return base.HTTPErrorf(status, "")
	} else if status == http.StatusPartialContent {
		data = data[start:end]
	}
	h.setHeader("Content-Length", strconv.FormatUint(uint64(len(data)), 10))

	h.setHeader("Etag", strconv.Quote(digest))
	if contentType, ok := meta["content_type"].(string); ok {
		h.setHeader("Content-Type", contentType)
	}
	if encoding, ok := meta["encoding"].(string); ok {
		if h.getOptBoolQuery("content_encoding", true) {
			h.setHeader("Content-Encoding", encoding)
		} else {
			// Couchbase Lite wants to download the encoded form directly and store it that way,
			// but some HTTP client libraries like NSURLConnection will automatically decompress
			// the HTTP response if it has a Content-Encoding header. As a workaround, allow the
			// client to add ?content_encoding=false to the request URL to disable setting this
			// header.
			h.setHeader("X-Content-Encoding", encoding)
			h.setHeader("Content-Type", "application/gzip")
		}
	}
	if h.privs == adminPrivs { // #720
		h.setHeader("Content-Disposition", fmt.Sprintf("attachment; filename=%q", attachmentName))
	}
	h.response.WriteHeader(status)
	h.response.Write(data)
	return nil
}
コード例 #16
0
//Return the internal test RSA private key, this is decoded from a base64 encoded string
//stored as a constant above
func privateKey() (key *rsa.PrivateKey, err error) {

	decodedPrivateKey, err := base64.StdEncoding.DecodeString(base64EncodedPrivateKey)
	if err != nil {
		return nil, base.HTTPErrorf(http.StatusInternalServerError, "Error decoding private RSA Key")
	}
	key, err = x509.ParsePKCS1PrivateKey(decodedPrivateKey)
	if err != nil {
		return nil, base.HTTPErrorf(http.StatusInternalServerError, "Error parsing private RSA Key")
	}

	return
}
コード例 #17
0
ファイル: crud.go プロジェクト: couchbase/sync_gateway
// Adds an existing revision to a document along with its history (list of rev IDs.)
// This is equivalent to the "new_edits":false mode of CouchDB.
func (db *Database) PutExistingRev(docid string, body Body, docHistory []string) error {
	newRev := docHistory[0]
	generation, _ := ParseRevID(newRev)
	if generation < 0 {
		return base.HTTPErrorf(http.StatusBadRequest, "Invalid revision ID")
	}
	deleted, _ := body["_deleted"].(bool)

	expiry, err := body.extractExpiry()
	if err != nil {
		return base.HTTPErrorf(http.StatusBadRequest, "Invalid expiry: %v", err)
	}

	_, err = db.updateDoc(docid, false, expiry, func(doc *document) (Body, AttachmentData, error) {
		// (Be careful: this block can be invoked multiple times if there are races!)
		// Find the point where this doc's history branches from the current rev:
		currentRevIndex := len(docHistory)
		parent := ""
		for i, revid := range docHistory {
			if doc.History.contains(revid) {
				currentRevIndex = i
				parent = revid
				break
			}
		}
		if currentRevIndex == 0 {
			base.LogTo("CRUD+", "PutExistingRev(%q): No new revisions to add", docid)
			return nil, nil, couchbase.UpdateCancel // No new revisions to add
		}

		// Add all the new-to-me revisions to the rev tree:
		for i := currentRevIndex - 1; i >= 0; i-- {
			doc.History.addRevision(RevInfo{
				ID:      docHistory[i],
				Parent:  parent,
				Deleted: (i == 0 && deleted)})
			parent = docHistory[i]
		}

		// Process the attachments, replacing bodies with digests.
		parentRevID := doc.History[newRev].Parent
		newAttachments, err := db.storeAttachments(doc, body, generation, parentRevID, docHistory)
		if err != nil {
			return nil, nil, err
		}
		body["_rev"] = newRev
		return body, newAttachments, nil
	})
	return err
}
コード例 #18
0
ファイル: crud.go プロジェクト: mindhash/sync_gateway
// Calls the JS sync function to assign the doc to channels, grant users
// access to channels, and reject invalid documents.
func (db *Database) getChannelsAndAccess(doc *document, body Body, revID string) (result base.Set, access channels.AccessMap, roles channels.AccessMap, err error) {
	base.LogTo("CRUD+", "Invoking sync on doc %q rev %s", doc.ID, body["_rev"])

	// Get the parent revision, to pass to the sync function:
	var oldJsonBytes []byte
	if oldJsonBytes, err = db.getAncestorJSON(doc, revID); err != nil {
		return
	}
	oldJson := string(oldJsonBytes)

	if db.ChannelMapper != nil {
		// Call the ChannelMapper:
		var output *channels.ChannelMapperOutput
		output, err = db.ChannelMapper.MapToChannelsAndAccess(body, oldJson,
			makeUserCtx(db.user))
		if err == nil {
			result = output.Channels
			if !doc.hasFlag(channels.Deleted) { // deleted docs can't grant access
				access = output.Access
				roles = output.Roles
			}
			err = output.Rejection
			if err != nil {
				base.Logf("Sync fn rejected: new=%+v  old=%s --> %s", body, oldJson, err)
			} else if !validateAccessMap(access) || !validateRoleAccessMap(roles) {
				err = base.HTTPErrorf(500, "Error in JS sync function")
			}

		} else {
			base.Warn("Sync fn exception: %+v; doc = %s", err, body)
			err = base.HTTPErrorf(500, "Exception in JS sync function")
		}

	} else {
		// No ChannelMapper so by default use the "channels" property:
		value, _ := body["channels"].([]interface{})
		if value != nil {
			array := make([]string, 0, len(value))
			for _, channel := range value {
				channelStr, ok := channel.(string)
				if ok && len(channelStr) > 0 {
					array = append(array, channelStr)
				}
			}
			result, err = channels.SetFromArray(array, channels.KeepStar)
		}
	}
	return
}
コード例 #19
0
ファイル: crud.go プロジェクト: mindhash/sync_gateway
// Lowest-level method that reads a document from the bucket.
func (db *DatabaseContext) GetDoc(docid string) (*document, error) {
	key := realDocID(docid)
	if key == "" {
		return nil, base.HTTPErrorf(400, "Invalid doc ID")
	}
	dbExpvars.Add("document_gets", 1)
	doc := newDocument(docid)
	_, err := db.Bucket.Get(key, doc)
	if err != nil {
		return nil, err
	} else if !doc.hasValidSyncData() {
		return nil, base.HTTPErrorf(404, "Not imported")
	}
	return doc, nil
}
コード例 #20
0
ファイル: user.go プロジェクト: guoyu07/sync_gateway
// Checks whether this userImpl object contains valid data; if not, returns an error.
func (user *userImpl) validate() error {
	if err := (&user.roleImpl).validate(); err != nil {
		return err
	} else if user.Email_ != "" && !IsValidEmail(user.Email_) {
		return base.HTTPErrorf(http.StatusBadRequest, "Invalid email address")
	} else if user.OldPasswordHash_ != nil {
		return base.HTTPErrorf(http.StatusBadRequest, "Obsolete password hash present")
	}
	for roleName, _ := range user.ExplicitRoles_ {
		if !IsValidPrincipalName(roleName) {
			return base.HTTPErrorf(http.StatusBadRequest, "Invalid role name %q", roleName)
		}
	}
	return nil
}
コード例 #21
0
ファイル: handler.go プロジェクト: paulharter/sync_gateway
func (h *handler) writeMultipart(subtype string, callback func(*multipart.Writer) error) error {
	if !h.requestAccepts("multipart/") {
		return base.HTTPErrorf(http.StatusNotAcceptable, "Response is multipart")
	}

	// Get the output stream. Due to a CouchDB bug, if we're sending to it we need to buffer the
	// output in memory so we can trim the final bytes.
	var output io.Writer
	var buffer bytes.Buffer
	if h.userAgentIs("CouchDB") {
		output = &buffer
	} else {
		output = h.response
	}

	writer := multipart.NewWriter(output)
	h.setHeader("Content-Type",
		fmt.Sprintf("multipart/%s; boundary=%q", subtype, writer.Boundary()))

	err := callback(writer)
	writer.Close()

	if err == nil && output == &buffer {
		// Trim trailing newline; CouchDB is allergic to it:
		_, err = h.response.Write(bytes.TrimRight(buffer.Bytes(), "\r\n"))
	}
	return err
}
コード例 #22
0
ファイル: handler.go プロジェクト: paulharter/sync_gateway
// Reads & parses the request body, handling either JSON or multipart.
func (h *handler) readDocument() (db.Body, error) {
	contentType, attrs, _ := mime.ParseMediaType(h.rq.Header.Get("Content-Type"))
	switch contentType {
	case "", "application/json":
		return h.readJSON()
	case "multipart/related":
		if DebugMultipart {
			raw, err := h.readBody()
			if err != nil {
				return nil, err
			}
			reader := multipart.NewReader(bytes.NewReader(raw), attrs["boundary"])
			body, err := db.ReadMultipartDocument(reader)
			if err != nil {
				ioutil.WriteFile("GatewayPUT.mime", raw, 0600)
				base.Warn("Error reading MIME data: copied to file GatewayPUT.mime")
			}
			return body, err
		} else {
			reader := multipart.NewReader(h.requestBody, attrs["boundary"])
			return db.ReadMultipartDocument(reader)
		}
	default:
		return nil, base.HTTPErrorf(http.StatusUnsupportedMediaType, "Invalid content type %s", contentType)
	}
}
コード例 #23
0
ファイル: bulk_api.go プロジェクト: diesal11/sync_gateway
// HTTP handler for _dumpchannel
func (h *handler) handleDumpChannel() error {
	channelName := h.PathVar("channel")
	since := h.getIntQuery("since", 0)
	base.LogTo("HTTP", "Dump channel %q", channelName)

	chanLog := h.db.GetChangeLog(channelName, since)
	if chanLog == nil {
		return base.HTTPErrorf(http.StatusNotFound, "no such channel")
	}
	title := fmt.Sprintf("/%s: “%s” Channel", html.EscapeString(h.db.Name), html.EscapeString(channelName))
	h.setHeader("Content-Type", `text/html; charset="UTF-8"`)
	h.response.Write([]byte(fmt.Sprintf(
		`<!DOCTYPE html><html><head><title>%s</title></head><body>
		<h1>%s</h1><code>
		<p>Since = %d</p>
		<table border=1>
		`,
		title, title, chanLog[0].Sequence-1)))
	h.response.Write([]byte("\t<tr><th>Seq</th><th>Doc</th><th>Rev</th><th>Flags</th></tr>\n"))
	for _, entry := range chanLog {
		h.response.Write([]byte(fmt.Sprintf("\t<tr><td>%d</td><td>%s</td><td>%s</td><td>%08b</td>",
			entry.Sequence,
			html.EscapeString(entry.DocID), html.EscapeString(entry.RevID), entry.Flags)))
		h.response.Write([]byte("</tr>\n"))
	}
	h.response.Write([]byte("</table>\n</code></html></body>"))
	return nil
}
コード例 #24
0
// POST /_session creates a login session and sets its cookie
func (h *handler) handleSessionPOST() error {
	// CORS not allowed for login #115 #762
	originHeader := h.rq.Header["Origin"]
	if len(originHeader) > 0 {
		matched := ""
		if h.server.config.CORS != nil {
			matched = matchedOrigin(h.server.config.CORS.LoginOrigin, originHeader)
		}
		if matched == "" {
			return base.HTTPErrorf(http.StatusBadRequest, "No CORS")
		}
	}

	user, err := h.getUserFromSessionRequestBody()

	// If we fail to get a user from the body and we've got a non-GUEST authenticated user, create the session based on that user
	if user == nil && h.user != nil && h.user.Name() != "" {
		return h.makeSession(h.user)
	} else {
		if err != nil {
			return err
		}
		return h.makeSession(user)
	}

}
コード例 #25
0
ファイル: google.go プロジェクト: vladoatanasov/sync_gateway
// POST /_google creates a google-based login session and sets its cookie.
func (h *handler) handleGooglePOST() error {
	// CORS not allowed for login #115 #762
	originHeader := h.rq.Header["Origin"]
	if len(originHeader) > 0 {
		matched := matchedOrigin(h.server.config.CORS.LoginOrigin, originHeader)
		if matched == "" {
			return base.HTTPErrorf(http.StatusBadRequest, "No CORS")
		}
	}

	var params struct {
		IDToken string `json:"id_token"`
	}

	err := h.readJSONInto(&params)
	if err != nil {
		return err
	}

	//validate the google id token
	googleResponse, err := verifyGoogle(params.IDToken, h.server.config.Google.AppClientID)
	if err != nil {
		return err
	}

	createUserIfNeeded := h.server.config.Google.Register
	return h.makeSessionFromNameAndEmail(googleResponse.UserID, googleResponse.Email, createUserIfNeeded)
}
コード例 #26
0
ファイル: facebook.go プロジェクト: joeljeske/sync_gateway
// POST /_facebook creates a facebook-based login session and sets its cookie.
func (h *handler) handleFacebookPOST() error {
	// CORS not allowed for login #115 #762
	originHeader := h.rq.Header["Origin"]
	if len(originHeader) > 0 {
		matched := matchedOrigin(h.server.config.CORS.LoginOrigin, originHeader)
		if matched == "" {
			return base.HTTPErrorf(http.StatusBadRequest, "No CORS")
		}
	}
	var params struct {
		AccessToken string `json:"access_token"`
	}
	err := h.readJSONInto(&params)
	if err != nil {
		return err
	}

	facebookResponse, err := verifyFacebook(kFacebookOpenGraphURL, params.AccessToken)
	if err != nil {
		return err
	}

	createUserIfNeeded := h.server.config.Facebook.Register
	return h.makeSessionFromNameAndEmail(facebookResponse.Id, facebookResponse.Email, createUserIfNeeded)

}
コード例 #27
0
ファイル: session_api.go プロジェクト: arjunblue/sync_gateway
// POST /_session creates a login session and sets its cookie
func (h *handler) handleSessionPOST() error {
	// CORS not allowed for login #115 #762
	originHeader := h.rq.Header["Origin"]
	if len(originHeader) > 0 {
		matched := ""
		if h.server.config.CORS != nil {
			matched = matchedOrigin(h.server.config.CORS.LoginOrigin, originHeader)
		}
		if matched == "" {
			return base.HTTPErrorf(http.StatusBadRequest, "No CORS")
		}
	}

	var params struct {
		Name     string `json:"name"`
		Password string `json:"password"`
	}
	err := h.readJSONInto(&params)
	if err != nil {
		return err
	}
	var user auth.User
	user, err = h.db.Authenticator().GetUser(params.Name)
	if err != nil {
		return err
	}

	if user != nil && !user.Authenticate(params.Password) {
		user = nil
	}
	return h.makeSession(user)
}
コード例 #28
0
ファイル: doc_api.go プロジェクト: joeljeske/sync_gateway
// HTTP handler for a PUT of a document
func (h *handler) handlePutDoc() error {
	docid := h.PathVar("docid")
	body, err := h.readDocument()
	if err != nil {
		return err
	}
	var newRev string

	if h.getQuery("new_edits") != "false" {
		// Regular PUT:
		if oldRev := h.getQuery("rev"); oldRev != "" {
			body["_rev"] = oldRev
		} else if ifMatch := h.rq.Header.Get("If-Match"); ifMatch != "" {
			body["_rev"] = ifMatch
		}
		newRev, err = h.db.Put(docid, body)
		if err != nil {
			return err
		}
		h.setHeader("Etag", strconv.Quote(newRev))
	} else {
		// Replicator-style PUT with new_edits=false:
		revisions := db.ParseRevisions(body)
		if revisions == nil {
			return base.HTTPErrorf(http.StatusBadRequest, "Bad _revisions")
		}
		err = h.db.PutExistingRev(docid, body, revisions)
		if err != nil {
			return err
		}
		newRev = body["_rev"].(string)
	}
	h.writeJSONStatus(http.StatusCreated, db.Body{"ok": true, "id": docid, "rev": newRev})
	return nil
}
コード例 #29
0
ファイル: user.go プロジェクト: guoyu07/sync_gateway
func (user *userImpl) SetEmail(email string) error {
	if email != "" && !IsValidEmail(email) {
		return base.HTTPErrorf(http.StatusBadRequest, "Invalid email address")
	}
	user.Email_ = email
	return nil
}
コード例 #30
0
func (dc *DatabaseContext) TakeDbOffline(reason string) error {
	base.LogTo("CRUD", "Taking Database : %v, offline", dc.Name)
	dbState := atomic.LoadUint32(&dc.State)
	//If the DB is already trasitioning to: offline or is offline silently return
	if dbState == DBOffline || dbState == DBResyncing || dbState == DBStopping {
		return nil
	}

	if atomic.CompareAndSwapUint32(&dc.State, DBOnline, DBStopping) {

		//notify all active _changes feeds to close
		close(dc.ExitChanges)

		base.LogTo("CRUD", "Waiting for all active calls to complete on Database : %v", dc.Name)
		//Block until all current calls have returned, including _changes feeds
		dc.AccessLock.Lock()
		defer dc.AccessLock.Unlock()

		base.LogTo("CRUD", "Database : %v, is offline", dc.Name)
		//set DB state to Offline
		atomic.StoreUint32(&dc.State, DBOffline)

		if dc.EventMgr.HasHandlerForEvent(DBStateChange) {
			dc.EventMgr.RaiseDBStateChangeEvent(dc.Name, "offline", reason, *dc.Options.AdminInterface)
		}

		return nil
	} else {
		base.LogTo("CRUD", "Unable to take Database offline, database must be in Online state")
		return base.HTTPErrorf(http.StatusServiceUnavailable, "Unable to take Database offline, database must be in Online state")
	}
}