| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- package router
- import (
- "net/http"
- "github.com/go-chi/chi"
- "github.com/porter-dev/porter/api/server/authn"
- "github.com/porter-dev/porter/api/server/authz"
- "github.com/porter-dev/porter/api/server/authz/policy"
- "github.com/porter-dev/porter/api/server/shared"
- "github.com/porter-dev/porter/api/types"
- )
- func NewAPIRouter(config *shared.Config) *chi.Mux {
- r := chi.NewRouter()
- // set the content type for all API endpoints
- r.Use(ContentTypeJSON)
- endpointFactory := shared.NewAPIObjectEndpointFactory(config)
- baseRegisterer := NewBaseRegisterer()
- clusterRegisterer := NewClusterScopedRegisterer()
- projRegisterer := NewProjectScopedRegisterer(clusterRegisterer)
- userRegisterer := NewUserScopedRegisterer(projRegisterer)
- r.Route("/api", func(r chi.Router) {
- baseRoutes := baseRegisterer.GetRoutes(
- r,
- config,
- &types.Path{
- RelativePath: "",
- },
- endpointFactory,
- )
- userRoutes := userRegisterer.GetRoutes(
- r,
- config,
- &types.Path{
- RelativePath: "",
- },
- endpointFactory,
- userRegisterer.Children...,
- )
- routes := append(baseRoutes, userRoutes...)
- registerRoutes(config, routes)
- })
- return r
- }
- type Route struct {
- Endpoint *shared.APIEndpoint
- Handler http.Handler
- Router chi.Router
- }
- type Registerer struct {
- GetRoutes func(
- r chi.Router,
- config *shared.Config,
- basePath *types.Path,
- factory shared.APIEndpointFactory,
- children ...*Registerer,
- ) []*Route
- Children []*Registerer
- }
- func registerRoutes(config *shared.Config, routes []*Route) {
- // Create a new "user-scoped" factory which will create a new user-scoped request
- // after authentication. Each subsequent http.Handler can lookup the user in context.
- authNFactory := authn.NewAuthNFactory(config)
- // Create a new "project-scoped" factory which will create a new project-scoped request
- // after authorization. Each subsequent http.Handler can lookup the project in context.
- projFactory := authz.NewProjectScopedFactory(config)
- // Create a new "cluster-scoped" factory which will create a new cluster-scoped request
- // after authorization. Each subsequent http.Handler can lookup the cluster in context.
- clusterFactory := authz.NewClusterScopedFactory(config)
- // Policy doc loader loads the policy documents for a specific project.
- policyDocLoader := policy.NewBasicPolicyDocumentLoader(config.Repo.Project())
- for _, route := range routes {
- atomicGroup := route.Router.Group(nil)
- for _, scope := range route.Endpoint.Metadata.Scopes {
- switch scope {
- case types.UserScope:
- // if the endpoint should redirect when authn fails, attach redirect handler
- if route.Endpoint.Metadata.ShouldRedirect {
- atomicGroup.Use(authNFactory.NewAuthenticatedWithRedirect)
- } else {
- atomicGroup.Use(authNFactory.NewAuthenticated)
- }
- case types.ProjectScope:
- policyFactory := authz.NewPolicyMiddleware(config, *route.Endpoint.Metadata, policyDocLoader)
- atomicGroup.Use(policyFactory.Middleware)
- atomicGroup.Use(projFactory.Middleware)
- case types.ClusterScope:
- atomicGroup.Use(clusterFactory.Middleware)
- }
- }
- atomicGroup.Method(
- string(route.Endpoint.Metadata.Method),
- route.Endpoint.Metadata.Path.RelativePath,
- route.Handler,
- )
- }
- }
- // ContentTypeJSON sets the content type for requests to application/json
- func ContentTypeJSON(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json;charset=utf8")
- next.ServeHTTP(w, r)
- })
- }
|