policy.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package authz
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "github.com/porter-dev/porter/api/server/authz/policy"
  7. "github.com/porter-dev/porter/api/server/shared/apierrors"
  8. "github.com/porter-dev/porter/api/server/shared/config"
  9. "github.com/porter-dev/porter/api/server/shared/requestutils"
  10. "github.com/porter-dev/porter/api/types"
  11. "github.com/porter-dev/porter/internal/models"
  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. // get the full map of scopes to resource actions
  36. reqScopes, reqErr := getRequestActionForEndpoint(r, h.endpointMeta)
  37. if reqErr != nil {
  38. apierrors.HandleAPIError(h.config.Logger, h.config.Alerter, w, r, reqErr, true)
  39. return
  40. }
  41. // load policy documents for the user + project
  42. projID := reqScopes[types.ProjectScope].Resource.UInt
  43. user, _ := r.Context().Value(types.UserScope).(*models.User)
  44. policyDocs, reqErr := h.loader.LoadPolicyDocuments(user.ID, projID)
  45. if reqErr != nil {
  46. apierrors.HandleAPIError(h.config.Logger, h.config.Alerter, w, r, reqErr, true)
  47. return
  48. }
  49. // validate that the policy permits the action
  50. hasAccess := policy.HasScopeAccess(policyDocs, reqScopes)
  51. if !hasAccess {
  52. apierrors.HandleAPIError(
  53. h.config.Logger,
  54. h.config.Alerter,
  55. w,
  56. r,
  57. apierrors.NewErrForbidden(fmt.Errorf("policy forbids action for user %d in project %d", user.ID, projID)),
  58. true,
  59. )
  60. return
  61. }
  62. // add the set of resource ids to the request context
  63. ctx := NewRequestScopeCtx(r.Context(), reqScopes)
  64. r = r.Clone(ctx)
  65. h.next.ServeHTTP(w, r)
  66. }
  67. func NewRequestScopeCtx(ctx context.Context, reqScopes map[types.PermissionScope]*types.RequestAction) context.Context {
  68. return context.WithValue(ctx, types.RequestScopeCtxKey, reqScopes)
  69. }
  70. func getRequestActionForEndpoint(
  71. r *http.Request,
  72. endpointMeta types.APIRequestMetadata,
  73. ) (res map[types.PermissionScope]*types.RequestAction, reqErr apierrors.RequestError) {
  74. res = make(map[types.PermissionScope]*types.RequestAction)
  75. // iterate through scopes, attach policies as needed
  76. for _, scope := range endpointMeta.Scopes {
  77. // find the resource ID and create the resource
  78. resource := types.NameOrUInt{}
  79. switch scope {
  80. case types.ProjectScope:
  81. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamProjectID)
  82. case types.ClusterScope:
  83. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamClusterID)
  84. case types.RegistryScope:
  85. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamRegistryID)
  86. case types.HelmRepoScope:
  87. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamHelmRepoID)
  88. case types.GitInstallationScope:
  89. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamGitInstallationID)
  90. case types.InfraScope:
  91. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamInfraID)
  92. case types.OperationScope:
  93. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamOperationID)
  94. case types.NamespaceScope:
  95. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamNamespace)
  96. case types.ReleaseScope:
  97. resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamReleaseName)
  98. case types.InviteScope:
  99. resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamInviteID)
  100. }
  101. if reqErr != nil {
  102. return nil, reqErr
  103. }
  104. res[scope] = &types.RequestAction{
  105. Verb: endpointMeta.Verb,
  106. Resource: resource,
  107. }
  108. }
  109. return res, nil
  110. }