router.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package router
  2. import (
  3. "net/http"
  4. "github.com/go-chi/chi"
  5. "github.com/porter-dev/porter/api/server/authn"
  6. "github.com/porter-dev/porter/api/server/authz"
  7. "github.com/porter-dev/porter/api/server/authz/policy"
  8. "github.com/porter-dev/porter/api/server/shared"
  9. "github.com/porter-dev/porter/api/types"
  10. )
  11. func NewAPIRouter(config *shared.Config) *chi.Mux {
  12. r := chi.NewRouter()
  13. // set the content type for all API endpoints
  14. r.Use(ContentTypeJSON)
  15. endpointFactory := shared.NewAPIObjectEndpointFactory(config)
  16. baseRegisterer := NewBaseRegisterer()
  17. clusterRegisterer := NewClusterScopedRegisterer()
  18. projRegisterer := NewProjectScopedRegisterer(clusterRegisterer)
  19. userRegisterer := NewUserScopedRegisterer(projRegisterer)
  20. r.Route("/api", func(r chi.Router) {
  21. baseRoutes := baseRegisterer.GetRoutes(
  22. r,
  23. config,
  24. &types.Path{
  25. RelativePath: "",
  26. },
  27. endpointFactory,
  28. )
  29. userRoutes := userRegisterer.GetRoutes(
  30. r,
  31. config,
  32. &types.Path{
  33. RelativePath: "",
  34. },
  35. endpointFactory,
  36. userRegisterer.Children...,
  37. )
  38. routes := append(baseRoutes, userRoutes...)
  39. registerRoutes(config, routes)
  40. })
  41. return r
  42. }
  43. type Route struct {
  44. Endpoint *shared.APIEndpoint
  45. Handler http.Handler
  46. Router chi.Router
  47. }
  48. type Registerer struct {
  49. GetRoutes func(
  50. r chi.Router,
  51. config *shared.Config,
  52. basePath *types.Path,
  53. factory shared.APIEndpointFactory,
  54. children ...*Registerer,
  55. ) []*Route
  56. Children []*Registerer
  57. }
  58. func registerRoutes(config *shared.Config, routes []*Route) {
  59. // Create a new "user-scoped" factory which will create a new user-scoped request
  60. // after authentication. Each subsequent http.Handler can lookup the user in context.
  61. authNFactory := authn.NewAuthNFactory(config)
  62. // Create a new "project-scoped" factory which will create a new project-scoped request
  63. // after authorization. Each subsequent http.Handler can lookup the project in context.
  64. projFactory := authz.NewProjectScopedFactory(config)
  65. // Create a new "cluster-scoped" factory which will create a new cluster-scoped request
  66. // after authorization. Each subsequent http.Handler can lookup the cluster in context.
  67. clusterFactory := authz.NewClusterScopedFactory(config)
  68. // Policy doc loader loads the policy documents for a specific project.
  69. policyDocLoader := policy.NewBasicPolicyDocumentLoader(config.Repo.Project())
  70. for _, route := range routes {
  71. atomicGroup := route.Router.Group(nil)
  72. for _, scope := range route.Endpoint.Metadata.Scopes {
  73. switch scope {
  74. case types.UserScope:
  75. // if the endpoint should redirect when authn fails, attach redirect handler
  76. if route.Endpoint.Metadata.ShouldRedirect {
  77. atomicGroup.Use(authNFactory.NewAuthenticatedWithRedirect)
  78. } else {
  79. atomicGroup.Use(authNFactory.NewAuthenticated)
  80. }
  81. case types.ProjectScope:
  82. policyFactory := authz.NewPolicyMiddleware(config, *route.Endpoint.Metadata, policyDocLoader)
  83. atomicGroup.Use(policyFactory.Middleware)
  84. atomicGroup.Use(projFactory.Middleware)
  85. case types.ClusterScope:
  86. atomicGroup.Use(clusterFactory.Middleware)
  87. }
  88. }
  89. atomicGroup.Method(
  90. string(route.Endpoint.Metadata.Method),
  91. route.Endpoint.Metadata.Path.RelativePath,
  92. route.Handler,
  93. )
  94. }
  95. }
  96. // ContentTypeJSON sets the content type for requests to application/json
  97. func ContentTypeJSON(next http.Handler) http.Handler {
  98. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  99. w.Header().Set("Content-Type", "application/json;charset=utf8")
  100. next.ServeHTTP(w, r)
  101. })
  102. }