// serveWriteJSON receives incoming series data in JSON and writes it to the database. func (h *Handler) serveWriteJSON(w http.ResponseWriter, r *http.Request, body []byte, user *meta.UserInfo) { var bp client.BatchPoints var dec *json.Decoder dec = json.NewDecoder(bytes.NewReader(body)) if err := dec.Decode(&bp); err != nil { if err.Error() == "EOF" { w.WriteHeader(http.StatusOK) return } resultError(w, influxql.Result{Err: err}, http.StatusBadRequest) return } if bp.Database == "" { resultError(w, influxql.Result{Err: fmt.Errorf("database is required")}, http.StatusBadRequest) return } if di, err := h.MetaStore.Database(bp.Database); err != nil { resultError(w, influxql.Result{Err: fmt.Errorf("metastore database error: %s", err)}, http.StatusInternalServerError) return } else if di == nil { resultError(w, influxql.Result{Err: fmt.Errorf("database not found: %q", bp.Database)}, http.StatusNotFound) return } if h.requireAuthentication && user == nil { resultError(w, influxql.Result{Err: fmt.Errorf("user is required to write to database %q", bp.Database)}, http.StatusUnauthorized) return } if h.requireAuthentication && !user.Authorize(influxql.WritePrivilege, bp.Database) { resultError(w, influxql.Result{Err: fmt.Errorf("%q user is not authorized to write to database %q", user.Name, bp.Database)}, http.StatusUnauthorized) return } points, err := NormalizeBatchPoints(bp) if err != nil { resultError(w, influxql.Result{Err: err}, http.StatusBadRequest) return } // Convert the json batch struct to a points writer struct if err := h.PointsWriter.WritePoints(&cluster.WritePointsRequest{ Database: bp.Database, RetentionPolicy: bp.RetentionPolicy, ConsistencyLevel: cluster.ConsistencyLevelOne, Points: points, }); influxdb.IsClientError(err) { resultError(w, influxql.Result{Err: err}, http.StatusBadRequest) return } else if err != nil { resultError(w, influxql.Result{Err: err}, http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) }
// Authorize user u to execute query q on database. // database can be "" for queries that do not require a database. // If no user is provided it will return an error unless the query's first statement is to create // a root user. func (q *QueryExecutor) Authorize(u *meta.UserInfo, query *influxql.Query, database string) error { const authErrLogFmt = "unauthorized request | user: %q | query: %q | database %q\n" // Special case if no users exist. if count, err := q.MetaStore.UserCount(); count == 0 && err == nil { // Get the first statement in the query. stmt := query.Statements[0] // First statement must create a root user. if cu, ok := stmt.(*influxql.CreateUserStatement); !ok || cu.Privilege == nil || *cu.Privilege != influxql.AllPrivileges { return ErrAuthorize{text: "no users exist. create root user first or disable authentication"} } return nil } if u == nil { q.Logger.Printf(authErrLogFmt, "", query.String(), database) return ErrAuthorize{text: "no user provided"} } // Cluster admins can do anything. if u.Admin { return nil } // Check each statement in the query. for _, stmt := range query.Statements { // Get the privileges required to execute the statement. privs := stmt.RequiredPrivileges() // Make sure the user has each privilege required to execute // the statement. for _, p := range privs { // Use the db name specified by the statement or the db // name passed by the caller if one wasn't specified by // the statement. dbname := p.Name if dbname == "" { dbname = database } // Check if user has required privilege. if !u.Authorize(p.Privilege, dbname) { var msg string if dbname == "" { msg = "requires cluster admin" } else { msg = fmt.Sprintf("requires %s privilege on %s", p.Privilege.String(), dbname) } q.Logger.Printf(authErrLogFmt, u.Name, query.String(), database) return ErrAuthorize{ text: fmt.Sprintf("%s not authorized to execute '%s'. %s", u.Name, stmt.String(), msg), } } } } return nil }
// Authorize user u to execute query q on database. // database can be "" for queries that do not require a database. // If no user is provided it will return an error unless the query's first statement is to create // a root user. func (q *QueryExecutor) Authorize(u *meta.UserInfo, query *influxql.Query, database string) error { // Special case if no users exist. if count, err := q.MetaStore.UserCount(); count == 0 && err == nil { // Ensure there is at least one statement. if len(query.Statements) > 0 { // First statement in the query must create a user with admin privilege. cu, ok := query.Statements[0].(*influxql.CreateUserStatement) if ok && cu.Admin == true { return nil } } return NewErrAuthorize(q, query, "", database, "create admin user first or disable authentication") } if u == nil { return NewErrAuthorize(q, query, "", database, "no user provided") } // Admin privilege allows the user to execute all statements. if u.Admin { return nil } // Check each statement in the query. for _, stmt := range query.Statements { // Get the privileges required to execute the statement. privs := stmt.RequiredPrivileges() // Make sure the user has the privileges required to execute // each statement. for _, p := range privs { if p.Admin { // Admin privilege already checked so statement requiring admin // privilege cannot be run. msg := fmt.Sprintf("statement '%s', requires admin privilege", stmt) return NewErrAuthorize(q, query, u.Name, database, msg) } // Use the db name specified by the statement or the db // name passed by the caller if one wasn't specified by // the statement. db := p.Name if db == "" { db = database } if !u.Authorize(p.Privilege, db) { msg := fmt.Sprintf("statement '%s', requires %s on %s", stmt, p.Privilege.String(), db) return NewErrAuthorize(q, query, u.Name, database, msg) } } } return nil }
// serveWriteLine receives incoming series data in line protocol format and writes it to the database. func (h *Handler) serveWriteLine(w http.ResponseWriter, r *http.Request, body []byte, user *meta.UserInfo) { // Some clients may not set the content-type header appropriately and send JSON with a non-json // content-type. If the body looks JSON, try to handle it as as JSON instead if len(body) > 0 { var i int for { // JSON requests must start w/ an opening bracket if body[i] == '{' { h.serveWriteJSON(w, r, body, user) return } // check that the byte is in the standard ascii code range if body[i] > 32 { break } i += 1 } } precision := r.FormValue("precision") if precision == "" { precision = "n" } points, err := models.ParsePointsWithPrecision(body, time.Now().UTC(), precision) if err != nil { if err.Error() == "EOF" { w.WriteHeader(http.StatusOK) return } h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest) return } database := r.FormValue("db") if database == "" { h.writeError(w, influxql.Result{Err: fmt.Errorf("database is required")}, http.StatusBadRequest) return } if di, err := h.MetaStore.Database(database); err != nil { h.writeError(w, influxql.Result{Err: fmt.Errorf("metastore database error: %s", err)}, http.StatusInternalServerError) return } else if di == nil { h.writeError(w, influxql.Result{Err: fmt.Errorf("database not found: %q", database)}, http.StatusNotFound) return } if h.requireAuthentication && user == nil { h.writeError(w, influxql.Result{Err: fmt.Errorf("user is required to write to database %q", database)}, http.StatusUnauthorized) return } if h.requireAuthentication && !user.Authorize(influxql.WritePrivilege, database) { h.writeError(w, influxql.Result{Err: fmt.Errorf("%q user is not authorized to write to database %q", user.Name, database)}, http.StatusUnauthorized) return } // Determine required consistency level. consistency := cluster.ConsistencyLevelOne switch r.Form.Get("consistency") { case "all": consistency = cluster.ConsistencyLevelAll case "any": consistency = cluster.ConsistencyLevelAny case "one": consistency = cluster.ConsistencyLevelOne case "quorum": consistency = cluster.ConsistencyLevelQuorum } // Write points. if err := h.PointsWriter.WritePoints(&cluster.WritePointsRequest{ Database: database, RetentionPolicy: r.FormValue("rp"), ConsistencyLevel: consistency, Points: points, }); influxdb.IsClientError(err) { h.statMap.Add(statPointsWrittenFail, int64(len(points))) h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest) return } else if err != nil { h.statMap.Add(statPointsWrittenFail, int64(len(points))) h.writeError(w, influxql.Result{Err: err}, http.StatusInternalServerError) return } h.statMap.Add(statPointsWrittenOK, int64(len(points))) w.WriteHeader(http.StatusNoContent) }