2018-05-14 16:26:38 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2018-09-06 16:19:58 +00:00
|
|
|
"fmt"
|
2019-02-01 03:10:05 +00:00
|
|
|
"net/http"
|
|
|
|
"path"
|
2020-02-06 19:58:10 +00:00
|
|
|
"strings"
|
2019-02-07 23:19:30 +00:00
|
|
|
|
2019-11-25 14:22:19 +00:00
|
|
|
"github.com/influxdata/httprouter"
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2"
|
|
|
|
"github.com/influxdata/influxdb/v2/kit/tracing"
|
|
|
|
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
|
|
|
|
"github.com/influxdata/influxdb/v2/pkg/httpc"
|
2019-12-10 03:11:53 +00:00
|
|
|
"go.uber.org/zap"
|
2018-05-14 16:26:38 +00:00
|
|
|
)
|
|
|
|
|
2019-01-16 03:34:09 +00:00
|
|
|
// OrgBackend is all services and associated parameters required to construct
|
|
|
|
// the OrgHandler.
|
|
|
|
type OrgBackend struct {
|
2019-06-27 01:33:20 +00:00
|
|
|
influxdb.HTTPErrorHandler
|
2019-12-04 23:10:23 +00:00
|
|
|
log *zap.Logger
|
2019-01-16 03:34:09 +00:00
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
OrganizationService influxdb.OrganizationService
|
|
|
|
OrganizationOperationLogService influxdb.OrganizationOperationLogService
|
|
|
|
UserResourceMappingService influxdb.UserResourceMappingService
|
|
|
|
SecretService influxdb.SecretService
|
|
|
|
LabelService influxdb.LabelService
|
|
|
|
UserService influxdb.UserService
|
2019-01-16 03:34:09 +00:00
|
|
|
}
|
|
|
|
|
2019-03-11 17:45:25 +00:00
|
|
|
// NewOrgBackend is a datasource used by the org handler.
|
2019-12-04 23:10:23 +00:00
|
|
|
func NewOrgBackend(log *zap.Logger, b *APIBackend) *OrgBackend {
|
2019-01-16 03:34:09 +00:00
|
|
|
return &OrgBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
2019-12-04 23:10:23 +00:00
|
|
|
log: log,
|
2019-01-16 03:34:09 +00:00
|
|
|
|
|
|
|
OrganizationService: b.OrganizationService,
|
|
|
|
OrganizationOperationLogService: b.OrganizationOperationLogService,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
SecretService: b.SecretService,
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
// OrgHandler represents an HTTP API handler for orgs.
|
|
|
|
type OrgHandler struct {
|
|
|
|
*httprouter.Router
|
2020-02-04 01:22:03 +00:00
|
|
|
*kithttp.API
|
2019-12-04 23:10:23 +00:00
|
|
|
log *zap.Logger
|
2018-12-20 16:07:46 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
OrgSVC influxdb.OrganizationService
|
2019-02-07 23:19:30 +00:00
|
|
|
OrganizationOperationLogService influxdb.OrganizationOperationLogService
|
|
|
|
UserResourceMappingService influxdb.UserResourceMappingService
|
|
|
|
SecretService influxdb.SecretService
|
|
|
|
LabelService influxdb.LabelService
|
|
|
|
UserService influxdb.UserService
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-27 22:50:32 +00:00
|
|
|
const (
|
2019-12-09 23:54:16 +00:00
|
|
|
prefixOrganizations = "/api/v2/orgs"
|
2018-09-27 22:50:32 +00:00
|
|
|
organizationsIDPath = "/api/v2/orgs/:id"
|
2019-03-11 17:45:25 +00:00
|
|
|
organizationsIDLogPath = "/api/v2/orgs/:id/logs"
|
2018-09-27 22:50:32 +00:00
|
|
|
organizationsIDMembersPath = "/api/v2/orgs/:id/members"
|
2018-11-14 10:54:06 +00:00
|
|
|
organizationsIDMembersIDPath = "/api/v2/orgs/:id/members/:userID"
|
2018-09-27 22:50:32 +00:00
|
|
|
organizationsIDOwnersPath = "/api/v2/orgs/:id/owners"
|
2018-11-14 10:54:06 +00:00
|
|
|
organizationsIDOwnersIDPath = "/api/v2/orgs/:id/owners/:userID"
|
2018-11-16 16:45:00 +00:00
|
|
|
organizationsIDSecretsPath = "/api/v2/orgs/:id/secrets"
|
|
|
|
// TODO(desa): need a way to specify which secrets to delete. this should work for now
|
|
|
|
organizationsIDSecretsDeletePath = "/api/v2/orgs/:id/secrets/delete"
|
2018-12-11 18:15:45 +00:00
|
|
|
organizationsIDLabelsPath = "/api/v2/orgs/:id/labels"
|
2019-01-18 19:03:36 +00:00
|
|
|
organizationsIDLabelsIDPath = "/api/v2/orgs/:id/labels/:lid"
|
2018-09-27 22:50:32 +00:00
|
|
|
)
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
func checkOrganizationExists(orgHandler *OrgHandler) kithttp.Middleware {
|
2019-10-21 21:37:30 +00:00
|
|
|
fn := func(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
2020-02-04 01:22:03 +00:00
|
|
|
orgID, err := decodeIDFromCtx(ctx, "id")
|
2019-10-21 21:37:30 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
orgHandler.API.Err(w, err)
|
2019-10-21 21:37:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
if _, err := orgHandler.OrgSVC.FindOrganizationByID(ctx, orgID); err != nil {
|
|
|
|
orgHandler.API.Err(w, err)
|
2019-10-21 21:37:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return fn
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
// NewOrgHandler returns a new instance of OrgHandler.
|
2019-12-04 23:10:23 +00:00
|
|
|
func NewOrgHandler(log *zap.Logger, b *OrgBackend) *OrgHandler {
|
2018-05-14 16:26:38 +00:00
|
|
|
h := &OrgHandler{
|
2020-02-04 01:22:03 +00:00
|
|
|
Router: NewRouter(b.HTTPErrorHandler),
|
|
|
|
API: kithttp.NewAPI(kithttp.WithLog(log)),
|
|
|
|
log: log,
|
2019-01-08 00:37:16 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
OrgSVC: b.OrganizationService,
|
2019-01-16 03:34:09 +00:00
|
|
|
OrganizationOperationLogService: b.OrganizationOperationLogService,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
SecretService: b.SecretService,
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
UserService: b.UserService,
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 23:54:16 +00:00
|
|
|
h.HandlerFunc("POST", prefixOrganizations, h.handlePostOrg)
|
|
|
|
h.HandlerFunc("GET", prefixOrganizations, h.handleGetOrgs)
|
2018-09-27 22:50:32 +00:00
|
|
|
h.HandlerFunc("GET", organizationsIDPath, h.handleGetOrg)
|
2018-11-02 18:21:14 +00:00
|
|
|
h.HandlerFunc("GET", organizationsIDLogPath, h.handleGetOrgLog)
|
2018-09-27 22:50:32 +00:00
|
|
|
h.HandlerFunc("PATCH", organizationsIDPath, h.handlePatchOrg)
|
|
|
|
h.HandlerFunc("DELETE", organizationsIDPath, h.handleDeleteOrg)
|
|
|
|
|
2019-01-17 03:35:54 +00:00
|
|
|
memberBackend := MemberBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
2019-12-04 23:10:23 +00:00
|
|
|
log: b.log.With(zap.String("handler", "member")),
|
2019-02-07 23:19:30 +00:00
|
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
|
|
UserType: influxdb.Member,
|
2019-01-17 03:35:54 +00:00
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
}
|
|
|
|
h.HandlerFunc("POST", organizationsIDMembersPath, newPostMemberHandler(memberBackend))
|
2020-02-04 01:22:03 +00:00
|
|
|
h.Handler("GET", organizationsIDMembersPath, applyMW(newGetMembersHandler(memberBackend), checkOrganizationExists(h)))
|
2019-01-17 03:35:54 +00:00
|
|
|
h.HandlerFunc("DELETE", organizationsIDMembersIDPath, newDeleteMemberHandler(memberBackend))
|
|
|
|
|
|
|
|
ownerBackend := MemberBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
2019-12-04 23:10:23 +00:00
|
|
|
log: b.log.With(zap.String("handler", "member")),
|
2019-02-07 23:19:30 +00:00
|
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
|
|
UserType: influxdb.Owner,
|
2019-01-17 03:35:54 +00:00
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
}
|
|
|
|
h.HandlerFunc("POST", organizationsIDOwnersPath, newPostMemberHandler(ownerBackend))
|
2020-02-04 01:22:03 +00:00
|
|
|
h.Handler("GET", organizationsIDOwnersPath, applyMW(newGetMembersHandler(ownerBackend), checkOrganizationExists(h)))
|
2019-01-17 03:35:54 +00:00
|
|
|
h.HandlerFunc("DELETE", organizationsIDOwnersIDPath, newDeleteMemberHandler(ownerBackend))
|
2018-09-27 22:50:32 +00:00
|
|
|
|
2018-11-16 16:45:00 +00:00
|
|
|
h.HandlerFunc("GET", organizationsIDSecretsPath, h.handleGetSecrets)
|
|
|
|
h.HandlerFunc("PATCH", organizationsIDSecretsPath, h.handlePatchSecrets)
|
|
|
|
// TODO(desa): need a way to specify which secrets to delete. this should work for now
|
|
|
|
h.HandlerFunc("POST", organizationsIDSecretsDeletePath, h.handleDeleteSecrets)
|
|
|
|
|
2019-01-17 04:33:09 +00:00
|
|
|
labelBackend := &LabelBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
2019-12-04 23:10:23 +00:00
|
|
|
log: b.log.With(zap.String("handler", "label")),
|
2019-06-27 01:33:20 +00:00
|
|
|
LabelService: b.LabelService,
|
|
|
|
ResourceType: influxdb.OrgsResourceType,
|
2019-01-17 04:33:09 +00:00
|
|
|
}
|
|
|
|
h.HandlerFunc("GET", organizationsIDLabelsPath, newGetLabelsHandler(labelBackend))
|
|
|
|
h.HandlerFunc("POST", organizationsIDLabelsPath, newPostLabelHandler(labelBackend))
|
|
|
|
h.HandlerFunc("DELETE", organizationsIDLabelsIDPath, newDeleteLabelHandler(labelBackend))
|
2018-12-11 18:15:45 +00:00
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
2018-09-11 07:32:43 +00:00
|
|
|
type orgsResponse struct {
|
|
|
|
Links map[string]string `json:"links"`
|
2020-02-04 01:22:03 +00:00
|
|
|
Organizations []orgResponse `json:"orgs"`
|
2018-09-11 07:32:43 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
func (o orgsResponse) toInfluxdb() []*influxdb.Organization {
|
2019-02-07 23:19:30 +00:00
|
|
|
orgs := make([]*influxdb.Organization, len(o.Organizations))
|
2018-09-14 23:34:40 +00:00
|
|
|
for i := range o.Organizations {
|
|
|
|
orgs[i] = &o.Organizations[i].Organization
|
|
|
|
}
|
|
|
|
return orgs
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func newOrgsResponse(orgs []*influxdb.Organization) *orgsResponse {
|
2018-09-11 07:32:43 +00:00
|
|
|
res := orgsResponse{
|
|
|
|
Links: map[string]string{
|
2018-09-26 08:49:19 +00:00
|
|
|
"self": "/api/v2/orgs",
|
2018-09-11 07:32:43 +00:00
|
|
|
},
|
2020-02-04 01:22:03 +00:00
|
|
|
Organizations: []orgResponse{},
|
2018-09-11 07:32:43 +00:00
|
|
|
}
|
|
|
|
for _, org := range orgs {
|
2020-02-04 01:22:03 +00:00
|
|
|
res.Organizations = append(res.Organizations, newOrgResponse(*org))
|
2018-09-11 07:32:43 +00:00
|
|
|
}
|
|
|
|
return &res
|
|
|
|
}
|
|
|
|
|
|
|
|
type orgResponse struct {
|
|
|
|
Links map[string]string `json:"links"`
|
2019-02-07 23:19:30 +00:00
|
|
|
influxdb.Organization
|
2018-09-11 07:32:43 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
func newOrgResponse(o influxdb.Organization) orgResponse {
|
|
|
|
return orgResponse{
|
2018-09-11 07:32:43 +00:00
|
|
|
Links: map[string]string{
|
2018-09-26 08:49:19 +00:00
|
|
|
"self": fmt.Sprintf("/api/v2/orgs/%s", o.ID),
|
2019-03-11 17:45:25 +00:00
|
|
|
"logs": fmt.Sprintf("/api/v2/orgs/%s/logs", o.ID),
|
2018-09-26 08:49:19 +00:00
|
|
|
"members": fmt.Sprintf("/api/v2/orgs/%s/members", o.ID),
|
2019-02-26 17:08:30 +00:00
|
|
|
"owners": fmt.Sprintf("/api/v2/orgs/%s/owners", o.ID),
|
2018-12-27 18:51:31 +00:00
|
|
|
"secrets": fmt.Sprintf("/api/v2/orgs/%s/secrets", o.ID),
|
|
|
|
"labels": fmt.Sprintf("/api/v2/orgs/%s/labels", o.ID),
|
2018-09-26 08:49:19 +00:00
|
|
|
"buckets": fmt.Sprintf("/api/v2/buckets?org=%s", o.Name),
|
|
|
|
"tasks": fmt.Sprintf("/api/v2/tasks?org=%s", o.Name),
|
|
|
|
"dashboards": fmt.Sprintf("/api/v2/dashboards?org=%s", o.Name),
|
2018-09-11 07:32:43 +00:00
|
|
|
},
|
2020-02-04 01:22:03 +00:00
|
|
|
Organization: o,
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handlePostOrg is the HTTP handler for the POST /api/v2/orgs route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *OrgHandler) handlePostOrg(w http.ResponseWriter, r *http.Request) {
|
2020-02-04 01:22:03 +00:00
|
|
|
var org influxdb.Organization
|
|
|
|
if err := h.API.DecodeJSON(r.Body, &org); err != nil {
|
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
if err := h.OrgSVC.CreateOrganization(r.Context(), &org); err != nil {
|
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
2020-02-04 01:22:03 +00:00
|
|
|
h.log.Debug("Org created", zap.String("org", fmt.Sprint(org)))
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusCreated, newOrgResponse(org))
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleGetOrg is the HTTP handler for the GET /api/v2/orgs/:id route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *OrgHandler) handleGetOrg(w http.ResponseWriter, r *http.Request) {
|
2020-02-04 01:22:03 +00:00
|
|
|
id, err := decodeIDFromCtx(r.Context(), "id")
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
org, err := h.OrgSVC.FindOrganizationByID(r.Context(), id)
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
2020-02-04 01:22:03 +00:00
|
|
|
h.log.Debug("Org retrieved", zap.String("org", fmt.Sprint(org)))
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusOK, newOrgResponse(*org))
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleGetOrgs is the HTTP handler for the GET /api/v2/orgs route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *OrgHandler) handleGetOrgs(w http.ResponseWriter, r *http.Request) {
|
2020-02-04 01:22:03 +00:00
|
|
|
var filter influxdb.OrganizationFilter
|
|
|
|
qp := r.URL.Query()
|
|
|
|
if name := qp.Get(Org); name != "" {
|
|
|
|
filter.Name = &name
|
|
|
|
}
|
2020-03-27 14:56:22 +00:00
|
|
|
if orgID := qp.Get("orgID"); orgID != "" {
|
|
|
|
id, err := influxdb.IDFromString(orgID)
|
|
|
|
if err != nil {
|
|
|
|
h.API.Err(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
filter.ID = id
|
2020-02-04 01:22:03 +00:00
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
orgs, _, err := h.OrgSVC.FindOrganizations(r.Context(), filter)
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
2019-12-04 23:10:23 +00:00
|
|
|
h.log.Debug("Orgs retrieved", zap.String("org", fmt.Sprint(orgs)))
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusOK, newOrgsResponse(orgs))
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleDeleteOrganization is the HTTP handler for the DELETE /api/v2/orgs/:id route.
|
2018-05-16 18:59:35 +00:00
|
|
|
func (h *OrgHandler) handleDeleteOrg(w http.ResponseWriter, r *http.Request) {
|
2020-02-04 01:22:03 +00:00
|
|
|
id, err := decodeIDFromCtx(r.Context(), "id")
|
2018-05-16 18:59:35 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
if err := h.OrgSVC.DeleteOrganization(ctx, id); err != nil {
|
|
|
|
h.API.Err(w, err)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
2020-02-04 01:22:03 +00:00
|
|
|
h.log.Debug("Org deleted", zap.String("orgID", fmt.Sprint(id)))
|
2018-05-16 18:59:35 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusNoContent, nil)
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handlePatchOrg is the HTTP handler for the PATH /api/v2/orgs route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *OrgHandler) handlePatchOrg(w http.ResponseWriter, r *http.Request) {
|
2020-02-04 01:22:03 +00:00
|
|
|
id, err := decodeIDFromCtx(r.Context(), "id")
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
var upd influxdb.OrganizationUpdate
|
|
|
|
if err := h.API.DecodeJSON(r.Body, &upd); err != nil {
|
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
org, err := h.OrgSVC.UpdateOrganization(r.Context(), id, upd)
|
|
|
|
if err != nil {
|
|
|
|
h.API.Err(w, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
2020-02-04 01:22:03 +00:00
|
|
|
h.log.Debug("Org updated", zap.String("org", fmt.Sprint(org)))
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusOK, newOrgResponse(*org))
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
type secretsResponse struct {
|
|
|
|
Links map[string]string `json:"links"`
|
|
|
|
Secrets []string `json:"secrets"`
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
func newSecretsResponse(orgID influxdb.ID, ks []string) *secretsResponse {
|
|
|
|
if ks == nil {
|
|
|
|
ks = []string{}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
2020-02-04 01:22:03 +00:00
|
|
|
return &secretsResponse{
|
|
|
|
Links: map[string]string{
|
|
|
|
"org": fmt.Sprintf("/api/v2/orgs/%s", orgID),
|
|
|
|
"self": fmt.Sprintf("/api/v2/orgs/%s/secrets", orgID),
|
|
|
|
},
|
|
|
|
Secrets: ks,
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-16 16:45:00 +00:00
|
|
|
// handleGetSecrets is the HTTP handler for the GET /api/v2/orgs/:id/secrets route.
|
|
|
|
func (h *OrgHandler) handleGetSecrets(w http.ResponseWriter, r *http.Request) {
|
2020-02-04 01:22:03 +00:00
|
|
|
orgID, err := decodeIDFromCtx(r.Context(), "id")
|
2018-11-16 16:45:00 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-11-16 16:45:00 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
ks, err := h.SecretService.GetSecretKeys(r.Context(), orgID)
|
2019-12-04 21:29:05 +00:00
|
|
|
if err != nil && influxdb.ErrorCode(err) != influxdb.ENotFound {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-11-16 16:45:00 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusOK, newSecretsResponse(orgID, ks))
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// handleGetPatchSecrets is the HTTP handler for the PATCH /api/v2/orgs/:id/secrets route.
|
|
|
|
func (h *OrgHandler) handlePatchSecrets(w http.ResponseWriter, r *http.Request) {
|
2020-02-04 01:22:03 +00:00
|
|
|
orgID, err := decodeIDFromCtx(r.Context(), "id")
|
2018-11-16 16:45:00 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-11-16 16:45:00 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
var secrets map[string]string
|
|
|
|
if err := h.API.DecodeJSON(r.Body, &secrets); err != nil {
|
|
|
|
h.API.Err(w, err)
|
2018-11-16 16:45:00 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
if err := h.SecretService.PatchSecrets(r.Context(), orgID, secrets); err != nil {
|
|
|
|
h.API.Err(w, err)
|
|
|
|
return
|
|
|
|
}
|
2018-11-16 16:45:00 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusNoContent, nil)
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-06 19:58:10 +00:00
|
|
|
type secretsDeleteBody struct {
|
|
|
|
Secrets []string `json:"secrets"`
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
// handleDeleteSecrets is the HTTP handler for the DELETE /api/v2/orgs/:id/secrets route.
|
|
|
|
func (h *OrgHandler) handleDeleteSecrets(w http.ResponseWriter, r *http.Request) {
|
|
|
|
orgID, err := decodeIDFromCtx(r.Context(), "id")
|
|
|
|
if err != nil {
|
|
|
|
h.API.Err(w, err)
|
|
|
|
return
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-06 19:58:10 +00:00
|
|
|
var reqBody secretsDeleteBody
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
if err := h.API.DecodeJSON(r.Body, &reqBody); err != nil {
|
|
|
|
h.API.Err(w, err)
|
|
|
|
return
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
if err := h.SecretService.DeleteSecret(r.Context(), orgID, reqBody.Secrets...); err != nil {
|
|
|
|
h.API.Err(w, err)
|
|
|
|
return
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusNoContent, nil)
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
// hanldeGetOrganizationLog retrieves a organization log by the organizations ID.
|
|
|
|
func (h *OrgHandler) handleGetOrgLog(w http.ResponseWriter, r *http.Request) {
|
|
|
|
orgID, err := decodeIDFromCtx(r.Context(), "id")
|
2018-11-16 16:45:00 +00:00
|
|
|
if err != nil {
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Err(w, err)
|
2018-11-16 16:45:00 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
opts, err := decodeFindOptions(r)
|
|
|
|
if err != nil {
|
|
|
|
h.API.Err(w, err)
|
2018-11-16 16:45:00 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
log, _, err := h.OrganizationOperationLogService.GetOrganizationOperationLog(r.Context(), orgID, *opts)
|
|
|
|
if err != nil {
|
|
|
|
h.API.Err(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h.log.Debug("Org logs retrieved", zap.String("log", fmt.Sprint(log)))
|
2018-11-16 16:45:00 +00:00
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
h.API.Respond(w, http.StatusOK, newOrganizationLogResponse(orgID, log))
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
func newOrganizationLogResponse(id influxdb.ID, es []*influxdb.OperationLogEntry) *operationLogResponse {
|
|
|
|
logs := make([]*operationLogEntryResponse, 0, len(es))
|
|
|
|
for _, e := range es {
|
|
|
|
logs = append(logs, newOperationLogEntryResponse(e))
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
2020-02-04 01:22:03 +00:00
|
|
|
return &operationLogResponse{
|
|
|
|
Links: map[string]string{
|
|
|
|
"self": fmt.Sprintf("/api/v2/orgs/%s/logs", id),
|
|
|
|
},
|
|
|
|
Logs: logs,
|
2018-11-16 16:45:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 19:58:10 +00:00
|
|
|
// SecretService connects to Influx via HTTP using tokens to manage secrets.
|
|
|
|
type SecretService struct {
|
|
|
|
Client *httpc.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadSecret is not implemented for http
|
|
|
|
func (s *SecretService) LoadSecret(ctx context.Context, orgID influxdb.ID, k string) (string, error) {
|
|
|
|
return "", &influxdb.Error{
|
|
|
|
Code: influxdb.EMethodNotAllowed,
|
|
|
|
Msg: "load secret is not implemented for http",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PutSecret is not implemented for http.
|
|
|
|
func (s *SecretService) PutSecret(ctx context.Context, orgID influxdb.ID, k string, v string) error {
|
|
|
|
return &influxdb.Error{
|
|
|
|
Code: influxdb.EMethodNotAllowed,
|
|
|
|
Msg: "put secret is not implemented for http",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSecretKeys get all secret keys mathing an org ID via HTTP.
|
|
|
|
func (s *SecretService) GetSecretKeys(ctx context.Context, orgID influxdb.ID) ([]string, error) {
|
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
|
|
|
defer span.Finish()
|
|
|
|
|
|
|
|
span.LogKV("org-id", orgID)
|
|
|
|
|
|
|
|
path := strings.Replace(organizationsIDSecretsPath, ":id", orgID.String(), 1)
|
|
|
|
|
|
|
|
var ss secretsResponse
|
|
|
|
err := s.Client.
|
|
|
|
Get(path).
|
|
|
|
DecodeJSON(&ss).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ss.Secrets, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PutSecrets is not implemented for http.
|
|
|
|
func (s *SecretService) PutSecrets(ctx context.Context, orgID influxdb.ID, m map[string]string) error {
|
|
|
|
return &influxdb.Error{
|
|
|
|
Code: influxdb.EMethodNotAllowed,
|
|
|
|
Msg: "put secrets is not implemented for http",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PatchSecrets will update the existing secret with new via http.
|
|
|
|
func (s *SecretService) PatchSecrets(ctx context.Context, orgID influxdb.ID, m map[string]string) error {
|
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
|
|
|
defer span.Finish()
|
|
|
|
|
|
|
|
if orgID != 0 {
|
|
|
|
span.LogKV("org-id", orgID)
|
|
|
|
}
|
|
|
|
|
|
|
|
path := strings.Replace(organizationsIDSecretsPath, ":id", orgID.String(), 1)
|
|
|
|
|
|
|
|
return s.Client.
|
|
|
|
PatchJSON(m, path).
|
|
|
|
Do(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteSecret removes a single secret via HTTP.
|
|
|
|
func (s *SecretService) DeleteSecret(ctx context.Context, orgID influxdb.ID, ks ...string) error {
|
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
|
|
|
defer span.Finish()
|
|
|
|
|
|
|
|
path := strings.Replace(organizationsIDSecretsDeletePath, ":id", orgID.String(), 1)
|
|
|
|
return s.Client.
|
|
|
|
PostJSON(secretsDeleteBody{
|
|
|
|
Secrets: ks,
|
|
|
|
}, path).
|
|
|
|
Do(ctx)
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
// OrganizationService connects to Influx via HTTP using tokens to manage organizations.
|
|
|
|
type OrganizationService struct {
|
2019-12-10 03:11:53 +00:00
|
|
|
Client *httpc.Client
|
2018-12-05 14:57:26 +00:00
|
|
|
// OpPrefix is for not found errors.
|
|
|
|
OpPrefix string
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:34:40 +00:00
|
|
|
// FindOrganizationByID gets a single organization with a given id using HTTP.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *OrganizationService) FindOrganizationByID(ctx context.Context, id influxdb.ID) (*influxdb.Organization, error) {
|
|
|
|
filter := influxdb.OrganizationFilter{ID: &id}
|
2018-12-05 14:57:26 +00:00
|
|
|
o, err := s.FindOrganization(ctx, filter)
|
|
|
|
if err != nil {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
2018-12-05 14:57:26 +00:00
|
|
|
Err: err,
|
2019-02-07 23:19:30 +00:00
|
|
|
Op: s.OpPrefix + influxdb.OpFindOrganizationByID,
|
2018-12-05 14:57:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return o, nil
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:34:40 +00:00
|
|
|
// FindOrganization gets a single organization matching the filter using HTTP.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *OrganizationService) FindOrganization(ctx context.Context, filter influxdb.OrganizationFilter) (*influxdb.Organization, error) {
|
2019-02-21 04:42:29 +00:00
|
|
|
if filter.ID == nil && filter.Name == nil {
|
2019-04-26 16:22:42 +00:00
|
|
|
return nil, influxdb.ErrInvalidOrgFilter
|
2019-02-21 04:42:29 +00:00
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
os, n, err := s.FindOrganizations(ctx, filter)
|
|
|
|
if err != nil {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
2018-12-05 14:57:26 +00:00
|
|
|
Err: err,
|
2019-02-07 23:19:30 +00:00
|
|
|
Op: s.OpPrefix + influxdb.OpFindOrganization,
|
2018-12-05 14:57:26 +00:00
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 22:53:57 +00:00
|
|
|
if n == 0 {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.ENotFound,
|
|
|
|
Op: s.OpPrefix + influxdb.OpFindOrganization,
|
2018-12-05 14:57:26 +00:00
|
|
|
Msg: "organization not found",
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return os[0], nil
|
|
|
|
}
|
|
|
|
|
2018-09-14 23:34:40 +00:00
|
|
|
// FindOrganizations returns all organizations that match the filter via HTTP.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *OrganizationService) FindOrganizations(ctx context.Context, filter influxdb.OrganizationFilter, opt ...influxdb.FindOptions) ([]*influxdb.Organization, int, error) {
|
2019-03-06 00:18:04 +00:00
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
2019-03-05 00:38:10 +00:00
|
|
|
defer span.Finish()
|
|
|
|
|
2019-12-10 03:11:53 +00:00
|
|
|
params := findOptionParams(opt...)
|
2019-03-05 00:38:10 +00:00
|
|
|
if filter.Name != nil {
|
|
|
|
span.LogKV("org", *filter.Name)
|
2019-12-10 03:11:53 +00:00
|
|
|
params = append(params, [2]string{"org", *filter.Name})
|
2019-03-05 00:38:10 +00:00
|
|
|
}
|
|
|
|
if filter.ID != nil {
|
|
|
|
span.LogKV("org-id", *filter.ID)
|
2019-12-10 03:11:53 +00:00
|
|
|
params = append(params, [2]string{"orgID", filter.ID.String()})
|
2019-03-05 00:38:10 +00:00
|
|
|
}
|
|
|
|
for _, o := range opt {
|
|
|
|
if o.Offset != 0 {
|
|
|
|
span.LogKV("offset", o.Offset)
|
|
|
|
}
|
|
|
|
span.LogKV("descending", o.Descending)
|
|
|
|
if o.Limit > 0 {
|
|
|
|
span.LogKV("limit", o.Limit)
|
|
|
|
}
|
|
|
|
if o.SortBy != "" {
|
|
|
|
span.LogKV("sortBy", o.SortBy)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-14 23:34:40 +00:00
|
|
|
var os orgsResponse
|
2019-12-10 03:11:53 +00:00
|
|
|
err := s.Client.
|
2020-02-04 01:22:03 +00:00
|
|
|
Get(prefixOrganizations).
|
2019-12-10 03:11:53 +00:00
|
|
|
QueryParams(params...).
|
|
|
|
DecodeJSON(&os).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 01:22:03 +00:00
|
|
|
orgs := os.toInfluxdb()
|
2018-09-14 23:34:40 +00:00
|
|
|
return orgs, len(orgs), nil
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateOrganization creates an organization.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *OrganizationService) CreateOrganization(ctx context.Context, o *influxdb.Organization) error {
|
2019-03-06 00:18:04 +00:00
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
2019-03-05 00:38:10 +00:00
|
|
|
defer span.Finish()
|
|
|
|
|
|
|
|
if o.Name != "" {
|
|
|
|
span.LogKV("org", o.Name)
|
|
|
|
}
|
|
|
|
if o.ID != 0 {
|
|
|
|
span.LogKV("org-id", o.ID)
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:11:53 +00:00
|
|
|
return s.Client.
|
2020-02-04 01:22:03 +00:00
|
|
|
PostJSON(o, prefixOrganizations).
|
2019-12-10 03:11:53 +00:00
|
|
|
DecodeJSON(o).
|
|
|
|
Do(ctx)
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:34:40 +00:00
|
|
|
// UpdateOrganization updates the organization over HTTP.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *OrganizationService) UpdateOrganization(ctx context.Context, id influxdb.ID, upd influxdb.OrganizationUpdate) (*influxdb.Organization, error) {
|
2019-03-06 00:18:04 +00:00
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
2019-03-05 00:38:10 +00:00
|
|
|
defer span.Finish()
|
|
|
|
|
|
|
|
span.LogKV("org-id", id)
|
|
|
|
span.LogKV("name", upd.Name)
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
var o influxdb.Organization
|
2019-12-10 03:11:53 +00:00
|
|
|
err := s.Client.
|
2020-02-04 01:22:03 +00:00
|
|
|
PatchJSON(upd, prefixOrganizations, id.String()).
|
2019-12-10 03:11:53 +00:00
|
|
|
DecodeJSON(&o).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
2019-03-05 00:38:10 +00:00
|
|
|
return nil, tracing.LogError(span, err)
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &o, nil
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:34:40 +00:00
|
|
|
// DeleteOrganization removes organization id over HTTP.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *OrganizationService) DeleteOrganization(ctx context.Context, id influxdb.ID) error {
|
2019-03-06 00:18:04 +00:00
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
2019-03-05 00:38:10 +00:00
|
|
|
defer span.Finish()
|
|
|
|
|
2019-12-10 03:11:53 +00:00
|
|
|
return s.Client.
|
2020-02-04 01:22:03 +00:00
|
|
|
Delete(prefixOrganizations, id.String()).
|
2019-12-10 03:11:53 +00:00
|
|
|
Do(ctx)
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func organizationIDPath(id influxdb.ID) string {
|
2020-02-04 01:22:03 +00:00
|
|
|
return path.Join(prefixOrganizations, id.String())
|
2018-11-02 18:21:14 +00:00
|
|
|
}
|