policy.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package authz
  2. import (
  3. "context"
  4. "net/http"
  5. "github.com/porter-dev/porter/api/server/authz/policy"
  6. "github.com/porter-dev/porter/api/server/shared/apierrors"
  7. "github.com/porter-dev/porter/api/server/shared/config"
  8. "github.com/porter-dev/porter/api/server/shared/requestutils"
  9. "github.com/porter-dev/porter/api/types"
  10. "github.com/porter-dev/porter/internal/models"
  11. "github.com/porter-dev/porter/internal/telemetry"
  12. )
  13. type PolicyMiddleware struct {
  14. config *config.Config
  15. endpointMeta types.APIRequestMetadata
  16. loader policy.PolicyDocumentLoader
  17. }
  18. func NewPolicyMiddleware(
  19. config *config.Config,
  20. endpointMeta types.APIRequestMetadata,
  21. loader policy.PolicyDocumentLoader,
  22. ) *PolicyMiddleware {
  23. return &PolicyMiddleware{config, endpointMeta, loader}
  24. }
  25. func (p *PolicyMiddleware) Middleware(next http.Handler) http.Handler {
  26. return &PolicyHandler{next, p.config, p.endpointMeta, p.loader}
  27. }
  28. type PolicyHandler struct {
  29. next http.Handler
  30. config *config.Config
  31. endpointMeta types.APIRequestMetadata
  32. loader policy.PolicyDocumentLoader
  33. }
  34. func (h *PolicyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  35. ctx, span := telemetry.NewSpan(r.Context(), "serve-policy-handler")
  36. defer span.End()
  37. // get the full map of scopes to resource actions
  38. reqScopes, reqErr := getRequestActionForEndpoint(r, h.endpointMeta)
  39. if reqErr != nil {
  40. err := telemetry.Error(ctx, span, reqErr, "unable to get request action for endpoint")
  41. apierrors.HandleAPIError(h.config.Logger, h.config.Alerter, w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest), true)
  42. return
  43. }
  44. policyLoaderOpts := &policy.PolicyLoaderOpts{}
  45. // first check if an api token exists in context
  46. if r.Context().Value("api_token") != nil {
  47. projID := reqScopes[types.ProjectScope].Resource.UInt
  48. // FIXME: find a clean way to get the project
  49. apiToken, _ := r.Context().Value("api_token").(*models.APIToken)
  50. policyLoaderOpts.ProjectToken = apiToken
  51. policyLoaderOpts.ProjectID = projID
  52. } else {
  53. projID := reqScopes[types.ProjectScope].Resource.UInt
  54. user, _ := r.Context().Value(types.UserScope).(*models.User)
  55. policyLoaderOpts.ProjectID = projID
  56. policyLoaderOpts.UserID = user.ID
  57. }
  58. // load policy documents for the user + project
  59. policyDocs, reqErr := h.loader.LoadPolicyDocuments(policyLoaderOpts)
  60. if reqErr != nil {
  61. err := telemetry.Error(ctx, span, reqErr, "unable to load policy documents")
  62. apierrors.HandleAPIError(h.config.Logger, h.config.Alerter, w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError), true)
  63. return
  64. }
  65. // validate that the policy permits the action
  66. hasAccess := policy.HasScopeAccess(policyDocs, reqScopes)
  67. if !hasAccess {
  68. err := telemetry.Error(ctx, span, nil, "insufficient permissions to perform action")
  69. apierrors.HandleAPIError(
  70. h.config.Logger,
  71. h.config.Alerter,
  72. w,
  73. r,
  74. apierrors.NewErrPassThroughToClient(err, http.StatusForbidden),
  75. true,
  76. )
  77. return
  78. }
  79. // add the set of resource ids to the request context
  80. ctx = NewRequestScopeCtx(ctx, reqScopes)
  81. r = r.Clone(ctx)
  82. h.next.ServeHTTP(w, r)
  83. }
  84. func NewRequestScopeCtx(ctx context.Context, reqScopes map[types.PermissionScope]*types.RequestAction) context.Context {
  85. return context.WithValue(ctx, types.RequestScopeCtxKey, reqScopes)
  86. }
  87. func getRequestActionForEndpoint(
  88. r *http.Request,
  89. endpointMeta types.APIRequestMetadata,
  90. ) (res map[types.PermissionScope]*types.RequestAction, reqErr apierrors.RequestError) {
  91. res = make(map[types.PermissionScope]*types.RequestAction)
  92. // iterate through scopes, attach policies as needed
  93. for _, scope := range endpointMeta.Scopes {
  94. // find the resource ID and create the resource
  95. resource := types.NameOrUInt{}
  96. switch scope {
  97. case types.ProjectScope:
  98. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamProjectID)
  99. case types.ClusterScope:
  100. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamClusterID)
  101. case types.RegistryScope:
  102. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamRegistryID)
  103. case types.HelmRepoScope:
  104. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamHelmRepoID)
  105. case types.GitInstallationScope:
  106. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamGitInstallationID)
  107. case types.InfraScope:
  108. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamInfraID)
  109. case types.OperationScope:
  110. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamOperationID)
  111. case types.NamespaceScope:
  112. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamNamespace)
  113. case types.ReleaseScope:
  114. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamReleaseName)
  115. case types.StackScope:
  116. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamStackID)
  117. case types.InviteScope:
  118. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamInviteID)
  119. case types.GitlabIntegrationScope:
  120. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamIntegrationID)
  121. case types.APIContractRevisionScope:
  122. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamAPIContractRevisionID)
  123. }
  124. if reqErr != nil {
  125. return nil, reqErr
  126. }
  127. res[scope] = &types.RequestAction{
  128. Verb: endpointMeta.Verb,
  129. Resource: resource,
  130. }
  131. }
  132. return res, nil
  133. }