influxdb/server/users.go

181 lines
4.3 KiB
Go

package server
import (
"encoding/json"
"fmt"
"net/http"
"golang.org/x/net/context"
"github.com/influxdata/chronograf"
)
type userLinks struct {
Self string `json:"self"` // Self link mapping to this resource
Explorations string `json:"explorations"` // URL for explorations endpoint
}
type userResponse struct {
*chronograf.User
Links userLinks `json:"links"`
}
func newUserResponse(usr *chronograf.User) userResponse {
base := "/chronograf/v1/users"
return userResponse{
User: usr,
Links: userLinks{
Self: fmt.Sprintf("%s/%d", base, usr.ID),
Explorations: fmt.Sprintf("%s/%d/explorations", base, usr.ID),
},
}
}
// NewUser adds a new valid user to the store
func (h *Service) NewUser(w http.ResponseWriter, r *http.Request) {
var usr *chronograf.User
if err := json.NewDecoder(r.Body).Decode(usr); err != nil {
invalidJSON(w, h.Logger)
return
}
if err := ValidUserRequest(usr); err != nil {
invalidData(w, err, h.Logger)
return
}
var err error
if usr, err = h.UsersStore.Add(r.Context(), usr); err != nil {
msg := fmt.Errorf("error storing user %v: %v", *usr, err)
unknownErrorWithMessage(w, msg, h.Logger)
return
}
res := newUserResponse(usr)
w.Header().Add("Location", res.Links.Self)
encodeJSON(w, http.StatusCreated, res, h.Logger)
}
// UserID retrieves a user from the store
func (h *Service) UserID(w http.ResponseWriter, r *http.Request) {
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
return
}
ctx := r.Context()
usr, err := h.UsersStore.Get(ctx, chronograf.UserID(id))
if err != nil {
notFound(w, id, h.Logger)
return
}
res := newUserResponse(usr)
encodeJSON(w, http.StatusOK, res, h.Logger)
}
// RemoveUser deletes the user from the store
func (h *Service) RemoveUser(w http.ResponseWriter, r *http.Request) {
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
return
}
usr := &chronograf.User{ID: chronograf.UserID(id)}
ctx := r.Context()
if err = h.UsersStore.Delete(ctx, usr); err != nil {
unknownErrorWithMessage(w, err, h.Logger)
return
}
w.WriteHeader(http.StatusNoContent)
}
// UpdateUser handles incremental updates of a data user
func (h *Service) UpdateUser(w http.ResponseWriter, r *http.Request) {
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
return
}
ctx := r.Context()
usr, err := h.UsersStore.Get(ctx, chronograf.UserID(id))
if err != nil {
notFound(w, id, h.Logger)
return
}
var req chronograf.User
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
invalidJSON(w, h.Logger)
return
}
usr.Email = req.Email
if err := ValidUserRequest(usr); err != nil {
invalidData(w, err, h.Logger)
return
}
if err := h.UsersStore.Update(ctx, usr); err != nil {
msg := fmt.Sprintf("Error updating user ID %d", id)
Error(w, http.StatusInternalServerError, msg, h.Logger)
return
}
encodeJSON(w, http.StatusOK, newUserResponse(usr), h.Logger)
}
// ValidUserRequest checks if email is nonempty
func ValidUserRequest(s *chronograf.User) error {
// email is required
if s.Email == "" {
return fmt.Errorf("Email required")
}
return nil
}
func getEmail(ctx context.Context) (string, error) {
principal := ctx.Value(chronograf.PrincipalKey).(chronograf.Principal)
if principal == "" {
return "", fmt.Errorf("Token not found")
}
return string(principal), nil
}
// Me does a findOrCreate based on the email in the context
func (h *Service) Me(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if !h.UseAuth {
// Using status code to signal no need for authentication
w.WriteHeader(http.StatusTeapot)
return
}
email, err := getEmail(ctx)
if err != nil {
invalidData(w, err, h.Logger)
return
}
usr, err := h.UsersStore.FindByEmail(ctx, email)
if err == nil {
res := newUserResponse(usr)
encodeJSON(w, http.StatusOK, res, h.Logger)
return
}
// Because we didnt find a user, making a new one
user := &chronograf.User{
Email: email,
}
user, err = h.UsersStore.Add(ctx, user)
if err != nil {
msg := fmt.Errorf("error storing user %v: %v", user, err)
unknownErrorWithMessage(w, msg, h.Logger)
return
}
res := newUserResponse(user)
encodeJSON(w, http.StatusOK, res, h.Logger)
}