فهرست منبع

Merge branch 'nico/rbac-crud-operations' into dev

Mohammed Nafees 3 سال پیش
والد
کامیت
752df9eebb

+ 85 - 0
api/server/handlers/project/delete_collaborator.go

@@ -0,0 +1,85 @@
+package project
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"gorm.io/gorm"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type DeleteCollaboratorHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewDeleteCollaboratorHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *DeleteCollaboratorHandler {
+	return &DeleteCollaboratorHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *DeleteCollaboratorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	userID, reqErr := requestutils.GetURLParamUint(r, "user_id")
+
+	if reqErr != nil {
+		p.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	// check for valid user ID
+	_, err := p.Repo().User().ReadUser(userID)
+
+	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+		p.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("user not found")))
+		return
+	} else if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	userRoles, err := p.Repo().ProjectRole().ListAllRolesForUser(proj.ID, userID)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if len(userRoles) == 0 {
+		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			fmt.Errorf("user is not a collaborator in this project"), http.StatusBadRequest,
+		))
+		return
+	}
+
+	for _, role := range userRoles {
+		userIDs := role.GetUserIDs()
+		var newUserIDs []uint
+
+		for _, id := range userIDs {
+			if id != userID {
+				newUserIDs = append(newUserIDs, id)
+			}
+		}
+
+		err = p.Repo().ProjectRole().UpdateUsersInProjectRole(proj.ID, role.UniqueID, newUserIDs)
+
+		if err != nil {
+			p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+	}
+}

+ 4 - 13
api/server/handlers/project/update_collaborator_roles.go

@@ -169,20 +169,11 @@ func (p *UpdateCollaboratorRolesHandler) ServeHTTP(w http.ResponseWriter, r *htt
 				}
 			}
 
-			if len(newUserIDs) == 0 {
-				err = p.Repo().ProjectRole().ClearUsersInProjectRole(proj.ID, uid)
+			err = p.Repo().ProjectRole().UpdateUsersInProjectRole(proj.ID, uid, newUserIDs)
 
-				if err != nil {
-					p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-					return
-				}
-			} else {
-				err = p.Repo().ProjectRole().UpdateUsersInProjectRole(proj.ID, uid, newUserIDs)
-
-				if err != nil {
-					p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-					return
-				}
+			if err != nil {
+				p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+				return
 			}
 		}
 	}

+ 29 - 0
api/server/router/project.go

@@ -415,6 +415,35 @@ func getProjectRoutes(
 		Router:   r,
 	})
 
+	// DELETE /api/projects/{project_id}/collaborators/{user_id} -> project.NewDeleteCollaboratorHandler
+	deleteCollaboratorEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/collaborators/{user_id}",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.SettingsScope,
+			},
+		},
+	)
+
+	deleteCollaboratorHandler := project.NewDeleteCollaboratorHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: deleteCollaboratorEndpoint,
+		Handler:  deleteCollaboratorHandler,
+		Router:   r,
+	})
+
 	// GET /api/projects/{project_id}/roles -> project.NewRolesListHandler
 	listRolesEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 8 - 6
dashboard/src/main/home/project-settings/InviteList.tsx

@@ -27,7 +27,7 @@ export type Collaborator = {
   roles: string[];
 };
 
-const InvitePage: React.FunctionComponent<Props> = ({}) => {
+const InvitePage: React.FunctionComponent<Props> = ({ }) => {
   const {
     currentProject,
     setCurrentModal,
@@ -220,8 +220,11 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
     try {
       api.removeCollaborator(
         "<token>",
-        { user_id },
-        { project_id: currentProject.id }
+        {},
+        {
+          project_id: currentProject.id,
+          user_id: user.id
+        }
       );
       getData();
     } catch (error) {
@@ -350,9 +353,8 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
     inviteList.sort((a: any, b: any) => (a.email > b.email ? 1 : -1));
     inviteList.sort((a: any, b: any) => (a.accepted > b.accepted ? 1 : -1));
     const buildInviteLink = (token: string) => `
-      ${isHTTPS ? "https://" : ""}${window.location.host}/api/projects/${
-      currentProject.id
-    }/invites/${token}
+      ${isHTTPS ? "https://" : ""}${window.location.host}/api/projects/${currentProject.id
+      }/invites/${token}
     `;
 
     if (!user) {

+ 8 - 2
dashboard/src/shared/api.tsx

@@ -1588,9 +1588,15 @@ const updateCollaborator = baseApi<
   }
 >("PATCH", ({ project_id, user_id }) => `/api/projects/${project_id}/collaborators/${user_id}`);
 
-const removeCollaborator = baseApi<{ user_id: number }, { project_id: number }>(
+const removeCollaborator = baseApi<
+  {},
+  {
+    project_id: number;
+    user_id: number;
+  }
+>(
   "DELETE",
-  ({ project_id }) => `/api/projects/${project_id}/roles`
+  ({ project_id, user_id }) => `/api/projects/${project_id}/collaborators/${user_id}`
 );
 
 const getPolicyDocument = baseApi<{}, { project_id: number }>(

+ 6 - 1
internal/repository/gorm/project_role.go

@@ -48,7 +48,7 @@ func (repo *ProjectRoleRepository) ListProjectRoles(projectID uint) ([]*models.P
 func (repo *ProjectRoleRepository) ListAllRolesForUser(projectID, userID uint) ([]*models.ProjectRole, error) {
 	projectRoles := []*models.ProjectRole{}
 
-	if err := repo.db.Where("project_id = ?", projectID).Find(&projectRoles).Error; err != nil {
+	if err := repo.db.Preload("Users").Where("project_id = ?", projectID).Find(&projectRoles).Error; err != nil {
 		return nil, err
 	}
 
@@ -66,6 +66,11 @@ func (repo *ProjectRoleRepository) ListAllRolesForUser(projectID, userID uint) (
 }
 
 func (repo *ProjectRoleRepository) UpdateUsersInProjectRole(projectID uint, roleUID string, userIDs []uint) error {
+	// add a safeguard here
+	if len(userIDs) == 0 {
+		return repo.ClearUsersInProjectRole(projectID, roleUID)
+	}
+
 	users := []*models.User{}
 
 	if err := repo.db.Find(&users, userIDs).Error; err != nil {