loader.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. package policy
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "github.com/porter-dev/porter/api/server/shared/apierrors"
  7. "github.com/porter-dev/porter/api/types"
  8. "github.com/porter-dev/porter/internal/models"
  9. "github.com/porter-dev/porter/internal/repository"
  10. "gorm.io/gorm"
  11. )
  12. type PolicyLoaderOpts struct {
  13. ProjectID, UserID uint
  14. ProjectToken *models.APIToken
  15. }
  16. type PolicyDocumentLoader interface {
  17. LoadPolicyDocuments(opts *PolicyLoaderOpts) ([]*types.PolicyDocument, apierrors.RequestError)
  18. }
  19. // RepoPolicyDocumentLoader loads policy documents by reading from the repository database
  20. type RepoPolicyDocumentLoader struct {
  21. projRepo repository.ProjectRepository
  22. policyRepo repository.PolicyRepository
  23. }
  24. func NewBasicPolicyDocumentLoader(projRepo repository.ProjectRepository, policyRepo repository.PolicyRepository) *RepoPolicyDocumentLoader {
  25. return &RepoPolicyDocumentLoader{projRepo, policyRepo}
  26. }
  27. func (b *RepoPolicyDocumentLoader) LoadPolicyDocuments(
  28. opts *PolicyLoaderOpts,
  29. ) ([]*types.PolicyDocument, apierrors.RequestError) {
  30. if opts.ProjectToken != nil {
  31. // check that the token belongs to the project, in this case it's solely project-scoped
  32. if opts.ProjectID == 0 || opts.ProjectToken.ProjectID == 0 || opts.ProjectID != opts.ProjectToken.ProjectID {
  33. return nil, apierrors.NewErrForbidden(fmt.Errorf("project id %d does not match token id %d", opts.ProjectID, opts.ProjectToken.ProjectID))
  34. }
  35. proj, err := b.projRepo.ReadProject(opts.ProjectID)
  36. if err != nil {
  37. return nil, apierrors.NewErrForbidden(fmt.Errorf("error fetching project: %w", err))
  38. }
  39. if !proj.APITokensEnabled {
  40. return nil, apierrors.NewErrForbidden(fmt.Errorf("api tokens are not enabled for this project"))
  41. }
  42. // load the policy
  43. apiPolicy, reqErr := GetAPIPolicyFromUID(b.policyRepo, opts.ProjectToken.ProjectID, opts.ProjectToken.PolicyUID)
  44. if reqErr != nil {
  45. return nil, reqErr
  46. }
  47. return apiPolicy.Policy, nil
  48. } else if opts.ProjectID != 0 && opts.UserID != 0 {
  49. userID := opts.UserID
  50. projectID := opts.ProjectID
  51. // read role and case on role "kind"
  52. role, err := b.projRepo.ReadProjectRole(projectID, userID)
  53. if err != nil && err == gorm.ErrRecordNotFound {
  54. return nil, apierrors.NewErrForbidden(
  55. fmt.Errorf("user %d does not have a role in project %d", userID, projectID),
  56. )
  57. } else if err != nil {
  58. return nil, apierrors.NewErrInternal(err)
  59. }
  60. // load role based on role kind
  61. switch role.Kind {
  62. case types.RoleAdmin:
  63. return types.AdminPolicy, nil
  64. case types.RoleDeveloper:
  65. return types.DeveloperPolicy, nil
  66. case types.RoleViewer:
  67. return types.ViewerPolicy, nil
  68. default:
  69. return nil, apierrors.NewErrForbidden(
  70. fmt.Errorf("%s role not supported for user %d, project %d", string(role.Kind), userID, projectID),
  71. )
  72. }
  73. }
  74. return nil, apierrors.NewErrForbidden(
  75. fmt.Errorf("policy loader called with invalid arguments"),
  76. )
  77. }
  78. func GetAPIPolicyFromUID(policyRepo repository.PolicyRepository, projectID uint, uid string) (*types.APIPolicy, apierrors.RequestError) {
  79. switch uid {
  80. case "admin":
  81. return &types.APIPolicy{
  82. APIPolicyMeta: &types.APIPolicyMeta{
  83. Name: "admin",
  84. UID: "admin",
  85. },
  86. Policy: types.AdminPolicy,
  87. }, nil
  88. case "developer":
  89. return &types.APIPolicy{
  90. APIPolicyMeta: &types.APIPolicyMeta{
  91. Name: "developer",
  92. UID: "developer",
  93. },
  94. Policy: types.DeveloperPolicy,
  95. }, nil
  96. case "viewer":
  97. return &types.APIPolicy{
  98. APIPolicyMeta: &types.APIPolicyMeta{
  99. Name: "viewer",
  100. UID: "viewer",
  101. },
  102. Policy: types.ViewerPolicy,
  103. }, nil
  104. default:
  105. // look up the policy and make sure it exists
  106. policyModel, err := policyRepo.ReadPolicy(projectID, uid)
  107. if err != nil {
  108. if errors.Is(err, gorm.ErrRecordNotFound) {
  109. return nil, apierrors.NewErrPassThroughToClient(
  110. fmt.Errorf("policy not found in project"),
  111. http.StatusBadRequest,
  112. )
  113. }
  114. return nil, apierrors.NewErrInternal(err)
  115. }
  116. apiPolicy, err := policyModel.ToAPIPolicyType()
  117. if err != nil {
  118. return nil, apierrors.NewErrInternal(err)
  119. }
  120. return apiPolicy, nil
  121. }
  122. }