2017-03-10 19:24:48 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/bouk/httprouter"
|
|
|
|
"github.com/influxdata/chronograf"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewRole adds role to source
|
|
|
|
func (h *Service) NewRole(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var req sourceRoleRequest
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
|
|
invalidJSON(w, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := req.ValidCreate(); err != nil {
|
|
|
|
invalidData(w, err, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srcID, ts, err := h.sourcesSeries(ctx, w, r)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
roles, ok := h.hasRoles(ctx, ts)
|
|
|
|
if !ok {
|
|
|
|
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-10 23:24:52 +00:00
|
|
|
if _, err := roles.Get(ctx, req.Name); err == nil {
|
|
|
|
Error(w, http.StatusBadRequest, fmt.Sprintf("Source %d already has role %s", srcID, req.Name), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-10 19:24:48 +00:00
|
|
|
res, err := roles.Add(ctx, &req.Role)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
rr := newRoleResponse(srcID, res)
|
|
|
|
w.Header().Add("Location", rr.Links.Self)
|
|
|
|
encodeJSON(w, http.StatusCreated, rr, h.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateRole changes the permissions or users of a role
|
|
|
|
func (h *Service) UpdateRole(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var req sourceRoleRequest
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
|
|
invalidJSON(w, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := req.ValidUpdate(); err != nil {
|
|
|
|
invalidData(w, err, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srcID, ts, err := h.sourcesSeries(ctx, w, r)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
roles, ok := h.hasRoles(ctx, ts)
|
|
|
|
if !ok {
|
|
|
|
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
rid := httprouter.GetParamFromContext(ctx, "rid")
|
|
|
|
req.Name = rid
|
|
|
|
|
|
|
|
if err := roles.Update(ctx, &req.Role); err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
role, err := roles.Get(ctx, req.Name)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
rr := newRoleResponse(srcID, role)
|
|
|
|
w.Header().Add("Location", rr.Links.Self)
|
|
|
|
encodeJSON(w, http.StatusOK, rr, h.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoleID retrieves a role with ID from store.
|
|
|
|
func (h *Service) RoleID(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
srcID, ts, err := h.sourcesSeries(ctx, w, r)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
roles, ok := h.hasRoles(ctx, ts)
|
|
|
|
if !ok {
|
|
|
|
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
rid := httprouter.GetParamFromContext(ctx, "rid")
|
|
|
|
role, err := roles.Get(ctx, rid)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
rr := newRoleResponse(srcID, role)
|
|
|
|
encodeJSON(w, http.StatusOK, rr, h.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Roles retrieves all roles from the store
|
|
|
|
func (h *Service) Roles(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
srcID, ts, err := h.sourcesSeries(ctx, w, r)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
store, ok := h.hasRoles(ctx, ts)
|
|
|
|
if !ok {
|
|
|
|
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
roles, err := store.All(ctx)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
rr := make([]roleResponse, len(roles))
|
|
|
|
for i, role := range roles {
|
|
|
|
rr[i] = newRoleResponse(srcID, &role)
|
|
|
|
}
|
|
|
|
|
|
|
|
res := struct {
|
|
|
|
Roles []roleResponse `json:"roles"`
|
|
|
|
}{rr}
|
|
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveRole removes role from data source.
|
|
|
|
func (h *Service) RemoveRole(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
srcID, ts, err := h.sourcesSeries(ctx, w, r)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
roles, ok := h.hasRoles(ctx, ts)
|
|
|
|
if !ok {
|
|
|
|
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
rid := httprouter.GetParamFromContext(ctx, "rid")
|
|
|
|
if err := roles.Delete(ctx, &chronograf.Role{Name: rid}); err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
|
|
|
// sourceRoleRequest is the format used for both creating and updating roles
|
|
|
|
type sourceRoleRequest struct {
|
|
|
|
chronograf.Role
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *sourceRoleRequest) ValidCreate() error {
|
|
|
|
if r.Name == "" || len(r.Name) > 254 {
|
|
|
|
return fmt.Errorf("Name is required for a role")
|
|
|
|
}
|
|
|
|
for _, user := range r.Users {
|
|
|
|
if user.Name == "" {
|
|
|
|
return fmt.Errorf("Username required")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return validPermissions(&r.Permissions)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *sourceRoleRequest) ValidUpdate() error {
|
|
|
|
if len(r.Name) > 254 {
|
|
|
|
return fmt.Errorf("Username too long; must be less than 254 characters")
|
|
|
|
}
|
|
|
|
for _, user := range r.Users {
|
|
|
|
if user.Name == "" {
|
|
|
|
return fmt.Errorf("Username required")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return validPermissions(&r.Permissions)
|
|
|
|
}
|
|
|
|
|
|
|
|
type roleResponse struct {
|
|
|
|
Users []*userResponse `json:"users"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Permissions chronograf.Permissions `json:"permissions"`
|
|
|
|
Links selfLinks `json:"links"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func newRoleResponse(srcID int, res *chronograf.Role) roleResponse {
|
|
|
|
su := make([]*userResponse, len(res.Users))
|
|
|
|
for i := range res.Users {
|
|
|
|
name := res.Users[i].Name
|
|
|
|
su[i] = newUserResponse(srcID, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
if res.Permissions == nil {
|
|
|
|
res.Permissions = make(chronograf.Permissions, 0)
|
|
|
|
}
|
|
|
|
return roleResponse{
|
|
|
|
Name: res.Name,
|
|
|
|
Permissions: res.Permissions,
|
|
|
|
Users: su,
|
|
|
|
Links: newSelfLinks(srcID, "roles", res.Name),
|
|
|
|
}
|
|
|
|
}
|