update_collaborator_roles.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package project
  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/server/shared/requestutils"
  8. "gorm.io/gorm"
  9. "github.com/porter-dev/porter/api/server/handlers"
  10. "github.com/porter-dev/porter/api/server/shared"
  11. "github.com/porter-dev/porter/api/server/shared/config"
  12. "github.com/porter-dev/porter/api/types"
  13. "github.com/porter-dev/porter/internal/models"
  14. )
  15. type UpdateCollaboratorRolesHandler struct {
  16. handlers.PorterHandlerReadWriter
  17. }
  18. func NewUpdateCollaboratorRolesHandler(
  19. config *config.Config,
  20. decoderValidator shared.RequestDecoderValidator,
  21. writer shared.ResultWriter,
  22. ) *UpdateCollaboratorRolesHandler {
  23. return &UpdateCollaboratorRolesHandler{
  24. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  25. }
  26. }
  27. func (p *UpdateCollaboratorRolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  28. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  29. userID, reqErr := requestutils.GetURLParamUint(r, "user_id")
  30. if reqErr != nil {
  31. p.HandleAPIError(w, r, reqErr)
  32. return
  33. }
  34. request := &types.UpdateCollaboratorRoleRequest{}
  35. ok := p.DecodeAndValidate(w, r, request)
  36. if !ok {
  37. return
  38. }
  39. if len(request.RoleUIDs) == 0 {
  40. p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
  41. fmt.Errorf("roles cannot be empty"), http.StatusPreconditionFailed,
  42. ))
  43. return
  44. }
  45. // check for valid user ID
  46. _, err := p.Repo().User().ReadUser(userID)
  47. if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
  48. p.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("user not found")))
  49. return
  50. } else if err != nil {
  51. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  52. return
  53. }
  54. rolesMap := make(map[string]*models.ProjectRole)
  55. // check for valid project roles
  56. for _, roleUID := range request.RoleUIDs {
  57. role, err := p.Repo().ProjectRole().ReadProjectRole(proj.ID, roleUID)
  58. if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
  59. p.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("role not found in project: %s", roleUID)))
  60. return
  61. } else if err != nil {
  62. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  63. return
  64. }
  65. rolesMap[roleUID] = role
  66. }
  67. userRoles, err := p.Repo().ProjectRole().ListAllRolesForUser(proj.ID, userID)
  68. if err != nil {
  69. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  70. return
  71. }
  72. if len(userRoles) == 0 {
  73. p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
  74. fmt.Errorf("user is not a collaborator in this project"), http.StatusBadRequest,
  75. ))
  76. return
  77. }
  78. userRolesMap := make(map[string]bool)
  79. for _, role := range userRoles {
  80. userRolesMap[role.UniqueID] = true
  81. }
  82. var rolesToAdd []string
  83. var rolesToRemove []string
  84. for _, uid := range userRoles {
  85. if _, ok := rolesMap[uid.UniqueID]; !ok {
  86. // user had this role, should be removed from
  87. rolesToRemove = append(rolesToRemove, uid.UniqueID)
  88. }
  89. }
  90. for _, uid := range request.RoleUIDs {
  91. if _, ok := userRolesMap[uid]; !ok {
  92. // user does not have this role, should be added to
  93. rolesToAdd = append(rolesToAdd, uid)
  94. }
  95. }
  96. for _, uid := range rolesToAdd {
  97. role, err := p.Repo().ProjectRole().ReadProjectRole(proj.ID, uid)
  98. if err != nil {
  99. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  100. return
  101. }
  102. userIDs := role.GetUserIDs()
  103. userIDs = append(userIDs, userID)
  104. err = p.Repo().ProjectRole().UpdateUsersInProjectRole(proj.ID, uid, userIDs)
  105. if err != nil {
  106. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  107. return
  108. }
  109. }
  110. for _, uid := range rolesToRemove {
  111. role, err := p.Repo().ProjectRole().ReadProjectRole(proj.ID, uid)
  112. if err != nil {
  113. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  114. return
  115. }
  116. userIDs := role.GetUserIDs()
  117. var newUserIDs []uint
  118. for _, id := range userIDs {
  119. if id != userID {
  120. newUserIDs = append(newUserIDs, id)
  121. }
  122. }
  123. err = p.Repo().ProjectRole().UpdateUsersInProjectRole(proj.ID, uid, newUserIDs)
  124. if err != nil {
  125. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  126. return
  127. }
  128. }
  129. }