router.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. releaseRegisterer := NewReleaseScopedRegisterer()
  18. namespaceRegisterer := NewNamespaceScopedRegisterer(releaseRegisterer)
  19. clusterRegisterer := NewClusterScopedRegisterer(namespaceRegisterer)
  20. projRegisterer := NewProjectScopedRegisterer(clusterRegisterer)
  21. userRegisterer := NewUserScopedRegisterer(projRegisterer)
  22. r.Route("/api", func(r chi.Router) {
  23. baseRoutes := baseRegisterer.GetRoutes(
  24. r,
  25. config,
  26. &types.Path{
  27. RelativePath: "",
  28. },
  29. endpointFactory,
  30. )
  31. userRoutes := userRegisterer.GetRoutes(
  32. r,
  33. config,
  34. &types.Path{
  35. RelativePath: "",
  36. },
  37. endpointFactory,
  38. userRegisterer.Children...,
  39. )
  40. routes := append(baseRoutes, userRoutes...)
  41. registerRoutes(config, routes)
  42. })
  43. return r
  44. }
  45. type Route struct {
  46. Endpoint *shared.APIEndpoint
  47. Handler http.Handler
  48. Router chi.Router
  49. }
  50. type Registerer struct {
  51. GetRoutes func(
  52. r chi.Router,
  53. config *shared.Config,
  54. basePath *types.Path,
  55. factory shared.APIEndpointFactory,
  56. children ...*Registerer,
  57. ) []*Route
  58. Children []*Registerer
  59. }
  60. func registerRoutes(config *shared.Config, routes []*Route) {
  61. // Create a new "user-scoped" factory which will create a new user-scoped request
  62. // after authentication. Each subsequent http.Handler can lookup the user in context.
  63. authNFactory := authn.NewAuthNFactory(config)
  64. // Create a new "project-scoped" factory which will create a new project-scoped request
  65. // after authorization. Each subsequent http.Handler can lookup the project in context.
  66. projFactory := authz.NewProjectScopedFactory(config)
  67. // Create a new "cluster-scoped" factory which will create a new cluster-scoped request
  68. // after authorization. Each subsequent http.Handler can lookup the cluster in context.
  69. clusterFactory := authz.NewClusterScopedFactory(config)
  70. // Create a new "registry-scoped" factory which will create a new registry-scoped request
  71. // after authorization. Each subsequent http.Handler can lookup the registry in context.
  72. registryFactory := authz.NewRegistryScopedFactory(config)
  73. // Create a new "release-scoped" factory which will create a new release-scoped request
  74. // after authorization. Each subsequent http.Handler can lookup the release in context.
  75. releaseFactory := authz.NewReleaseScopedFactory(config)
  76. // Policy doc loader loads the policy documents for a specific project.
  77. policyDocLoader := policy.NewBasicPolicyDocumentLoader(config.Repo.Project())
  78. for _, route := range routes {
  79. atomicGroup := route.Router.Group(nil)
  80. for _, scope := range route.Endpoint.Metadata.Scopes {
  81. switch scope {
  82. case types.UserScope:
  83. // if the endpoint should redirect when authn fails, attach redirect handler
  84. if route.Endpoint.Metadata.ShouldRedirect {
  85. atomicGroup.Use(authNFactory.NewAuthenticatedWithRedirect)
  86. } else {
  87. atomicGroup.Use(authNFactory.NewAuthenticated)
  88. }
  89. case types.ProjectScope:
  90. policyFactory := authz.NewPolicyMiddleware(config, *route.Endpoint.Metadata, policyDocLoader)
  91. atomicGroup.Use(policyFactory.Middleware)
  92. atomicGroup.Use(projFactory.Middleware)
  93. case types.ClusterScope:
  94. atomicGroup.Use(clusterFactory.Middleware)
  95. case types.RegistryScope:
  96. atomicGroup.Use(registryFactory.Middleware)
  97. case types.ReleaseScope:
  98. atomicGroup.Use(releaseFactory.Middleware)
  99. }
  100. }
  101. atomicGroup.Method(
  102. string(route.Endpoint.Metadata.Method),
  103. route.Endpoint.Metadata.Path.RelativePath,
  104. route.Handler,
  105. )
  106. }
  107. }
  108. // ContentTypeJSON sets the content type for requests to application/json
  109. func ContentTypeJSON(next http.Handler) http.Handler {
  110. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  111. w.Header().Set("Content-Type", "application/json;charset=utf8")
  112. next.ServeHTTP(w, r)
  113. })
  114. }