chore(platform): refactor platform http handler to allow for auth

pull/10616/head
Michael Desa 2018-10-02 12:00:29 -04:00
parent aa25727979
commit 0cf834e532
3 changed files with 291 additions and 232 deletions

View File

@ -35,7 +35,6 @@ import (
taskbolt "github.com/influxdata/platform/task/backend/bolt"
"github.com/influxdata/platform/task/backend/coordinator"
taskexecutor "github.com/influxdata/platform/task/backend/executor"
pzap "github.com/influxdata/platform/zap"
"github.com/prometheus/client_golang/prometheus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -174,9 +173,9 @@ func platformF(cmd *cobra.Command, args []string) {
dashboardSvc = c
}
var cellSvc platform.ViewService
var viewSvc platform.ViewService
{
cellSvc = c
viewSvc = c
}
var sourceSvc platform.SourceService
@ -286,87 +285,34 @@ func platformF(cmd *cobra.Command, args []string) {
Addr: httpBindAddress,
}
handlerConfig := &http.APIBackend{
Logger: logger,
NewBucketService: source.NewBucketService,
NewQueryService: source.NewQueryService,
PublisherFn: func(r io.Reader) error {
return publisher.Publish(IngressSubject, r)
},
AuthorizationService: authSvc,
BucketService: bucketSvc,
SessionService: sessionSvc,
UserService: userSvc,
OrganizationService: orgSvc,
UserResourceMappingService: userResourceSvc,
DashboardService: dashboardSvc,
ViewService: viewSvc,
SourceService: sourceSvc,
MacroService: macroSvc,
BasicAuthService: basicAuthSvc,
OnboardingService: onboardingSvc,
QueryService: queryService,
TaskService: taskSvc,
ScraperTargetStoreService: scraperTargetSvc,
ChronografService: chronografSvc,
}
// HTTP server
go func() {
sessionHandler := http.NewSessionHandler()
sessionHandler.BasicAuthService = basicAuthSvc
sessionHandler.SessionService = sessionSvc
bucketHandler := http.NewBucketHandler()
bucketHandler.BucketService = bucketSvc
orgHandler := http.NewOrgHandler()
orgHandler.OrganizationService = orgSvc
orgHandler.BucketService = bucketSvc
orgHandler.UserResourceMappingService = userResourceSvc
userHandler := http.NewUserHandler()
userHandler.UserService = userSvc
dashboardHandler := http.NewDashboardHandler()
dashboardHandler.DashboardService = dashboardSvc
cellHandler := http.NewViewHandler()
cellHandler.ViewService = cellSvc
macroHandler := http.NewMacroHandler()
macroHandler.MacroService = macroSvc
authHandler := http.NewAuthorizationHandler()
authHandler.AuthorizationService = authSvc
authHandler.Logger = logger.With(zap.String("handler", "auth"))
assetHandler := http.NewAssetHandler()
assetHandler.Develop = developerMode
fluxLangHandler := http.NewFluxLangHandler()
sourceHandler := http.NewSourceHandler()
sourceHandler.SourceService = sourceSvc
sourceHandler.NewBucketService = source.NewBucketService
sourceHandler.NewQueryService = source.NewQueryService
setupHandler := http.NewSetupHandler()
setupHandler.OnboardingService = onboardingSvc
taskHandler := http.NewTaskHandler(logger)
taskHandler.TaskService = taskSvc
publishFn := func(r io.Reader) error {
return publisher.Publish(IngressSubject, r)
}
writeHandler := http.NewWriteHandler(publishFn)
writeHandler.AuthorizationService = authSvc
writeHandler.OrganizationService = orgSvc
writeHandler.BucketService = bucketSvc
writeHandler.Logger = logger.With(zap.String("handler", "write"))
queryHandler := http.NewFluxHandler()
queryHandler.AuthorizationService = authSvc
queryHandler.OrganizationService = orgSvc
queryHandler.Logger = logger.With(zap.String("handler", "query"))
queryHandler.ProxyQueryService = pzap.NewProxyQueryService(queryHandler.Logger)
// TODO(desa): what to do about idpe.
chronografHandler := http.NewChronografHandler(chronografSvc)
platformHandler := &http.PlatformHandler{
BucketHandler: bucketHandler,
OrgHandler: orgHandler,
UserHandler: userHandler,
AuthorizationHandler: authHandler,
DashboardHandler: dashboardHandler,
AssetHandler: assetHandler,
FluxLangHandler: fluxLangHandler,
ChronografHandler: chronografHandler,
SourceHandler: sourceHandler,
TaskHandler: taskHandler,
ViewHandler: cellHandler,
MacroHandler: macroHandler,
QueryHandler: queryHandler,
WriteHandler: writeHandler,
SetupHandler: setupHandler,
}
platformHandler := http.NewPlatformHandler(handlerConfig)
reg.MustRegister(platformHandler.PrometheusCollectors()...)
h := http.NewHandlerFromRegistry("platform", reg)

249
http/api_handler.go Normal file
View File

@ -0,0 +1,249 @@
package http
import (
"io"
http "net/http"
"strings"
"github.com/influxdata/platform"
"github.com/influxdata/platform/chronograf/server"
"github.com/influxdata/platform/query"
pzap "github.com/influxdata/platform/zap"
"go.uber.org/zap"
)
// APIHandler is a collection of all the service handlers.
type APIHandler struct {
BucketHandler *BucketHandler
UserHandler *UserHandler
OrgHandler *OrgHandler
AuthorizationHandler *AuthorizationHandler
DashboardHandler *DashboardHandler
AssetHandler *AssetHandler
ChronografHandler *ChronografHandler
ViewHandler *ViewHandler
SourceHandler *SourceHandler
MacroHandler *MacroHandler
TaskHandler *TaskHandler
FluxLangHandler *FluxLangHandler
QueryHandler *FluxHandler
WriteHandler *WriteHandler
SetupHandler *SetupHandler
SessionHandler *SessionHandler
}
// APIBackend is all services and associated parameters required to construct
// an APIHandler.
type APIBackend struct {
Logger *zap.Logger
NewBucketService func(*platform.Source) (platform.BucketService, error)
NewQueryService func(*platform.Source) (query.ProxyQueryService, error)
PublisherFn func(r io.Reader) error
AuthorizationService platform.AuthorizationService
BucketService platform.BucketService
SessionService platform.SessionService
UserService platform.UserService
OrganizationService platform.OrganizationService
UserResourceMappingService platform.UserResourceMappingService
DashboardService platform.DashboardService
ViewService platform.ViewService
SourceService platform.SourceService
MacroService platform.MacroService
BasicAuthService platform.BasicAuthService
OnboardingService platform.OnboardingService
QueryService query.QueryService
TaskService platform.TaskService
ScraperTargetStoreService platform.ScraperTargetStoreService
ChronografService *server.Service
}
// NewAPIHandler constructs all api handlers beneath it and returns an APIHandler
func NewAPIHandler(b *APIBackend) *APIHandler {
h := &APIHandler{}
h.SessionHandler = NewSessionHandler()
h.SessionHandler.BasicAuthService = b.BasicAuthService
h.SessionHandler.SessionService = b.SessionService
h.BucketHandler = NewBucketHandler()
h.BucketHandler.BucketService = b.BucketService
h.OrgHandler = NewOrgHandler()
h.OrgHandler.OrganizationService = b.OrganizationService
h.OrgHandler.BucketService = b.BucketService
h.OrgHandler.UserResourceMappingService = b.UserResourceMappingService
h.UserHandler = NewUserHandler()
h.UserHandler.UserService = b.UserService
h.DashboardHandler = NewDashboardHandler()
h.DashboardHandler.DashboardService = b.DashboardService
h.ViewHandler = NewViewHandler()
h.ViewHandler.ViewService = b.ViewService
h.MacroHandler = NewMacroHandler()
h.MacroHandler.MacroService = b.MacroService
h.AuthorizationHandler = NewAuthorizationHandler()
h.AuthorizationHandler.AuthorizationService = b.AuthorizationService
h.AuthorizationHandler.Logger = b.Logger.With(zap.String("handler", "auth"))
h.FluxLangHandler = NewFluxLangHandler()
h.SourceHandler = NewSourceHandler()
h.SourceHandler.SourceService = b.SourceService
h.SourceHandler.NewBucketService = b.NewBucketService
h.SourceHandler.NewQueryService = b.NewQueryService
h.SetupHandler = NewSetupHandler()
h.SetupHandler.OnboardingService = b.OnboardingService
h.TaskHandler = NewTaskHandler(b.Logger)
h.TaskHandler.TaskService = b.TaskService
h.WriteHandler = NewWriteHandler(b.PublisherFn)
h.WriteHandler.AuthorizationService = b.AuthorizationService
h.WriteHandler.OrganizationService = b.OrganizationService
h.WriteHandler.BucketService = b.BucketService
h.WriteHandler.Logger = b.Logger.With(zap.String("handler", "write"))
h.QueryHandler = NewFluxHandler()
h.QueryHandler.AuthorizationService = b.AuthorizationService
h.QueryHandler.OrganizationService = b.OrganizationService
h.QueryHandler.Logger = b.Logger.With(zap.String("handler", "query"))
h.QueryHandler.ProxyQueryService = pzap.NewProxyQueryService(h.QueryHandler.Logger)
h.ChronografHandler = NewChronografHandler(b.ChronografService)
return h
}
var apiLinks = map[string]interface{}{
"signin": "/api/v2/signin",
"signout": "/api/v2/singout",
"setup": "/api/v2/setup",
"sources": "/api/v2/sources",
"dashboards": "/api/v2/dashboards",
"query": "/api/v2/query",
"write": "/api/v2/write",
"orgs": "/api/v2/orgs",
"auths": "/api/v2/authorizations",
"buckets": "/api/v2/buckets",
"users": "/api/v2/users",
"tasks": "/api/v2/tasks",
"flux": map[string]string{
"self": "/api/v2/flux",
"ast": "/api/v2/flux/ast",
"suggestions": "/api/v2/flux/suggestions",
},
"external": map[string]string{
"statusFeed": "https://www.influxdata.com/feed/json",
},
"system": map[string]string{
"metrics": "/metrics",
"debug": "/debug/pprof",
"health": "/healthz",
},
}
func (h *APIHandler) serveLinks(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if err := encodeResponse(ctx, w, http.StatusOK, apiLinks); err != nil {
EncodeError(ctx, err, w)
return
}
}
// ServeHTTP delegates a request to the appropriate subhandler.
func (h *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
setCORSResponseHeaders(w, r)
if r.Method == "OPTIONS" {
return
}
// Serve the links base links for the API.
if r.URL.Path == "/api/v2/" || r.URL.Path == "/api/v2" {
h.serveLinks(w, r)
return
}
if r.URL.Path == "/api/v2/signin" || r.URL.Path == "/api/v2/signout" {
h.SessionHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/setup") {
h.SetupHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/write") {
h.WriteHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/query") {
h.QueryHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/buckets") {
h.BucketHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/users") {
h.UserHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/orgs") {
h.OrgHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/authorizations") {
h.AuthorizationHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/dashboards") {
h.DashboardHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/sources") {
h.SourceHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/tasks") {
h.TaskHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/views") {
h.ViewHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/macros") {
h.MacroHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/flux") {
h.FluxLangHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/chronograf/") {
h.ChronografHandler.ServeHTTP(w, r)
return
}
http.NotFound(w, r)
}

View File

@ -1,35 +1,19 @@
package http
import (
"context"
nethttp "net/http"
"net/http"
"strings"
idpctx "github.com/influxdata/platform/context"
"github.com/prometheus/client_golang/prometheus"
)
// PlatformHandler is a collection of all the service handlers.
type PlatformHandler struct {
BucketHandler *BucketHandler
UserHandler *UserHandler
OrgHandler *OrgHandler
AuthorizationHandler *AuthorizationHandler
DashboardHandler *DashboardHandler
AssetHandler *AssetHandler
ChronografHandler *ChronografHandler
ViewHandler *ViewHandler
SourceHandler *SourceHandler
MacroHandler *MacroHandler
TaskHandler *TaskHandler
FluxLangHandler *FluxLangHandler
QueryHandler *FluxHandler
WriteHandler *WriteHandler
SetupHandler *SetupHandler
SessionHandler *SessionHandler
AssetHandler *AssetHandler
APIHandler http.Handler
}
func setCORSResponseHeaders(w nethttp.ResponseWriter, r *nethttp.Request) {
func setCORSResponseHeaders(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
@ -37,44 +21,20 @@ func setCORSResponseHeaders(w nethttp.ResponseWriter, r *nethttp.Request) {
}
}
var platformLinks = map[string]interface{}{
"signin": "/api/v2/signin",
"signout": "/api/v2/singout",
"setup": "/api/v2/setup",
"sources": "/api/v2/sources",
"dashboards": "/api/v2/dashboards",
"query": "/api/v2/query",
"write": "/api/v2/write",
"orgs": "/api/v2/orgs",
"auths": "/api/v2/authorizations",
"buckets": "/api/v2/buckets",
"users": "/api/v2/users",
"tasks": "/api/v2/tasks",
"flux": map[string]string{
"self": "/api/v2/flux",
"ast": "/api/v2/flux/ast",
"suggestions": "/api/v2/flux/suggestions",
},
"external": map[string]string{
"statusFeed": "https://www.influxdata.com/feed/json",
},
"system": map[string]string{
"metrics": "/metrics",
"debug": "/debug/pprof",
"health": "/healthz",
},
}
// NewPlatformHandler returns a platform handler that serves the API and associated assets.
func NewPlatformHandler(b *APIBackend) *PlatformHandler {
h := NewAuthenticationHandler()
h.Handler = NewAPIHandler(b)
h.RegisterNoAuthRoute("GET", "/api/v2")
func (h *PlatformHandler) serveLinks(w nethttp.ResponseWriter, r *nethttp.Request) {
ctx := r.Context()
if err := encodeResponse(ctx, w, nethttp.StatusOK, platformLinks); err != nil {
EncodeError(ctx, err, w)
return
return &PlatformHandler{
AssetHandler: NewAssetHandler(),
APIHandler: h,
}
}
// ServeHTTP delegates a request to the appropriate subhandler.
func (h *PlatformHandler) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request) {
func (h *PlatformHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
setCORSResponseHeaders(w, r)
if r.Method == "OPTIONS" {
return
@ -89,95 +49,7 @@ func (h *PlatformHandler) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request
return
}
// Serve the links base links for the API.
if r.URL.Path == "/api/v2/" || r.URL.Path == "/api/v2" {
h.serveLinks(w, r)
return
}
if r.URL.Path == "/api/v2/signin" || r.URL.Path == "/api/v2/signout" {
h.SessionHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/setup") {
h.SetupHandler.ServeHTTP(w, r)
return
}
ctx := r.Context()
var err error
if ctx, err = extractAuthorization(ctx, r); err != nil {
// TODO(desa): remove this when the authentication middleware is added.
}
r = r.WithContext(ctx)
if strings.HasPrefix(r.URL.Path, "/api/v2/write") {
h.WriteHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/query") {
h.QueryHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/buckets") {
h.BucketHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/users") {
h.UserHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/orgs") {
h.OrgHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/authorizations") {
h.AuthorizationHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/dashboards") {
h.DashboardHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/sources") {
h.SourceHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/tasks") {
h.TaskHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/views") {
h.ViewHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/macros") {
h.MacroHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/v2/flux") {
h.FluxLangHandler.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/chronograf/") {
h.ChronografHandler.ServeHTTP(w, r)
return
}
nethttp.NotFound(w, r)
h.APIHandler.ServeHTTP(w, r)
}
// PrometheusCollectors satisfies the prom.PrometheusCollector interface.
@ -185,11 +57,3 @@ func (h *PlatformHandler) PrometheusCollectors() []prometheus.Collector {
// TODO: collect and return relevant metrics.
return nil
}
func extractAuthorization(ctx context.Context, r *nethttp.Request) (context.Context, error) {
t, err := GetToken(r)
if err != nil {
return ctx, err
}
return idpctx.SetToken(ctx, t), nil
}