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" taskbolt "github.com/influxdata/platform/task/backend/bolt"
"github.com/influxdata/platform/task/backend/coordinator" "github.com/influxdata/platform/task/backend/coordinator"
taskexecutor "github.com/influxdata/platform/task/backend/executor" taskexecutor "github.com/influxdata/platform/task/backend/executor"
pzap "github.com/influxdata/platform/zap"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -174,9 +173,9 @@ func platformF(cmd *cobra.Command, args []string) {
dashboardSvc = c dashboardSvc = c
} }
var cellSvc platform.ViewService var viewSvc platform.ViewService
{ {
cellSvc = c viewSvc = c
} }
var sourceSvc platform.SourceService var sourceSvc platform.SourceService
@ -286,87 +285,34 @@ func platformF(cmd *cobra.Command, args []string) {
Addr: httpBindAddress, 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 // HTTP server
go func() { go func() {
sessionHandler := http.NewSessionHandler() platformHandler := http.NewPlatformHandler(handlerConfig)
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,
}
reg.MustRegister(platformHandler.PrometheusCollectors()...) reg.MustRegister(platformHandler.PrometheusCollectors()...)
h := http.NewHandlerFromRegistry("platform", reg) 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 package http
import ( import (
"context" "net/http"
nethttp "net/http"
"strings" "strings"
idpctx "github.com/influxdata/platform/context"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
// PlatformHandler is a collection of all the service handlers. // PlatformHandler is a collection of all the service handlers.
type PlatformHandler struct { type PlatformHandler struct {
BucketHandler *BucketHandler AssetHandler *AssetHandler
UserHandler *UserHandler APIHandler http.Handler
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
} }
func setCORSResponseHeaders(w nethttp.ResponseWriter, r *nethttp.Request) { func setCORSResponseHeaders(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" { if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") 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{}{ // NewPlatformHandler returns a platform handler that serves the API and associated assets.
"signin": "/api/v2/signin", func NewPlatformHandler(b *APIBackend) *PlatformHandler {
"signout": "/api/v2/singout", h := NewAuthenticationHandler()
"setup": "/api/v2/setup", h.Handler = NewAPIHandler(b)
"sources": "/api/v2/sources", h.RegisterNoAuthRoute("GET", "/api/v2")
"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 *PlatformHandler) serveLinks(w nethttp.ResponseWriter, r *nethttp.Request) { return &PlatformHandler{
ctx := r.Context() AssetHandler: NewAssetHandler(),
if err := encodeResponse(ctx, w, nethttp.StatusOK, platformLinks); err != nil { APIHandler: h,
EncodeError(ctx, err, w)
return
} }
} }
// ServeHTTP delegates a request to the appropriate subhandler. // 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) setCORSResponseHeaders(w, r)
if r.Method == "OPTIONS" { if r.Method == "OPTIONS" {
return return
@ -89,95 +49,7 @@ func (h *PlatformHandler) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request
return return
} }
// Serve the links base links for the API. h.APIHandler.ServeHTTP(w, r)
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)
} }
// PrometheusCollectors satisfies the prom.PrometheusCollector interface. // PrometheusCollectors satisfies the prom.PrometheusCollector interface.
@ -185,11 +57,3 @@ func (h *PlatformHandler) PrometheusCollectors() []prometheus.Collector {
// TODO: collect and return relevant metrics. // TODO: collect and return relevant metrics.
return nil 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
}