router.go 4.7 KB

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