From d494e46d93bd87381c153ab99476cedf2901ea52 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 7 Feb 2024 17:00:01 +0900 Subject: [PATCH 01/15] improve role mgnt. logic --- internal/delivery/api/endpoint.go | 87 ++++ internal/delivery/http/auth.go | 1 + internal/delivery/http/role.go | 523 +++++++++++++++++++++++ internal/helper/util.go | 8 + internal/middleware/auth/role/default.go | 324 ++++++++++++++ internal/middleware/auth/role/role.go | 37 +- internal/repository/endpoint.go | 140 ++++++ internal/repository/endpoint_test.go | 75 ++++ internal/repository/organization.go | 21 + internal/repository/permission.go | 219 ++++++++++ internal/repository/permission_test.go | 445 +++++++++++++++++++ internal/repository/repository.go | 1 + internal/repository/role.go | 303 +++++++++++++ internal/repository/role_test.go | 28 ++ internal/repository/user.go | 23 +- internal/route/route.go | 13 + internal/usecase/permission.go | 45 ++ internal/usecase/role.go | 126 ++++++ internal/usecase/role_test.go | 217 ++++++++++ internal/usecase/user.go | 8 +- pkg/domain/endpoint.go | 11 + pkg/domain/permmision.go | 18 + pkg/domain/role.go | 109 +++++ pkg/domain/user.go | 9 - 24 files changed, 2750 insertions(+), 41 deletions(-) create mode 100644 internal/delivery/http/role.go create mode 100644 internal/middleware/auth/role/default.go create mode 100644 internal/repository/endpoint.go create mode 100644 internal/repository/endpoint_test.go create mode 100644 internal/repository/permission.go create mode 100644 internal/repository/permission_test.go create mode 100644 internal/repository/role.go create mode 100644 internal/repository/role_test.go create mode 100644 internal/usecase/permission.go create mode 100644 internal/usecase/role.go create mode 100644 internal/usecase/role_test.go create mode 100644 pkg/domain/endpoint.go create mode 100644 pkg/domain/permmision.go create mode 100644 pkg/domain/role.go diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 36edf66a..00cfc5a4 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -156,6 +156,13 @@ const ( GetAudits GetAudit DeleteAudit + + // Role + CreateTksRole + ListTksRoles + GetTksRole + DeleteTksRole + UpdateTksRole ) var ApiMap = map[Endpoint]EndpointInfo{ @@ -635,6 +642,46 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "DeleteAudit", Group: "Audit", }, + CreateTksRole: { + Name: "CreateTksRole", + Group: "Role", + }, + CreateProjectRole: { + Name: "CreateProjectRole", + Group: "Role", + }, + ListTksRoles: { + Name: "ListTksRoles", + Group: "Role", + }, + ListProjectRoles: { + Name: "ListProjectRoles", + Group: "Role", + }, + GetTksRole: { + Name: "GetTksRole", + Group: "Role", + }, + GetProjectRole: { + Name: "GetProjectRole", + Group: "Role", + }, + DeleteTksRole: { + Name: "DeleteTksRole", + Group: "Role", + }, + DeleteProjectRole: { + Name: "DeleteProjectRole", + Group: "Role", + }, + UpdateTksRole: { + Name: "UpdateTksRole", + Group: "Role", + }, + UpdateProjectRole: { + Name: "UpdateProjectRole", + Group: "Role", + }, } func (e Endpoint) String() string { @@ -877,6 +924,26 @@ func (e Endpoint) String() string { return "GetAudit" case DeleteAudit: return "DeleteAudit" + case CreateTksRole: + return "CreateTksRole" + case CreateProjectRole: + return "CreateProjectRole" + case ListTksRoles: + return "ListTksRoles" + case ListProjectRoles: + return "ListProjectRoles" + case GetTksRole: + return "GetTksRole" + case GetProjectRole: + return "GetProjectRole" + case DeleteTksRole: + return "DeleteTksRole" + case DeleteProjectRole: + return "DeleteProjectRole" + case UpdateTksRole: + return "UpdateTksRole" + case UpdateProjectRole: + return "UpdateProjectRole" default: return "" } @@ -1121,6 +1188,26 @@ func GetEndpoint(name string) Endpoint { return GetAudit case "DeleteAudit": return DeleteAudit + case "CreateTksRole": + return CreateTksRole + case "CreateProjectRole": + return CreateProjectRole + case "ListTksRoles": + return ListTksRoles + case "ListProjectRoles": + return ListProjectRoles + case "GetTksRole": + return GetTksRole + case "GetProjectRole": + return GetProjectRole + case "DeleteTksRole": + return DeleteTksRole + case "DeleteProjectRole": + return DeleteProjectRole + case "UpdateTksRole": + return UpdateTksRole + case "UpdateProjectRole": + return UpdateProjectRole default: return -1 } diff --git a/internal/delivery/http/auth.go b/internal/delivery/http/auth.go index c44247de..a7566312 100644 --- a/internal/delivery/http/auth.go +++ b/internal/delivery/http/auth.go @@ -18,6 +18,7 @@ import ( type IAuthHandler interface { Login(w http.ResponseWriter, r *http.Request) Logout(w http.ResponseWriter, r *http.Request) + // Deprecated: PingToken is deprecated. Use VerifyToken instead. PingToken(w http.ResponseWriter, r *http.Request) RefreshToken(w http.ResponseWriter, r *http.Request) FindId(w http.ResponseWriter, r *http.Request) diff --git a/internal/delivery/http/role.go b/internal/delivery/http/role.go new file mode 100644 index 00000000..c254b7ec --- /dev/null +++ b/internal/delivery/http/role.go @@ -0,0 +1,523 @@ +package http + +import ( + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "net/http" +) + +type IRoleHandler interface { + CreateTksRole(w http.ResponseWriter, r *http.Request) + CreateProjectRole(w http.ResponseWriter, r *http.Request) + + ListTksRoles(w http.ResponseWriter, r *http.Request) + ListProjectRoles(w http.ResponseWriter, r *http.Request) + + GetTksRole(w http.ResponseWriter, r *http.Request) + GetProjectRole(w http.ResponseWriter, r *http.Request) + + DeleteTksRole(w http.ResponseWriter, r *http.Request) + DeleteProjectRole(w http.ResponseWriter, r *http.Request) + + UpdateTksRole(w http.ResponseWriter, r *http.Request) + UpdateProjectRole(w http.ResponseWriter, r *http.Request) +} + +type RoleHandler struct { + roleUsecase usecase.IRoleUsecase +} + +func NewRoleHandler(roleUsecase usecase.IRoleUsecase) *RoleHandler { + return &RoleHandler{ + roleUsecase: roleUsecase, + } +} + +// CreateTksRole godoc +// @Tags Role +// @Summary Create Tks Role +// @Description Create Tks Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param body body domain.CreateTksRoleRequest true "Create Tks Role Request" +// @Success 200 {object} domain.CreateTksRoleResponse +// @Router /organizations/{organizationId}/roles [post] + +func (h RoleHandler) CreateTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + var organizationId string + + vars := mux.Vars(r) + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + // request body + input := domain.CreateTksRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // input to dto + dto := domain.TksRole{ + Role: domain.Role{ + OrganizationID: organizationId, + Name: input.Name, + Description: input.Description, + Type: string(domain.RoleTypeTks), + }, + } + + if err := h.roleUsecase.CreateTksRole(&dto); err != nil { + ErrorJSON(w, r, err) + return + } + +} + +// CreateTksRole godoc +// @Tags Role +// @Summary Create Project Role +// @Description Create Project Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param body body domain.CreateProjectRoleRequest true "Create Project Role Request" +// @Success 200 {object} domain.CreateProjectRoleResponse +// @Router /organizations/{organizationId}/projects/{projectId}/roles [post] + +func (h RoleHandler) CreateProjectRole(w http.ResponseWriter, r *http.Request) { + // path parameter + var organizationId, projectId string + + vars := mux.Vars(r) + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + if v, ok := vars["projectId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + projectId = v + } + + // request body + input := domain.CreateProjectRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // input to dto + projectIdUuid, err := uuid.Parse(projectId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + dto := domain.ProjectRole{ + Role: domain.Role{ + OrganizationID: organizationId, + Name: input.Name, + Description: input.Description, + Type: string(domain.RoleTypeProject), + }, + ProjectID: projectIdUuid, + } + + if err := h.roleUsecase.CreateProjectRole(&dto); err != nil { + ErrorJSON(w, r, err) + return + } +} + +// ListTksRoles godoc +// @Tags Role +// @Summary List Tks Roles +// @Description List Tks Roles +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.ListTksRoleResponse +// @Router /organizations/{organizationId}/roles [get] +func (h RoleHandler) ListTksRoles(w http.ResponseWriter, r *http.Request) { + // path parameter + var organizationId string + + vars := mux.Vars(r) + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + // query parameter + urlParams := r.URL.Query() + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + // list roles + roles, err := h.roleUsecase.ListTksRoles(organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ListTksRoleResponse + out.Roles = make([]domain.GetTksRoleResponse, len(roles)) + for i, role := range roles { + out.Roles[i] = domain.GetTksRoleResponse{ + ID: role.ID.String(), + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, + } + } + + if err := serializer.Map(*pg, &out.Pagination); err != nil { + log.InfoWithContext(r.Context(), err) + } + + // response + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListProjectRoles godoc +// @Tags Role +// @Summary List Project Roles +// @Description List Project Roles +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Success 200 {object} domain.ListProjectRoleResponse +// @Router /organizations/{organizationId}/projects/{projectId}/roles [get] + +func (h RoleHandler) ListProjectRoles(w http.ResponseWriter, r *http.Request) { + // path parameter + var projectId string + + vars := mux.Vars(r) + if v, ok := vars["projectId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + projectId = v + } + + // query parameter + urlParams := r.URL.Query() + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + // list roles + roles, err := h.roleUsecase.ListProjectRoles(projectId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ListProjectRoleResponse + out.Roles = make([]domain.GetProjectRoleResponse, len(roles)) + for i, role := range roles { + out.Roles[i] = domain.GetProjectRoleResponse{ + ID: role.RoleID.String(), + Name: role.Role.Name, + OrganizationID: role.Role.OrganizationID, + ProjectID: role.ProjectID.String(), + Description: role.Role.Description, + Creator: role.Role.Creator.String(), + CreatedAt: role.Role.CreatedAt, + UpdatedAt: role.Role.UpdatedAt, + } + } + + if err := serializer.Map(*pg, &out.Pagination); err != nil { + log.InfoWithContext(r.Context(), err) + } + + // response + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetTksRole godoc +// @Tags Role +// @Summary Get Tks Role +// @Description Get Tks Role +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Success 200 {object} domain.GetTksRoleResponse +// @Router /organizations/{organizationId}/roles/{roleId} [get] + +func (h RoleHandler) GetTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + } else { + roleId = v + } + + // get role + role, err := h.roleUsecase.GetTksRole(roleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // response + out := domain.GetTksRoleResponse{ + ID: role.ID.String(), + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectRole godoc +// @Tags Role +// @Summary Get Project Role +// @Description Get Project Role +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param roleId path string true "Role ID" +// @Success 200 {object} domain.GetProjectRoleResponse +// @Router /organizations/{organizationId}/projects/{projectId}/roles/{roleId} [get] + +func (h RoleHandler) GetProjectRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // get role + role, err := h.roleUsecase.GetProjectRole(roleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // response + out := domain.GetProjectRoleResponse{ + ID: role.RoleID.String(), + Name: role.Role.Name, + OrganizationID: role.Role.OrganizationID, + ProjectID: role.ProjectID.String(), + Description: role.Role.Description, + Creator: role.Role.Creator.String(), + CreatedAt: role.Role.CreatedAt, + UpdatedAt: role.Role.UpdatedAt, + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// DeleteTksRole godoc +// @Tags Role +// @Summary Delete Tks Role +// @Description Delete Tks Role +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId} [delete] + +func (h RoleHandler) DeleteTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // delete role + if err := h.roleUsecase.DeleteTksRole(roleId); err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeleteProjectRole godoc +// @Tags Role +// @Summary Delete Project Role +// @Description Delete Project Role +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param roleId path string true "Role ID" +// @Success 200 +// @Router /organizations/{organizationId}/projects/{projectId}/roles/{roleId} [delete] + +func (h RoleHandler) DeleteProjectRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // delete role + if err := h.roleUsecase.DeleteProjectRole(roleId); err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} + +// UpdateTksRole godoc +// @Tags Role +// @Summary Update Tks Role +// @Description Update Tks Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Param body body domain.UpdateTksRoleRequest true "Update Tks Role Request" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId} [put] + +func (h RoleHandler) UpdateTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // request body + input := domain.UpdateTksRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // input to dto + roleIdUuid, err := uuid.Parse(roleId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + dto := domain.TksRole{ + Role: domain.Role{ + ID: roleIdUuid, + Name: input.Name, + Description: input.Description, + }, + } + + // update role + if err := h.roleUsecase.UpdateTksRole(&dto); err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} + +// UpdateProjectRole godoc +// @Tags Role +// @Summary Update Project Role +// @Description Update Project Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param roleId path string true "Role ID" +// @Param body body domain.UpdateProjectRoleRequest true "Update Project Role Request" +// @Success 200 +// @Router /organizations/{organizationId}/projects/{projectId}/roles/{roleId} [put] + +func (h RoleHandler) UpdateProjectRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // request body + input := domain.UpdateProjectRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // input to dto + roleIdUuid, err := uuid.Parse(roleId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + dto := domain.ProjectRole{ + Role: domain.Role{ + ID: roleIdUuid, + Name: input.Name, + Description: input.Description, + }, + } + + // update role + if err := h.roleUsecase.UpdateProjectRole(&dto); err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} diff --git a/internal/helper/util.go b/internal/helper/util.go index 59933cbf..5919d7b7 100644 --- a/internal/helper/util.go +++ b/internal/helper/util.go @@ -91,3 +91,11 @@ func ToSnakeCase(str string) string { snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") return strings.ToLower(snake) } + +func BoolP(value bool) *bool { + return &value +} + +func UUIDP(value uuid.UUID) *uuid.UUID { + return &value +} diff --git a/internal/middleware/auth/role/default.go b/internal/middleware/auth/role/default.go new file mode 100644 index 00000000..41bf5652 --- /dev/null +++ b/internal/middleware/auth/role/default.go @@ -0,0 +1,324 @@ +package role + +import internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + +var defaults = []*defaultPermission{ + &defaultPermissionOfAdminInMaster, + &defaultPermissionOfUserInMaster, + &defaultPermissionOfAdmin, + &defaultPermissionOfUser, + &defaultLeaderPermission, + &defaultMemberPermission, + &defaultViewerPermission, +} + +func getDefaultPermissions() []*defaultPermission { + return defaults +} + +type defaultPermission struct { + role Role + permissions *[]internalApi.Endpoint +} + +var defaultPermissionOfAdminInMaster = defaultPermission{ + role: Admin, + permissions: &[]internalApi.Endpoint{}, +} + +var defaultPermissionOfUserInMaster = defaultPermission{ + role: User, + permissions: &[]internalApi.Endpoint{}, +} + +var defaultPermissionOfAdmin = defaultPermission{ + role: Admin, + permissions: &[]internalApi.Endpoint{ + // Auth + internalApi.Logout, + internalApi.RefreshToken, + internalApi.VerifyToken, + + // User + internalApi.CreateUser, + internalApi.ListUser, + internalApi.GetUser, + internalApi.DeleteUser, + internalApi.UpdateUser, + internalApi.ResetPassword, + internalApi.CheckId, + internalApi.CheckEmail, + + // MyProfile + internalApi.GetMyProfile, + internalApi.UpdateMyProfile, + internalApi.UpdateMyPassword, + internalApi.RenewPasswordExpiredDate, + internalApi.DeleteMyProfile, + + // Organization + internalApi.CreateOrganization, + internalApi.GetOrganizations, + internalApi.GetOrganization, + internalApi.DeleteOrganization, + internalApi.UpdateOrganization, + + // Cluster + internalApi.UpdatePrimaryCluster, + internalApi.CreateCluster, + internalApi.GetClusters, + internalApi.ImportCluster, + internalApi.GetCluster, + internalApi.DeleteCluster, + internalApi.GetClusterSiteValues, + internalApi.InstallCluster, + internalApi.CreateBootstrapKubeconfig, + internalApi.GetBootstrapKubeconfig, + internalApi.GetNodes, + + // Appgroup + internalApi.CreateAppgroup, + internalApi.GetAppgroups, + internalApi.GetAppgroup, + internalApi.DeleteAppgroup, + internalApi.GetApplications, + internalApi.CreateApplication, + + // AppServeApp + internalApi.CreateAppServeApp, + internalApi.GetAppServeApps, + internalApi.GetNumOfAppsOnStack, + internalApi.GetAppServeApp, + internalApi.GetAppServeAppLatestTask, + internalApi.IsAppServeAppExist, + internalApi.IsAppServeAppNameExist, + internalApi.DeleteAppServeApp, + internalApi.UpdateAppServeApp, + internalApi.UpdateAppServeAppStatus, + internalApi.UpdateAppServeAppEndpoint, + internalApi.RollbackAppServeApp, + + // CloudAccount + internalApi.GetCloudAccounts, + internalApi.CreateCloudAccount, + internalApi.CheckCloudAccountName, + internalApi.CheckAwsAccountId, + internalApi.GetCloudAccount, + internalApi.UpdateCloudAccount, + internalApi.DeleteCloudAccount, + internalApi.DeleteForceCloudAccount, + internalApi.GetResourceQuota, + + // StackTemplate + internalApi.GetStackTemplates, + internalApi.CreateStackTemplate, + internalApi.GetStackTemplate, + internalApi.UpdateStackTemplate, + internalApi.DeleteStackTemplate, + + // Dashboard + internalApi.GetChartsDashboard, + internalApi.GetChartDashboard, + internalApi.GetStacksDashboard, + internalApi.GetResourcesDashboard, + + // Alert + internalApi.CreateAlert, + internalApi.GetAlerts, + internalApi.GetAlert, + internalApi.DeleteAlert, + internalApi.UpdateAlert, + internalApi.CreateAlertAction, + + // Stack + internalApi.GetStacks, + internalApi.CreateStack, + internalApi.CheckStackName, + internalApi.GetStack, + internalApi.UpdateStack, + internalApi.DeleteStack, + internalApi.GetStackKubeConfig, + internalApi.GetStackStatus, + internalApi.SetFavoriteStack, + internalApi.DeleteFavoriteStack, + internalApi.InstallStack, + + // Project + internalApi.CreateProject, + internalApi.GetProjects, + internalApi.GetProject, + internalApi.UpdateProject, + internalApi.DeleteProject, + internalApi.AddProjectMember, + internalApi.GetProjectMembers, + internalApi.RemoveProjectMember, + internalApi.UpdateProjectMemberRole, + internalApi.CreateProjectNamespace, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.DeleteProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +// TODO: check-up the permission of User +var defaultPermissionOfUser = defaultPermission{ + role: User, + permissions: &[]internalApi.Endpoint{ + // Auth + internalApi.Logout, + internalApi.RefreshToken, + internalApi.VerifyToken, + + // User + internalApi.ListUser, + internalApi.GetUser, + internalApi.CheckId, + internalApi.CheckEmail, + + // MyProfile + internalApi.GetMyProfile, + internalApi.UpdateMyProfile, + internalApi.UpdateMyPassword, + internalApi.RenewPasswordExpiredDate, + internalApi.DeleteMyProfile, + + // Organization + internalApi.GetOrganizations, + internalApi.GetOrganization, + + // Cluster + internalApi.GetClusters, + internalApi.GetCluster, + internalApi.GetClusterSiteValues, + internalApi.GetBootstrapKubeconfig, + internalApi.GetNodes, + + // Appgroup + internalApi.CreateAppgroup, + internalApi.GetAppgroups, + internalApi.GetAppgroup, + internalApi.DeleteAppgroup, + internalApi.GetApplications, + internalApi.CreateApplication, + + // AppServeApp + internalApi.CreateAppServeApp, + internalApi.GetAppServeApps, + internalApi.GetNumOfAppsOnStack, + internalApi.GetAppServeApp, + internalApi.GetAppServeAppLatestTask, + internalApi.IsAppServeAppExist, + internalApi.IsAppServeAppNameExist, + internalApi.DeleteAppServeApp, + internalApi.UpdateAppServeApp, + internalApi.UpdateAppServeAppStatus, + internalApi.UpdateAppServeAppEndpoint, + internalApi.RollbackAppServeApp, + + // CloudAccount + internalApi.GetCloudAccounts, + internalApi.GetCloudAccount, + internalApi.GetResourceQuota, + + // StackTemplate + internalApi.GetStackTemplates, + internalApi.GetStackTemplate, + + // Dashboard + internalApi.GetChartsDashboard, + internalApi.GetChartDashboard, + internalApi.GetStacksDashboard, + internalApi.GetResourcesDashboard, + + // Alert + internalApi.GetAlerts, + internalApi.GetAlert, + + // Stack + internalApi.GetStacks, + internalApi.GetStack, + internalApi.GetStackKubeConfig, + internalApi.GetStackStatus, + internalApi.SetFavoriteStack, + internalApi.DeleteFavoriteStack, + + // Project + internalApi.CreateProject, + internalApi.GetProjects, + internalApi.GetProject, + internalApi.UpdateProject, + internalApi.DeleteProject, + internalApi.AddProjectMember, + internalApi.GetProjectMembers, + internalApi.RemoveProjectMember, + internalApi.UpdateProjectMemberRole, + internalApi.CreateProjectNamespace, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.DeleteProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +var defaultLeaderPermission = defaultPermission{ + role: leader, + permissions: &[]internalApi.Endpoint{ + // Project + internalApi.CreateProject, + internalApi.GetProjects, + internalApi.GetProject, + internalApi.UpdateProject, + internalApi.DeleteProject, + internalApi.AddProjectMember, + internalApi.GetProjectMembers, + internalApi.RemoveProjectMember, + internalApi.UpdateProjectMemberRole, + internalApi.CreateProjectNamespace, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.DeleteProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +var defaultMemberPermission = defaultPermission{ + role: member, + permissions: &[]internalApi.Endpoint{ + // Project + internalApi.GetProjects, + internalApi.GetProject, + internalApi.GetProjectMembers, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +var defaultViewerPermission = defaultPermission{ + role: viewer, + permissions: &[]internalApi.Endpoint{ + // Project + internalApi.GetProjects, + internalApi.GetProject, + internalApi.GetProjectMembers, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} diff --git a/internal/middleware/auth/role/role.go b/internal/middleware/auth/role/role.go index b8908590..986de7d1 100644 --- a/internal/middleware/auth/role/role.go +++ b/internal/middleware/auth/role/role.go @@ -8,20 +8,34 @@ const ( Admin Role = "admin" User Role = "user" leader Role = "leader" + member Role = "member" + viewer Role = "viewer" ) +func init() { + RBAC = make(map[internalApi.Endpoint]map[Role]struct{}) + + defaultPermissions := getDefaultPermissions() + registerRole(defaultPermissions...) +} + func (r Role) String() string { return string(r) } -var RBAC = map[internalApi.Endpoint][]Role{ - internalApi.CreateProject: {Admin, User, leader}, - internalApi.UpdateProject: {Admin, User, leader}, - internalApi.DeleteProject: {Admin, User, leader}, - internalApi.GetProject: {Admin, User, leader}, - internalApi.GetProjects: {Admin, User, leader}, +func registerRole(dps ...*defaultPermission) { + for _, dp := range dps { + for _, endpoint := range *dp.permissions { + if RBAC[endpoint] == nil { + RBAC[endpoint] = make(map[Role]struct{}) + } + RBAC[endpoint][dp.role] = struct{}{} + } + } } +var RBAC map[internalApi.Endpoint]map[Role]struct{} + func StrToRole(role string) Role { switch role { case "admin": @@ -35,10 +49,13 @@ func StrToRole(role string) Role { } } func IsRoleAllowed(endpoint internalApi.Endpoint, role Role) bool { - for _, r := range RBAC[endpoint] { - if r == role { - return true - } + if RBAC[endpoint] == nil { + return false } + + if _, ok := RBAC[endpoint][role]; ok { + return true + } + return false } diff --git a/internal/repository/endpoint.go b/internal/repository/endpoint.go new file mode 100644 index 00000000..16fd5968 --- /dev/null +++ b/internal/repository/endpoint.go @@ -0,0 +1,140 @@ +package repository + +import ( + "fmt" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" + "math" +) + +type IEndpointRepository interface { + Create(endpoint *domain.Endpoint) error + List(pg *pagination.Pagination) ([]*domain.Endpoint, error) + Get(id uuid.UUID) (*domain.Endpoint, error) + Update(endpoint *domain.Endpoint) error + Delete(id uuid.UUID) error +} + +type EndpointRepository struct { + db *gorm.DB +} + +func NewEndpointRepository(db *gorm.DB) *EndpointRepository { + return &EndpointRepository{ + db: db, + } +} + +type Endpoint struct { + gorm.Model + + ID uuid.UUID `gorm:"type:uuid;primary_key;"` + + Name string `gorm:"type:text;not null;unique"` + Group string `gorm:"type:text;"` + PermissionID uuid.UUID `gorm:"type:uuid;"` + Permission Permission `gorm:"foreignKey:PermissionID;"` +} + +func (e *Endpoint) BeforeCreate(tx *gorm.DB) error { + e.ID = uuid.New() + return nil +} + +func (e *EndpointRepository) Create(endpoint *domain.Endpoint) error { + obj := &Endpoint{ + Name: endpoint.Name, + Group: endpoint.Group, + PermissionID: endpoint.PermissionID, + } + + if err := e.db.Create(obj).Error; err != nil { + return err + } + + return nil +} + +func (e *EndpointRepository) List(pg *pagination.Pagination) ([]*domain.Endpoint, error) { + var endpoints []*domain.Endpoint + var objs []*Endpoint + + if pg == nil { + pg = pagination.NewDefaultPagination() + } + filterFunc := CombinedGormFilter("endpoints", pg.GetFilters(), pg.CombinedFilter) + db := filterFunc(e.db.Model(&Endpoint{})) + + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Preload("Permissions").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + if res.Error != nil { + return nil, res.Error + } + for _, obj := range objs { + endpoints = append(endpoints, &domain.Endpoint{ + ID: obj.ID, + Name: obj.Name, + Group: obj.Group, + PermissionID: obj.PermissionID, + Permission: *ConvertRepoToDomainPermission(&obj.Permission), + }) + } + + return endpoints, nil +} + +func (e *EndpointRepository) Get(id uuid.UUID) (*domain.Endpoint, error) { + var obj Endpoint + + if err := e.db.Preload("Permission").First(&obj, "id = ?", id).Error; err != nil { + return nil, err + } + + return &domain.Endpoint{ + ID: obj.ID, + Name: obj.Name, + Group: obj.Group, + }, nil +} + +func (e *EndpointRepository) Update(endpoint *domain.Endpoint) error { + obj := &Endpoint{ + ID: endpoint.ID, + Name: endpoint.Name, + Group: endpoint.Group, + PermissionID: endpoint.PermissionID, + } + + if err := e.db.Save(obj).Error; err != nil { + return err + } + + return nil +} + +// domain.Endpoint to repository.Endpoint +func ConvertDomainToRepoEndpoint(endpoint *domain.Endpoint) *Endpoint { + return &Endpoint{ + ID: endpoint.ID, + Name: endpoint.Name, + Group: endpoint.Group, + PermissionID: endpoint.PermissionID, + Permission: *ConvertDomainToRepoPermission(&endpoint.Permission), + } +} + +// repository.Endpoint to domain.Endpoint +func ConvertRepoToDomainEndpoint(endpoint *Endpoint) *domain.Endpoint { + return &domain.Endpoint{ + ID: endpoint.ID, + Name: endpoint.Name, + Group: endpoint.Group, + PermissionID: endpoint.PermissionID, + Permission: *ConvertRepoToDomainPermission(&endpoint.Permission), + } +} diff --git a/internal/repository/endpoint_test.go b/internal/repository/endpoint_test.go new file mode 100644 index 00000000..f27a9f06 --- /dev/null +++ b/internal/repository/endpoint_test.go @@ -0,0 +1,75 @@ +package repository_test + +import ( + "fmt" + myRepository "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm_test/config" + "testing" +) + +func TestNewEndpointRepository(t *testing.T) { + conf := config.NewDefaultConfig() + dsn := fmt.Sprintf( + "host=%s dbname=%s user=%s password=%s port=%d sslmode=disable TimeZone=Asia/Seoul", + conf.Address, conf.Database, conf.AdminId, conf.AdminPassword, conf.Port, + ) + + t.Logf("dsn: %s", dsn) + + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + panic(err) + } + + if err = db.AutoMigrate(&myRepository.Endpoint{}); err != nil { + panic(err) + } + + dbClient := myRepository.NewEndpointRepository(db) + + // create + endpoint := &domain.Endpoint{ + Name: "test", + Group: "test", + } + + if err := dbClient.Create(endpoint); err != nil { + panic(err) + } + + t.Log("list") + // list + endpoints, err := dbClient.List(nil) + if err != nil { + panic(err) + } + + for _, endpoint := range endpoints { + t.Logf("endpoint: %+v", endpoint) + } + + t.Log("get") + // get + for _, endpoint := range endpoints { + endpoint, err := dbClient.Get(endpoint.ID) + if err != nil { + panic(err) + } + t.Logf("endpoint: %+v", endpoint) + } + + t.Log("update") + // update + for _, endpoint := range endpoints { + endpoint.Name = "test2" + t.Logf("BeforeUpdate: %+v", endpoint) + if err := dbClient.Update(endpoint); err != nil { + panic(err) + } else { + t.Logf("AfterUpdate: %+v", endpoint) + } + } +} diff --git a/internal/repository/organization.go b/internal/repository/organization.go index e37fc7e4..12e31962 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -168,3 +168,24 @@ func (r *OrganizationRepository) reflect(organization Organization) (out domain. return } + +func ConvertDomainToRepoOrganization(organization *domain.Organization) *Organization { + + return &Organization{ + ID: organization.ID, + Name: organization.Name, + Description: organization.Description, + Phone: organization.Phone, + Creator: uuid.MustParse(organization.Creator), + } +} + +func ConvertRepoToDomainOrganization(organization *Organization) *domain.Organization { + return &domain.Organization{ + ID: organization.ID, + Name: organization.Name, + Description: organization.Description, + Phone: organization.Phone, + Creator: organization.Creator.String(), + } +} diff --git a/internal/repository/permission.go b/internal/repository/permission.go new file mode 100644 index 00000000..0580244c --- /dev/null +++ b/internal/repository/permission.go @@ -0,0 +1,219 @@ +package repository + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" +) + +type IPermissionRepository interface { + Create(permission *domain.Permission) error + List() ([]*domain.Permission, error) + Get(id uuid.UUID) (*domain.Permission, error) + Delete(id uuid.UUID) error + Update(permission *domain.Permission) error +} + +type PermissionRepository struct { + db *gorm.DB +} + +func NewPermissionRepository(db *gorm.DB) *PermissionRepository { + return &PermissionRepository{ + db: db, + } +} + +type Permission struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid;"` + Name string + + IsAllowed *bool `gorm:"type:boolean;"` + RoleID *uuid.UUID + Role *Role `gorm:"foreignKey:RoleID;references:ID;"` + Endpoints []*Endpoint `gorm:"one2many:endpoints;"` + + ParentID *uuid.UUID + Parent *Permission `gorm:"foreignKey:ParentID;references:ID;"` + Children []*Permission `gorm:"foreignKey:ParentID;references:ID;"` +} + +func (p *Permission) BeforeCreate(tx *gorm.DB) (err error) { + if p.ID == uuid.Nil { + p.ID = uuid.New() + } + return nil +} + +func (r PermissionRepository) Create(p *domain.Permission) error { + //var parent *Permission + //var children []*Permission + // + //if p.Parent != nil { + // parent = &Permission{} + // result := r.db.First(&parent, "id = ?", p.Parent.ID) + // if result.Error != nil { + // return result.Error + // } + //} + //if p.Children != nil { + // for _, child := range p.Children { + // newChild := &Permission{} + // result := r.db.First(&newChild, "id = ?", child.ID) + // if result.Error != nil { + // return result.Error + // } + // children = append(children, newChild) + // } + //} + + permission := ConvertDomainToRepoPermission(p) + + return r.db.Create(permission).Error +} + +func (r PermissionRepository) List() ([]*domain.Permission, error) { + var permissions []*Permission + var outs []*domain.Permission + + err := r.db.Preload("Children.Children.Children.Children").Where("parent_id IS NULL").Find(&permissions).Error + if err != nil { + return nil, err + } + + for _, permission := range permissions { + outs = append(outs, ConvertRepoToDomainPermission(permission)) + } + return outs, nil +} + +func (r PermissionRepository) Get(id uuid.UUID) (*domain.Permission, error) { + permission := &Permission{} + result := r.db.Preload("Children.Children.Children").Preload("Parent").First(&permission, "id = ?", id) + if result.Error != nil { + return nil, result.Error + } + + return ConvertRepoToDomainPermission(permission), nil +} + +func (r PermissionRepository) Delete(id uuid.UUID) error { + return r.db.Delete(&Permission{}, id).Error +} + +func (r PermissionRepository) Update(p *domain.Permission) error { + permission := ConvertDomainToRepoPermission(p) + + return r.db.Updates(permission).Error + + //var parent *Permission + //var children []*Permission + // + //if p.Parent != nil { + // parent = &Permission{} + // result := r.db.First(&parent, "id = ?", p.Parent.ID) + // if result.Error != nil { + // return result.Error + // } + //} + //if p.Children != nil { + // for _, child := range p.Children { + // newChild := &Permission{} + // result := r.db.First(&newChild, "id = ?", child.ID) + // if result.Error != nil { + // return result.Error + // } + // children = append(children, newChild) + // } + //} + // + //permission := &Permission{} + // + //result := r.db.First(&permission, "id = ?", p.ID) + //if result.Error != nil { + // return result.Error + //} + // + //permission.Name = p.Name + //permission.Parent = parent + //permission.Children = children + //permission.IsAllowed = p.IsAllowed + // + //return r.db.Save(permission).Error +} + +// repository.Permission to domain.Permission +func ConvertRepoToDomainPermission(repoPerm *Permission) *domain.Permission { + if repoPerm == nil { + return nil + } + + if repoPerm.Endpoints == nil { + repoPerm.Endpoints = []*Endpoint{} + } + var domainEndpoints []*domain.Endpoint + for _, endpoint := range repoPerm.Endpoints { + domainEndpoints = append(domainEndpoints, ConvertRepoToDomainEndpoint(endpoint)) + } + + // Domain Permission 객체 생성 + domainPerm := &domain.Permission{ + ID: repoPerm.ID, + Name: repoPerm.Name, + ParentID: repoPerm.ParentID, + IsAllowed: repoPerm.IsAllowed, + Endpoints: domainEndpoints, + } + + // 자식 권한들 변환 + for _, child := range repoPerm.Children { + domainChild := ConvertRepoToDomainPermission(child) + domainPerm.Children = append(domainPerm.Children, domainChild) + } + + // 부모 권한 변환 (부모 권한이 있을 경우만) + if repoPerm.Parent != nil { + domainPerm.Parent = ConvertRepoToDomainPermission(repoPerm.Parent) + } + + return domainPerm +} + +// domain.Permission to repository.Permission +func ConvertDomainToRepoPermission(domainPerm *domain.Permission) *Permission { + if domainPerm == nil { + return nil + } + + if domainPerm.Endpoints == nil { + domainPerm.Endpoints = []*domain.Endpoint{} + } + var repoEndpoints []*Endpoint + for _, endpoint := range domainPerm.Endpoints { + repoEndpoints = append(repoEndpoints, ConvertDomainToRepoEndpoint(endpoint)) + } + + // Domain Permission 객체 생성 + repoPerm := &Permission{ + ID: domainPerm.ID, + Name: domainPerm.Name, + ParentID: domainPerm.ParentID, + IsAllowed: domainPerm.IsAllowed, + Endpoints: repoEndpoints, + } + + // 자식 권한들 변환 + for _, child := range domainPerm.Children { + repoChild := ConvertDomainToRepoPermission(child) + repoPerm.Children = append(repoPerm.Children, repoChild) + } + + // 부모 권한 변환 (부모 권한이 있을 경우만) + if domainPerm.Parent != nil { + repoPerm.Parent = ConvertDomainToRepoPermission(domainPerm.Parent) + } + + return repoPerm +} diff --git a/internal/repository/permission_test.go b/internal/repository/permission_test.go new file mode 100644 index 00000000..dad9d96e --- /dev/null +++ b/internal/repository/permission_test.go @@ -0,0 +1,445 @@ +package repository_test + +import ( + "encoding/json" + "fmt" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm_test/config" + "testing" +) + +func TestPermission(t *testing.T) { + db := db_connection() + + db.AutoMigrate(&repository.Permission{}) + + //model := domain.Permission{ + // Name: "대시보드", + //} + + repo := repository.NewPermissionRepository(db) + + permissions, err := repo.List() + if err != nil { + t.Fatal(err) + } + out, err := json.MarshalIndent(permissions, "", " ") + if err != nil { + t.Fatal(err) + } + t.Log("start") + t.Logf("permission: %s", string(out)) + t.Log("end") + + //t.Logf("permission: %+v", permissions) + + // + //for _, permission := range permissions { + // // encoding to json + // b, err := json.Marshal(permission) + // if err != nil { + // t.Fatal(err) + // } + // t.Logf("permission: %s", string(b)) + //} + // + ////create + + //if err := repo.Create(dashboard); err != nil { + // t.Fatal(err) + //} + //if err := repo.Create(stack); err != nil { + // t.Fatal(err) + //} + //if err := repo.Create(security_policy); err != nil { + // t.Fatal(err) + //} + //if err := repo.Create(projectManagement); err != nil { + // t.Fatal(err) + //} + //if err := repo.Create(notification); err != nil { + // t.Fatal(err) + //} + //if err := repo.Create(configuration); err != nil { + // t.Fatal(err) + //} + + // get + //permission, err := repo.Get(uuid.MustParse("fd4363a7-d1d2-4feb-b976-b87d99a775c4")) + //if err != nil { + // t.Fatal(err) + //} + //out, err := json.Marshal(permission) + //if err != nil { + // t.Fatal(err) + //} + //t.Logf("permission: %s", string(out)) + + // print json pretty + //out, err := json.MarshalIndent(permission, "", " ") + //if err != nil { + // t.Fatal(err) + //} + //t.Logf("permission: %s", string(out)) + +} + +func db_connection() *gorm.DB { + conf := config.NewDefaultConfig() + dsn := fmt.Sprintf( + "host=%s dbname=%s user=%s password=%s port=%d sslmode=disable TimeZone=Asia/Seoul", + conf.Address, conf.Database, conf.AdminId, conf.AdminPassword, conf.Port, + ) + + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + panic(err) + } + + return db +} + +var dashboard = &domain.Permission{ + Name: "대시보드", + Children: []*domain.Permission{ + { + Name: "대시보드", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "대시보드 설정", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, +} + +var stack = &domain.Permission{ + Name: "스택 관리", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, +} + +var security_policy = &domain.Permission{ + Name: "보안/정책 관리", + Children: []*domain.Permission{ + { + Name: "보안/정책", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, +} + +var projectManagement = &domain.Permission{ + Name: "프로젝트 관리", + Children: []*domain.Permission{ + { + Name: "프로젝트", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "앱 서빙", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "빌드", + IsAllowed: helper.BoolP(false), + }, + { + Name: "배포", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "설정-일반", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "설정-멤버", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "설정-네임스페이스", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, +} + +var notification = &domain.Permission{ + Name: "알림", + Children: []*domain.Permission{ + { + Name: "시스템 경고", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "보안/정책 감사로그", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, +} + +var configuration = &domain.Permission{ + Name: "설정", + Children: []*domain.Permission{ + { + Name: "일반", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "클라우드 계정", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "스택 템플릿", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "프로젝트 관리", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "사용자", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "사용자 권한 관리", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "알림 설정", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 09ab4035..e176a7c8 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -21,6 +21,7 @@ type Repository struct { CloudAccount ICloudAccountRepository StackTemplate IStackTemplateRepository Alert IAlertRepository + Role IRoleRepository Project IProjectRepository Audit IAuditRepository } diff --git a/internal/repository/role.go b/internal/repository/role.go new file mode 100644 index 00000000..f8906c11 --- /dev/null +++ b/internal/repository/role.go @@ -0,0 +1,303 @@ +package repository + +import ( + "fmt" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" + "math" +) + +type Role struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid;"` + Name string + OrganizationID string + Organization Organization `gorm:"foreignKey:OrganizationID;references:ID;"` + Type string + Creator uuid.UUID + Description string +} + +func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { + if r.ID == uuid.Nil { + r.ID = uuid.New() + } + return nil +} + +type TksRole struct { + RoleID uuid.UUID `gorm:"type:uuid;primary_key;"` + Role Role `gorm:"foreignKey:RoleID;references:ID;"` +} + +type ProjectRole struct { + RoleID uuid.UUID `gorm:"type:uuid;primary_key;"` + Role Role `gorm:"foreignKey:RoleID;references:ID;"` + ProjectID uuid.UUID + Project Project `gorm:"foreignKey:ProjectID;references:ID;"` +} + +type IRoleRepository interface { + Create(roleObj interface{}) error + List(pg *pagination.Pagination) ([]*domain.Role, error) + ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) + ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) + Get(id uuid.UUID) (*domain.Role, error) + GetTksRole(id uuid.UUID) (*domain.TksRole, error) + GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error) + DeleteCascade(id uuid.UUID) error + Update(roleObj interface{}) error +} + +type RoleRepository struct { + db *gorm.DB +} + +func (r RoleRepository) Create(roleObj interface{}) error { + if roleObj == nil { + return fmt.Errorf("roleObj is nil") + } + switch roleObj.(type) { + case *domain.TksRole: + inputRole := roleObj.(*domain.TksRole) + role := ConvertDomainToRepoTksRole(inputRole) + if err := r.db.Create(role).Error; err != nil { + return err + } + + case *domain.ProjectRole: + inputRole := roleObj.(*domain.ProjectRole) + role := ConvertDomainToRepoProjectRole(inputRole) + if err := r.db.Create(role).Error; err != nil { + return err + } + } + + return nil +} + +func (r RoleRepository) List(pg *pagination.Pagination) ([]*domain.Role, error) { + var roles []*domain.Role + var objs []*Role + + if pg == nil { + pg = pagination.NewDefaultPagination() + } + filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) + db := filterFunc(r.db.Model(&Role{})) + + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + //res := db.Joins("JOIN roles as r on r.id = tks_roles.role_id"). + // Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + + if res.Error != nil { + return nil, res.Error + } + for _, role := range objs { + roles = append(roles, ConvertRepoToDomainRole(role)) + } + + return roles, nil +} + +func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) { + var roles []*domain.TksRole + var objs []*TksRole + + if pg == nil { + pg = pagination.NewDefaultPagination() + } + filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) + db := filterFunc(r.db.Model(&TksRole{})) + + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Joins("JOIN roles as r on r.id = tks_roles.role_id"). + Where("r.organization_id = ?", organizationId). + Offset(pg.GetOffset()). + Limit(pg.GetLimit()). + Order(orderQuery). + Find(&objs) + //res := db.Preload("Role").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + if res.Error != nil { + return nil, res.Error + } + for _, role := range objs { + roles = append(roles, ConvertRepoToDomainTksRole(role)) + } + + return roles, nil +} + +func (r RoleRepository) ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) { + var roles []*domain.ProjectRole + var objs []*ProjectRole + + if pg == nil { + pg = pagination.NewDefaultPagination() + } + filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) + db := filterFunc(r.db.Model(&ProjectRole{})) + + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Joins("JOIN roles as r on r.id = project_roles.role_id"). + Where("project_roles.project_id = ?", projectId). + Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + //res := db.Preload("Role").Preload("Project").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + if res.Error != nil { + return nil, res.Error + } + for _, role := range objs { + roles = append(roles, ConvertRepoToDomainProjectRole(role)) + } + + return roles, nil +} + +func (r RoleRepository) Get(id uuid.UUID) (*domain.Role, error) { + var role Role + if err := r.db.First(&role, "id = ?", id).Error; err != nil { + return nil, err + } + + return ConvertRepoToDomainRole(&role), nil +} + +func (r RoleRepository) GetTksRole(id uuid.UUID) (*domain.TksRole, error) { + var role TksRole + if err := r.db.Preload("Role").First(&role, "role_id = ?", id).Error; err != nil { + return nil, err + } + + return ConvertRepoToDomainTksRole(&role), nil +} + +func (r RoleRepository) GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error) { + var role ProjectRole + if err := r.db.Preload("Role").Preload("Project").First(&role, "role_id = ?", id).Error; err != nil { + return nil, err + } + + return ConvertRepoToDomainProjectRole(&role), nil +} + +func (r RoleRepository) DeleteCascade(id uuid.UUID) error { + // manual cascade delete + if err := r.db.Delete(&TksRole{}, "role_id = ?", id).Error; err != nil { + return err + } + if err := r.db.Delete(&ProjectRole{}, "role_id = ?", id).Error; err != nil { + return err + } + + if err := r.db.Delete(&Role{}, "id = ?", id).Error; err != nil { + return err + } + + return nil +} + +func (r RoleRepository) Update(roleObj interface{}) error { + switch roleObj.(type) { + case *TksRole: + inputRole := roleObj.(*TksRole) + role := ConvertRepoToDomainTksRole(inputRole) + if err := r.db.Model(&TksRole{}).Where("id = ?", role.ID).Updates(Role{ + Name: role.Name, + Description: role.Description, + }).Error; err != nil { + return err + } + + case *ProjectRole: + inputRole := roleObj.(*ProjectRole) + projectRole := ConvertRepoToDomainProjectRole(inputRole) + // update role + if err := r.db.Model(&ProjectRole{}).Where("role_id = ?", projectRole.RoleID).Updates(Role{ + Name: projectRole.Role.Name, + Description: projectRole.Role.Description, + }).Error; err != nil { + return err + } + } + + return nil +} + +func NewRoleRepository(db *gorm.DB) IRoleRepository { + return &RoleRepository{ + db: db, + } +} + +// domain.Role to repository.Role +func ConverDomainToRepoRole(domainRole *domain.Role) *Role { + return &Role{ + ID: domainRole.ID, + Name: domainRole.Name, + OrganizationID: domainRole.OrganizationID, + Type: domainRole.Type, + Creator: domainRole.Creator, + Description: domainRole.Description, + } +} + +// repository.Role to domain.Role +func ConvertRepoToDomainRole(repoRole *Role) *domain.Role { + return &domain.Role{ + ID: repoRole.ID, + Name: repoRole.Name, + OrganizationID: repoRole.OrganizationID, + Type: repoRole.Type, + Creator: repoRole.Creator, + Description: repoRole.Description, + } +} + +// domain.TksRole to repository.TksRole +func ConvertDomainToRepoTksRole(domainRole *domain.TksRole) *TksRole { + return &TksRole{ + RoleID: domainRole.Role.ID, + Role: *ConverDomainToRepoRole(&domainRole.Role), + } +} + +// repository.TksRole to domain.TksRole +func ConvertRepoToDomainTksRole(repoRole *TksRole) *domain.TksRole { + return &domain.TksRole{ + RoleID: repoRole.RoleID, + Role: *ConvertRepoToDomainRole(&repoRole.Role), + } +} + +// domain.ProjectRole to repository.ProjectRole +func ConvertDomainToRepoProjectRole(domainRole *domain.ProjectRole) *ProjectRole { + return &ProjectRole{ + RoleID: domainRole.RoleID, + ProjectID: domainRole.ProjectID, + Role: *ConverDomainToRepoRole(&domainRole.Role), + Project: *ConvertDomainToRepoProject(&domainRole.Project), + } +} + +// repository.ProjectRole to domain.ProjectRole +func ConvertRepoToDomainProjectRole(repoRole *ProjectRole) *domain.ProjectRole { + return &domain.ProjectRole{ + RoleID: repoRole.RoleID, + ProjectID: repoRole.ProjectID, + Role: *ConvertRepoToDomainRole(&repoRole.Role), + Project: *ConvertRepoToDomainProject(&repoRole.Project), + } +} diff --git a/internal/repository/role_test.go b/internal/repository/role_test.go new file mode 100644 index 00000000..8c9d78a8 --- /dev/null +++ b/internal/repository/role_test.go @@ -0,0 +1,28 @@ +package repository_test + +import ( + "encoding/json" + "github.com/openinfradev/tks-api/internal/repository" + "testing" +) + +func TestRole(t *testing.T) { + db := db_connection() + + db.AutoMigrate(&repository.Role{}, &repository.Organization{}, &repository.Project{}, &repository.User{}, &repository.TksRole{}, &repository.ProjectRole{}) + + repo := repository.NewRoleRepository(db) + + roles, err := repo.List() + if err != nil { + t.Fatal(err) + } + out, err := json.MarshalIndent(roles, "", " ") + if err != nil { + t.Fatal(err) + } + t.Log("start") + t.Logf("role: %s", string(out)) + t.Log("end") + +} diff --git a/internal/repository/user.go b/internal/repository/user.go index 84120da9..5bf6ad10 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -276,20 +276,7 @@ func (r *UserRepository) DeleteWithUuid(uuid uuid.UUID) error { return nil } -type Role struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid;"` - Name string - Description string - Creator uuid.UUID -} - -func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { - r.ID = uuid.New() - return nil -} - +// Deprecated: type Policy struct { gorm.Model @@ -431,10 +418,10 @@ func (r *UserRepository) getRoleByName(roleName string) (Role, error) { func (r *UserRepository) reflect(user User) domain.User { role := domain.Role{ - ID: user.Role.ID.String(), + ID: user.Role.ID, Name: user.Role.Name, Description: user.Role.Description, - Creator: user.Role.Creator.String(), + Creator: user.Role.Creator, CreatedAt: user.Role.CreatedAt, UpdatedAt: user.Role.UpdatedAt, } @@ -492,10 +479,10 @@ func (r *UserRepository) reflect(user User) domain.User { func (r *UserRepository) reflectRole(role Role) domain.Role { return domain.Role{ - ID: role.ID.String(), + ID: role.ID, Name: role.Name, Description: role.Description, - Creator: role.Creator.String(), + Creator: role.Creator, CreatedAt: role.CreatedAt, UpdatedAt: role.UpdatedAt, } diff --git a/internal/route/route.go b/internal/route/route.go index 4382baa1..ede3ff36 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -50,6 +50,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa CloudAccount: repository.NewCloudAccountRepository(db), StackTemplate: repository.NewStackTemplateRepository(db), Alert: repository.NewAlertRepository(db), + Role: repository.NewRoleRepository(db), Project: repository.NewProjectRepository(db), Audit: repository.NewAuditRepository(db), } @@ -231,6 +232,18 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/audits/{auditId}", customMiddleware.Handle(internalApi.GetAudit, http.HandlerFunc(auditHandler.GetAudit))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/audits/{auditId}", customMiddleware.Handle(internalApi.DeleteAudit, http.HandlerFunc(auditHandler.DeleteAudit))).Methods(http.MethodDelete) + roleHandler := delivery.NewRoleHandler(usecase.NewRoleUsecase(repoFactory)) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.CreateTksRole, http.HandlerFunc(roleHandler.CreateTksRole))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.ListTksRoles, http.HandlerFunc(roleHandler.ListTksRoles))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.GetTksRole, http.HandlerFunc(roleHandler.GetTksRole))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.DeleteTksRole, http.HandlerFunc(roleHandler.DeleteTksRole))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.UpdateTksRole, http.HandlerFunc(roleHandler.UpdateTksRole))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles", customMiddleware.Handle(internalApi.CreateProjectRole, http.HandlerFunc(roleHandler.CreateProjectRole))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles", customMiddleware.Handle(internalApi.ListProjectRoles, http.HandlerFunc(roleHandler.ListProjectRoles))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles/{roleId}", customMiddleware.Handle(internalApi.GetProjectRole, http.HandlerFunc(roleHandler.GetProjectRole))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles/{roleId}", customMiddleware.Handle(internalApi.DeleteProjectRole, http.HandlerFunc(roleHandler.DeleteProjectRole))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles/{roleId}", customMiddleware.Handle(internalApi.UpdateProjectRole, http.HandlerFunc(roleHandler.UpdateProjectRole))).Methods(http.MethodPut) + r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", alertHandler.CreateAlert).Methods(http.MethodPost) // assets r.PathPrefix("/api/").HandlerFunc(http.NotFound) diff --git a/internal/usecase/permission.go b/internal/usecase/permission.go new file mode 100644 index 00000000..df3c8072 --- /dev/null +++ b/internal/usecase/permission.go @@ -0,0 +1,45 @@ +package usecase + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" +) + +type IPermissionUsecase interface { + CreatePermission(permission *domain.Permission) error + ListPermissions() ([]*domain.Permission, error) + GetPermission(id uuid.UUID) (*domain.Permission, error) + DeletePermission(id uuid.UUID) error + UpdatePermission(permission *domain.Permission) error +} + +type PermissionUsecase struct { + repo repository.IPermissionRepository +} + +func NewPermissionUsecase(repo repository.IPermissionRepository) *PermissionUsecase { + return &PermissionUsecase{ + repo: repo, + } +} + +func (p PermissionUsecase) CreatePermission(permission *domain.Permission) error { + return p.repo.Create(permission) +} + +func (p PermissionUsecase) ListPermissions() ([]*domain.Permission, error) { + return p.repo.List() +} + +func (p PermissionUsecase) GetPermission(id uuid.UUID) (*domain.Permission, error) { + return p.repo.Get(id) +} + +func (p PermissionUsecase) DeletePermission(id uuid.UUID) error { + return p.repo.Delete(id) +} + +func (p PermissionUsecase) UpdatePermission(permission *domain.Permission) error { + return p.repo.Update(permission) +} diff --git a/internal/usecase/role.go b/internal/usecase/role.go new file mode 100644 index 00000000..fb6dbc56 --- /dev/null +++ b/internal/usecase/role.go @@ -0,0 +1,126 @@ +package usecase + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" +) + +type IRoleUsecase interface { + CreateTksRole(role *domain.TksRole) error + CreateProjectRole(role *domain.ProjectRole) error + ListRoles(pg *pagination.Pagination) ([]*domain.Role, error) + ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) + ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) + GetTksRole(id string) (*domain.TksRole, error) + GetProjectRole(id string) (*domain.ProjectRole, error) + DeleteTksRole(id string) error + DeleteProjectRole(id string) error + UpdateTksRole(role *domain.TksRole) error + UpdateProjectRole(role *domain.ProjectRole) error +} + +type RoleUsecase struct { + repo repository.IRoleRepository +} + +func NewRoleUsecase(repo repository.Repository) *RoleUsecase { + return &RoleUsecase{ + repo: repo.Role, + } +} + +func (r RoleUsecase) CreateTksRole(role *domain.TksRole) error { + return r.repo.Create(role) +} + +func (r RoleUsecase) CreateProjectRole(role *domain.ProjectRole) error { + return r.repo.Create(role) +} + +func (r RoleUsecase) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) { + roles, err := r.repo.ListTksRoles(organizationId, pg) + if err != nil { + return nil, err + } + + return roles, nil +} + +func (r RoleUsecase) ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) { + roles, err := r.repo.ListProjectRoles(projectId, pg) + if err != nil { + return nil, err + } + + return roles, nil +} + +func (r RoleUsecase) ListRoles(pg *pagination.Pagination) ([]*domain.Role, error) { + return r.repo.List(nil) +} + +func (r RoleUsecase) GetTksRole(id string) (*domain.TksRole, error) { + roldId, err := uuid.Parse(id) + if err != nil { + return nil, err + } + + role, err := r.repo.GetTksRole(roldId) + if err != nil { + return nil, err + } + + return role, nil +} + +func (r RoleUsecase) GetProjectRole(id string) (*domain.ProjectRole, error) { + roleId, err := uuid.Parse(id) + if err != nil { + return nil, err + } + + role, err := r.repo.GetProjectRole(roleId) + if err != nil { + return nil, err + } + + return role, nil +} + +func (r RoleUsecase) DeleteTksRole(id string) error { + roleId, err := uuid.Parse(id) + if err != nil { + return err + } + + return r.repo.DeleteCascade(roleId) +} + +func (r RoleUsecase) DeleteProjectRole(id string) error { + roleId, err := uuid.Parse(id) + if err != nil { + return err + } + + return r.repo.DeleteCascade(roleId) +} + +func (r RoleUsecase) UpdateTksRole(role *domain.TksRole) error { + err := r.repo.Update(role) + if err != nil { + return err + } + + return nil +} + +func (r RoleUsecase) UpdateProjectRole(role *domain.ProjectRole) error { + err := r.repo.Update(role) + if err != nil { + return err + } + + return nil +} diff --git a/internal/usecase/role_test.go b/internal/usecase/role_test.go new file mode 100644 index 00000000..10e746c7 --- /dev/null +++ b/internal/usecase/role_test.go @@ -0,0 +1,217 @@ +package usecase_test + +import ( + "fmt" + "github.com/google/uuid" + myRepository "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm_test/config" + "testing" +) + +func TestNewRoleUsecase(t *testing.T) { + conf := config.NewDefaultConfig() + dsn := fmt.Sprintf( + "host=%s dbname=%s user=%s password=%s port=%d sslmode=disable TimeZone=Asia/Seoul", + conf.Address, conf.Database, conf.AdminId, conf.AdminPassword, conf.Port, + ) + + t.Logf("dsn: %s", dsn) + + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + panic(err) + } + + if err = db.AutoMigrate(&myRepository.Organization{}); err != nil { + panic(err) + } + if err = db.AutoMigrate(&myRepository.Role{}); err != nil { + panic(err) + } + if err = db.AutoMigrate(&myRepository.TksRole{}); err != nil { + panic(err) + } + if err = db.AutoMigrate(&myRepository.ProjectRole{}); err != nil { + panic(err) + } + + orgaizationRepository := myRepository.NewOrganizationRepository(db) + roleDbClient := myRepository.NewRoleRepository(db) + + roleUsecase := usecase.NewRoleUsecase(roleDbClient) + + // create organization + // organizationId = "test" + if _, err := orgaizationRepository.Get("test"); err != nil { + if _, err := orgaizationRepository.Create("test", "test", uuid.New(), "test", "test"); err != nil { + panic(err) + } + } + + // create project + // projectName = "testProject" + projectRepository := myRepository.NewProjectRepository(db) + projectFound := false + if projects, err := projectRepository.List(); err != nil { + panic(err) + } else { + for _, project := range projects { + if project.Name == "testProject" { + projectFound = true + break + } + } + } + + if !projectFound { + project := &domain.Project{ + Name: "testProject", + OrganizationID: "test", + Description: "testDesc", + } + if err := projectRepository.Create(project); err != nil { + panic(err) + } + } + + // get id of project + projects, err := projectRepository.List() + if err != nil { + panic(err) + } + var projectId uuid.UUID + for _, project := range projects { + if project.Name == "testProject" { + projectId = project.ID + break + } + } + + // create tks role + tksRoles, err := roleUsecase.ListTksRoles() + if err != nil { + panic(err) + } + tksRoleFound := false + for _, role := range tksRoles { + if role.Name == "testTksRole" { + tksRoleFound = true + break + } + } + if !tksRoleFound { + role := &domain.TksRole{ + Role: domain.Role{ + Name: "testTksRole", + OrganizationID: "test", + Description: "testDesc", + }, + } + if err := roleUsecase.CreateTksRole(role); err != nil { + panic(err) + } + } + + // create project role + projectRoles, err := roleUsecase.ListProjectRoles() + if err != nil { + panic(err) + } + + projectRoleFound := false + for _, role := range projectRoles { + if role.Role.Name == "testProjectRole" { + projectRoleFound = true + break + } + } + + if !projectRoleFound { + role := &domain.ProjectRole{ + Role: domain.Role{ + Name: "testProjectRole", + OrganizationID: "test", + Description: "testDesc", + }, + ProjectID: projectId, + } + if err := roleUsecase.CreateProjectRole(role); err != nil { + panic(err) + } + } + + // list tks role + tksRoles, err = roleUsecase.ListTksRoles() + if err != nil { + panic(err) + } + for i, role := range tksRoles { + t.Logf("index: %d, tksRole: %+v", i, role) + } + + // list project role + projectRoles, err = roleUsecase.ListProjectRoles() + if err != nil { + panic(err) + } + for i, role := range projectRoles { + t.Logf("index: %d, projectRole: %+v", i, role) + } + + // list all role + roles, err := roleUsecase.ListRoles() + if err != nil { + panic(err) + } + for i, role := range roles { + t.Logf("index: %d, role: %+v", i, role) + } + + t.Logf("now delete all roles") + + // delete tks role + for _, role := range tksRoles { + if err := roleDbClient.Delete(role.RoleID); err != nil { + panic(err) + } + } + + // delete project role + for _, role := range projectRoles { + if err := roleDbClient.Delete(role.RoleID); err != nil { + panic(err) + } + } + + // print tks role + tksRoles, err = roleUsecase.ListTksRoles() + if err != nil { + panic(err) + } + for i, role := range tksRoles { + t.Logf("index: %d, tksRole: %+v", i, role) + } + + // print project role + projectRoles, err = roleUsecase.ListProjectRoles() + if err != nil { + panic(err) + } + for i, role := range projectRoles { + t.Logf("index: %d, projectRole: %+v", i, role) + } + + // print all role + roles, err = roleUsecase.ListRoles() + if err != nil { + panic(err) + } + for i, role := range roles { + t.Logf("index: %d, role: %+v", i, role) + } + +} diff --git a/internal/usecase/user.go b/internal/usecase/user.go index f507a7eb..12cfa33a 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -258,7 +258,7 @@ func (u *UserUsecase) CreateAdmin(orgainzationId string, email string) (*domain. user.Role.ID = role.ID } } - roleUuid, err := uuid.Parse(user.Role.ID) + roleUuid := user.Role.ID if err != nil { return nil, err } @@ -444,7 +444,7 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u originPassword := (*users)[0].Password - roleUuid, err := uuid.Parse((*users)[0].Role.ID) + roleUuid := (*users)[0].Role.ID if err != nil { return nil, err } @@ -548,7 +548,7 @@ func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.Us user.Role.ID = role.ID } } - roleUuid, err := uuid.Parse(user.Role.ID) + roleUuid := user.Role.ID if err != nil { return nil, err } @@ -623,7 +623,7 @@ func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId st user.Role.ID = role.ID } } - roleUuid, err := uuid.Parse(user.Role.ID) + roleUuid := user.Role.ID if err != nil { return nil, err } diff --git a/pkg/domain/endpoint.go b/pkg/domain/endpoint.go new file mode 100644 index 00000000..84006eac --- /dev/null +++ b/pkg/domain/endpoint.go @@ -0,0 +1,11 @@ +package domain + +import "github.com/google/uuid" + +type Endpoint struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` + Group string `json:"group"` + PermissionID uuid.UUID `json:"permissionId"` + Permission Permission `json:"permission"` +} diff --git a/pkg/domain/permmision.go b/pkg/domain/permmision.go new file mode 100644 index 00000000..c9b40762 --- /dev/null +++ b/pkg/domain/permmision.go @@ -0,0 +1,18 @@ +package domain + +import "github.com/google/uuid" + +type Permission struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` + + IsAllowed *bool `json:"is_allowed,omitempty"` + RoleID *uuid.UUID `json:"role_id,omitempty"` + Role *Role `json:"role,omitempty"` + Endpoints []*Endpoint `json:"endpoints,omitempty"` + // omit empty + + ParentID *uuid.UUID `json:"parent_id,omitempty"` + Parent *Permission `json:"parent,omitempty"` + Children []*Permission `json:"children,omitempty"` +} diff --git a/pkg/domain/role.go b/pkg/domain/role.go new file mode 100644 index 00000000..d2025254 --- /dev/null +++ b/pkg/domain/role.go @@ -0,0 +1,109 @@ +package domain + +import ( + "github.com/google/uuid" + "time" +) + +type RoleType string + +const ( + RoleTypeDefault RoleType = "default" + RoleTypeTks RoleType = "tks" + RoleTypeProject RoleType = "project" +) + +type Role struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` + OrganizationID string `json:"organizationId"` + Organization Organization `json:"organization"` + Type string `json:"type"` + Description string `json:"description"` + Creator uuid.UUID `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type TksRole struct { + RoleID uuid.UUID `json:"roleId"` + Role +} + +type ProjectRole struct { + RoleID uuid.UUID `json:"roleId"` + Role Role `json:"role"` + ProjectID uuid.UUID `json:"projectID"` + Project Project `json:"project"` +} + +//type Role = struct { +// ID uuid.UUID `json:"id"` +// Name string `json:"name"` +// OrganizationID string `json:"organizationId"` +// Organization Organization `json:"organization"` +// Type string `json:"type"` +// Description string `json:"description"` +// Creator uuid.UUID `json:"creator"` +// CreatedAt time.Time `json:"createdAt"` +// UpdatedAt time.Time `json:"updatedAt"` +//} + +type CreateTksRoleRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` +} + +type CreateTksRoleResponse struct { + ID string `json:"id"` +} + +type GetTksRoleResponse struct { + ID string `json:"id"` + Name string `json:"name"` + OrganizationID string `json:"organizationId"` + Description string `json:"description"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type ListTksRoleResponse struct { + Roles []GetTksRoleResponse `json:"roles"` + Pagination PaginationResponse `json:"pagination"` +} + +type UpdateTksRoleRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` +} + +type CreateProjectRoleRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` +} + +type CreateProjectRoleResponse struct { + ID string `json:"id"` +} + +type GetProjectRoleResponse struct { + ID string `json:"id"` + Name string `json:"name"` + OrganizationID string `json:"organizationId"` + ProjectID string `json:"projectId"` + Description string `json:"description"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type ListProjectRoleResponse struct { + Roles []GetProjectRoleResponse `json:"roles"` + Pagination PaginationResponse `json:"pagination"` +} + +type UpdateProjectRoleRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` +} diff --git a/pkg/domain/user.go b/pkg/domain/user.go index 0e79216f..138b0edc 100644 --- a/pkg/domain/user.go +++ b/pkg/domain/user.go @@ -23,15 +23,6 @@ type User = struct { Description string `json:"description"` } -type Role = struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - type SimpleRoleResponse = struct { ID string `json:"id"` Name string `json:"name"` From 51a0a62e08aa56a3db02103a13e18fe9100eba6a Mon Sep 17 00:00:00 2001 From: donggyu Date: Thu, 8 Feb 2024 11:08:43 +0900 Subject: [PATCH 02/15] merge with project api --- internal/delivery/http/project.go | 3 +- internal/repository/project.go | 50 +------------------------------ internal/repository/role.go | 8 ++--- pkg/domain/project.go | 21 +------------ 4 files changed, 8 insertions(+), 74 deletions(-) diff --git a/internal/delivery/http/project.go b/internal/delivery/http/project.go index dc11d64b..5f724ce5 100644 --- a/internal/delivery/http/project.go +++ b/internal/delivery/http/project.go @@ -54,7 +54,8 @@ type IProjectHandler interface { } type ProjectHandler struct { - usecase usecase.IProjectUsecase + usecase usecase.IProjectUsecase + roleUsecase usecase.IRoleUsecase } func NewProjectHandler(u usecase.Usecase) IProjectHandler { diff --git a/internal/repository/project.go b/internal/repository/project.go index c042cdc2..67f57e95 100644 --- a/internal/repository/project.go +++ b/internal/repository/project.go @@ -20,9 +20,6 @@ type IProjectRepository interface { GetProjectByIdAndLeader(organizationId string, projectId string) (*domain.Project, error) GetProjectByName(organizationId string, projectName string) (*domain.Project, error) UpdateProject(p *domain.Project) error - GetAllProjectRoles() ([]domain.ProjectRole, error) - GetProjectRoleByName(name string) (*domain.ProjectRole, error) - GetProjectRoleById(id string) (*domain.ProjectRole, error) AddProjectMember(*domain.ProjectMember) (string, error) GetProjectMembersByProjectId(projectId string, pg *pagination.Pagination) ([]domain.ProjectMember, error) GetProjectMembersByProjectIdAndRoleName(projectId string, memberRole string, pg *pagination.Pagination) ([]domain.ProjectMember, error) @@ -206,6 +203,7 @@ func (r *ProjectRepository) GetProjectByIdAndLeader(organizationId string, proje res := r.db.Limit(1). Preload("ProjectMembers", "is_project_leader = ?", true). Preload("ProjectMembers.ProjectRole"). + Preload("ProjectMembers.ProjectRole.Role"). Preload("ProjectMembers.ProjectUser"). First(&p, "organization_id = ? and id = ?", organizationId, projectId) @@ -248,52 +246,6 @@ func (r *ProjectRepository) UpdateProject(p *domain.Project) error { return nil } -func (r *ProjectRepository) GetProjectRoleById(id string) (*domain.ProjectRole, error) { - var pr = &domain.ProjectRole{ID: id} - res := r.db.First(pr) - if res.Error != nil { - if errors.Is(res.Error, gorm.ErrRecordNotFound) { - log.Info("Cannot find project role") - return nil, nil - } else { - log.Error(res.Error) - return nil, res.Error - } - } - - return pr, nil -} - -func (r *ProjectRepository) GetAllProjectRoles() (prs []domain.ProjectRole, err error) { - res := r.db.Find(&prs) - if res.Error != nil { - if errors.Is(res.Error, gorm.ErrRecordNotFound) { - log.Info("Cannot find project roles") - return nil, nil - } else { - log.Error(res.Error) - return nil, res.Error - } - } - - return prs, nil -} - -func (r *ProjectRepository) GetProjectRoleByName(name string) (pr *domain.ProjectRole, err error) { - res := r.db.Where("name = ?", name).First(&pr) - if res.Error != nil { - if errors.Is(res.Error, gorm.ErrRecordNotFound) { - log.Info("Cannot find project roles") - return nil, nil - } else { - log.Error(res.Error) - return nil, res.Error - } - } - - return pr, nil -} - func (r *ProjectRepository) AddProjectMember(pm *domain.ProjectMember) (string, error) { res := r.db.Create(&pm) if res.Error != nil { diff --git a/internal/repository/role.go b/internal/repository/role.go index f8906c11..03736c67 100644 --- a/internal/repository/role.go +++ b/internal/repository/role.go @@ -37,7 +37,7 @@ type ProjectRole struct { RoleID uuid.UUID `gorm:"type:uuid;primary_key;"` Role Role `gorm:"foreignKey:RoleID;references:ID;"` ProjectID uuid.UUID - Project Project `gorm:"foreignKey:ProjectID;references:ID;"` + Project domain.Project `gorm:"foreignKey:ProjectID;references:ID;"` } type IRoleRepository interface { @@ -164,7 +164,7 @@ func (r RoleRepository) ListProjectRoles(projectId string, pg *pagination.Pagina } return roles, nil -} +}- func (r RoleRepository) Get(id uuid.UUID) (*domain.Role, error) { var role Role @@ -288,7 +288,7 @@ func ConvertDomainToRepoProjectRole(domainRole *domain.ProjectRole) *ProjectRole RoleID: domainRole.RoleID, ProjectID: domainRole.ProjectID, Role: *ConverDomainToRepoRole(&domainRole.Role), - Project: *ConvertDomainToRepoProject(&domainRole.Project), + Project: domainRole.Project, } } @@ -298,6 +298,6 @@ func ConvertRepoToDomainProjectRole(repoRole *ProjectRole) *domain.ProjectRole { RoleID: repoRole.RoleID, ProjectID: repoRole.ProjectID, Role: *ConvertRepoToDomainRole(&repoRole.Role), - Project: *ConvertRepoToDomainProject(&repoRole.Project), + Project: repoRole.Project, } } diff --git a/pkg/domain/project.go b/pkg/domain/project.go index 23882b28..38e670e6 100644 --- a/pkg/domain/project.go +++ b/pkg/domain/project.go @@ -1,10 +1,9 @@ package domain import ( - "time" - "github.com/google/uuid" "gorm.io/gorm" + "time" ) func (a *Project) BeforeCreate(*gorm.DB) (err error) { @@ -12,11 +11,6 @@ func (a *Project) BeforeCreate(*gorm.DB) (err error) { return nil } -func (t *ProjectRole) BeforeCreate(*gorm.DB) (err error) { - t.ID = uuid.New().String() - return nil -} - func (t *ProjectMember) BeforeCreate(*gorm.DB) (err error) { t.ID = uuid.New().String() return nil @@ -84,15 +78,6 @@ type GetProjectResponse struct { Project *ProjectDetailResponse `json:"project"` } -type ProjectRole struct { - ID string `gorm:"primarykey" json:"id"` - Name string `json:"name"` // project-leader, project-member, project-viewer - Description string `json:"description,omitempty"` - CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt" ` - UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` - DeletedAt *time.Time `json:"deletedAt"` -} - type ProjectUser struct { ID uuid.UUID `gorm:"primarykey;type:uuid" json:"id"` AccountId string `json:"accountId"` @@ -157,10 +142,6 @@ type UpdateProjectRequest struct { CreateProjectRequest } -type GetProjectRoleResponse struct { - ProjectRole ProjectRole `json:"projectRole"` -} - type GetProjectRolesResponse struct { ProjectRoles []ProjectRole `json:"projectRoles"` } From 27871ee0a16aee7125f5c2e96854f6d7e2c0d4e6 Mon Sep 17 00:00:00 2001 From: donggyu Date: Thu, 15 Feb 2024 14:33:51 +0900 Subject: [PATCH 03/15] refactoring: merge gorm object with domain object --- internal/database/database.go | 24 +- internal/delivery/api/endpoint.go | 23 +- internal/delivery/http/role.go | 58 ++-- internal/repository/alert.go | 6 +- internal/repository/app-group.go | 8 +- internal/repository/cloud-account.go | 14 +- internal/repository/cluster.go | 18 +- internal/repository/endpoint.go | 80 +++--- internal/repository/organization.go | 81 +++--- internal/repository/permission.go | 387 +++++++++++++------------- internal/repository/role.go | 168 +++++------ internal/repository/role_test.go | 28 -- internal/repository/stack-template.go | 14 +- internal/repository/user.go | 213 ++++++-------- internal/usecase/auth.go | 28 +- internal/usecase/user.go | 42 +-- pkg/domain/endpoint.go | 20 +- pkg/domain/organization.go | 7 +- pkg/domain/permmision.go | 28 +- pkg/domain/project.go | 2 +- pkg/domain/role.go | 24 +- pkg/domain/user.go | 69 +++-- 22 files changed, 617 insertions(+), 725 deletions(-) delete mode 100644 internal/repository/role_test.go diff --git a/internal/database/database.go b/internal/database/database.go index 7d3a295c..6e27de84 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -57,18 +57,15 @@ func migrateSchema(db *gorm.DB) error { if err := db.AutoMigrate(&repository.CacheEmailCode{}); err != nil { return err } - if err := db.AutoMigrate(&repository.User{}); err != nil { + if err := db.AutoMigrate(&domain.User{}); err != nil { return err } - if err := db.AutoMigrate(&repository.Role{}); err != nil { - return err - } - if err := db.AutoMigrate(&repository.Policy{}); err != nil { + if err := db.AutoMigrate(&domain.Role{}); err != nil { return err } // Organization - if err := db.AutoMigrate(&repository.Organization{}); err != nil { + if err := db.AutoMigrate(&domain.Organization{}); err != nil { return err } @@ -114,13 +111,24 @@ func migrateSchema(db *gorm.DB) error { return err } - // Project - if err := db.AutoMigrate(&domain.Project{}); err != nil { + // Role + if err := db.AutoMigrate(&domain.Role{}); err != nil { + return err + } + if err := db.AutoMigrate(&domain.Permission{}); err != nil { return err } if err := db.AutoMigrate(&domain.ProjectRole{}); err != nil { return err } + if err := db.AutoMigrate(&domain.Endpoint{}); err != nil { + return err + } + + // Project + if err := db.AutoMigrate(&domain.Project{}); err != nil { + return err + } if err := db.AutoMigrate(&domain.ProjectMember{}); err != nil { return err } diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 00cfc5a4..2a20f6ed 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -20,6 +20,7 @@ const ( VerifyIdentityForLostId VerifyIdentityForLostPassword VerifyToken + DeleteToken // User CreateUser @@ -129,8 +130,6 @@ const ( // Project CreateProject - GetProjectRoles - GetProjectRole GetProjects GetProject UpdateProject @@ -202,6 +201,10 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "VerifyToken", Group: "Auth", }, + DeleteToken: { + Name: "DeleteToken", + Group: "Auth", + }, CreateUser: { Name: "CreateUser", Group: "User", @@ -542,14 +545,6 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "CreateProject", Group: "Project", }, - GetProjectRoles: { - Name: "GetProjectRoles", - Group: "Project", - }, - GetProjectRole: { - Name: "GetProjectRole", - Group: "Project", - }, GetProjects: { Name: "GetProjects", Group: "Project", @@ -704,6 +699,8 @@ func (e Endpoint) String() string { return "VerifyIdentityForLostPassword" case VerifyToken: return "VerifyToken" + case DeleteToken: + return "DeleteToken" case CreateUser: return "CreateUser" case ListUser: @@ -874,10 +871,6 @@ func (e Endpoint) String() string { return "InstallStack" case CreateProject: return "CreateProject" - case GetProjectRoles: - return "GetProjectRoles" - case GetProjectRole: - return "GetProjectRole" case GetProjects: return "GetProjects" case GetProject: @@ -968,6 +961,8 @@ func GetEndpoint(name string) Endpoint { return VerifyIdentityForLostPassword case "VerifyToken": return VerifyToken + case "DeleteToken": + return DeleteToken case "CreateUser": return CreateUser case "ListUser": diff --git a/internal/delivery/http/role.go b/internal/delivery/http/role.go index c254b7ec..605005e9 100644 --- a/internal/delivery/http/role.go +++ b/internal/delivery/http/role.go @@ -1,7 +1,6 @@ package http import ( - "github.com/google/uuid" "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" @@ -125,11 +124,6 @@ func (h RoleHandler) CreateProjectRole(w http.ResponseWriter, r *http.Request) { } // input to dto - projectIdUuid, err := uuid.Parse(projectId) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } dto := domain.ProjectRole{ Role: domain.Role{ OrganizationID: organizationId, @@ -137,7 +131,7 @@ func (h RoleHandler) CreateProjectRole(w http.ResponseWriter, r *http.Request) { Description: input.Description, Type: string(domain.RoleTypeProject), }, - ProjectID: projectIdUuid, + ProjectID: projectId, } if err := h.roleUsecase.CreateProjectRole(&dto); err != nil { @@ -185,13 +179,13 @@ func (h RoleHandler) ListTksRoles(w http.ResponseWriter, r *http.Request) { out.Roles = make([]domain.GetTksRoleResponse, len(roles)) for i, role := range roles { out.Roles[i] = domain.GetTksRoleResponse{ - ID: role.ID.String(), - Name: role.Name, - OrganizationID: role.OrganizationID, - Description: role.Description, - Creator: role.Creator.String(), - CreatedAt: role.CreatedAt, - UpdatedAt: role.UpdatedAt, + ID: role.Role.ID, + Name: role.Role.Name, + OrganizationID: role.Role.OrganizationID, + Description: role.Role.Description, + Creator: role.Role.Creator.String(), + CreatedAt: role.Role.CreatedAt, + UpdatedAt: role.Role.UpdatedAt, } } @@ -244,10 +238,10 @@ func (h RoleHandler) ListProjectRoles(w http.ResponseWriter, r *http.Request) { out.Roles = make([]domain.GetProjectRoleResponse, len(roles)) for i, role := range roles { out.Roles[i] = domain.GetProjectRoleResponse{ - ID: role.RoleID.String(), + ID: role.RoleID, Name: role.Role.Name, OrganizationID: role.Role.OrganizationID, - ProjectID: role.ProjectID.String(), + ProjectID: role.ProjectID, Description: role.Role.Description, Creator: role.Role.Creator.String(), CreatedAt: role.Role.CreatedAt, @@ -292,13 +286,13 @@ func (h RoleHandler) GetTksRole(w http.ResponseWriter, r *http.Request) { // response out := domain.GetTksRoleResponse{ - ID: role.ID.String(), - Name: role.Name, - OrganizationID: role.OrganizationID, - Description: role.Description, - Creator: role.Creator.String(), - CreatedAt: role.CreatedAt, - UpdatedAt: role.UpdatedAt, + ID: role.Role.ID, + Name: role.Role.Name, + OrganizationID: role.Role.OrganizationID, + Description: role.Role.Description, + Creator: role.Role.Creator.String(), + CreatedAt: role.Role.CreatedAt, + UpdatedAt: role.Role.UpdatedAt, } ResponseJSON(w, r, http.StatusOK, out) @@ -335,10 +329,10 @@ func (h RoleHandler) GetProjectRole(w http.ResponseWriter, r *http.Request) { // response out := domain.GetProjectRoleResponse{ - ID: role.RoleID.String(), + ID: role.RoleID, Name: role.Role.Name, OrganizationID: role.Role.OrganizationID, - ProjectID: role.ProjectID.String(), + ProjectID: role.ProjectID, Description: role.Role.Description, Creator: role.Role.Creator.String(), CreatedAt: role.Role.CreatedAt, @@ -443,14 +437,9 @@ func (h RoleHandler) UpdateTksRole(w http.ResponseWriter, r *http.Request) { } // input to dto - roleIdUuid, err := uuid.Parse(roleId) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } dto := domain.TksRole{ Role: domain.Role{ - ID: roleIdUuid, + ID: roleId, Name: input.Name, Description: input.Description, }, @@ -499,14 +488,9 @@ func (h RoleHandler) UpdateProjectRole(w http.ResponseWriter, r *http.Request) { } // input to dto - roleIdUuid, err := uuid.Parse(roleId) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } dto := domain.ProjectRole{ Role: domain.Role{ - ID: roleIdUuid, + ID: roleId, Name: input.Name, Description: input.Description, }, diff --git a/internal/repository/alert.go b/internal/repository/alert.go index 0db9a350..4e8c8575 100644 --- a/internal/repository/alert.go +++ b/internal/repository/alert.go @@ -43,7 +43,7 @@ type Alert struct { ID uuid.UUID `gorm:"primarykey"` OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` + Organization domain.Organization `gorm:"foreignKey:OrganizationId"` Name string Code string Description string @@ -72,8 +72,8 @@ type AlertAction struct { AlertId uuid.UUID Content string Status domain.AlertActionStatus - TakerId *uuid.UUID `gorm:"type:uuid"` - Taker User `gorm:"foreignKey:TakerId"` + TakerId *uuid.UUID `gorm:"type:uuid"` + Taker domain.User `gorm:"foreignKey:TakerId"` } func (c *AlertAction) BeforeCreate(tx *gorm.DB) (err error) { diff --git a/internal/repository/app-group.go b/internal/repository/app-group.go index be160e53..d78aa1bc 100644 --- a/internal/repository/app-group.go +++ b/internal/repository/app-group.go @@ -49,10 +49,10 @@ type AppGroup struct { WorkflowId string Status domain.AppGroupStatus StatusDesc string - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator domain.User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator domain.User `gorm:"foreignKey:UpdatorId"` } func (c *AppGroup) BeforeCreate(tx *gorm.DB) (err error) { diff --git a/internal/repository/cloud-account.go b/internal/repository/cloud-account.go index a6dd6102..5d9a7de9 100644 --- a/internal/repository/cloud-account.go +++ b/internal/repository/cloud-account.go @@ -41,9 +41,9 @@ type CloudAccount struct { ID uuid.UUID `gorm:"primarykey"` OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` - Name string `gorm:"index"` - Description string `gorm:"index"` + Organization domain.Organization `gorm:"foreignKey:OrganizationId"` + Name string `gorm:"index"` + Description string `gorm:"index"` Resource string CloudService string WorkflowId string @@ -51,10 +51,10 @@ type CloudAccount struct { StatusDesc string AwsAccountId string CreatedIAM bool - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator domain.User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator domain.User `gorm:"foreignKey:UpdatorId"` } func (c *CloudAccount) BeforeCreate(tx *gorm.DB) (err error) { diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index 66de2205..e637c040 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -53,8 +53,8 @@ type Cluster struct { Name string `gorm:"index"` CloudService string `gorm:"default:AWS"` OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` - Description string `gorm:"index"` + Organization domain.Organization `gorm:"foreignKey:OrganizationId"` + Description string `gorm:"index"` WorkflowId string Status domain.ClusterStatus StatusDesc string @@ -76,10 +76,10 @@ type Cluster struct { TksUserNode int TksUserNodeMax int TksUserNodeType string - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator domain.User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator domain.User `gorm:"foreignKey:UpdatorId"` } type ClusterFavorite struct { @@ -87,9 +87,9 @@ type ClusterFavorite struct { ID uuid.UUID `gorm:"primarykey;type:uuid"` ClusterId domain.ClusterId - Cluster Cluster `gorm:"foreignKey:ClusterId"` - UserId uuid.UUID `gorm:"type:uuid"` - User User `gorm:"foreignKey:UserId"` + Cluster Cluster `gorm:"foreignKey:ClusterId"` + UserId uuid.UUID `gorm:"type:uuid"` + User domain.User `gorm:"foreignKey:UserId"` } func (c *ClusterFavorite) BeforeCreate(tx *gorm.DB) (err error) { diff --git a/internal/repository/endpoint.go b/internal/repository/endpoint.go index 16fd5968..1bd4d4cc 100644 --- a/internal/repository/endpoint.go +++ b/internal/repository/endpoint.go @@ -27,24 +27,19 @@ func NewEndpointRepository(db *gorm.DB) *EndpointRepository { } } -type Endpoint struct { - gorm.Model - - ID uuid.UUID `gorm:"type:uuid;primary_key;"` - - Name string `gorm:"type:text;not null;unique"` - Group string `gorm:"type:text;"` - PermissionID uuid.UUID `gorm:"type:uuid;"` - Permission Permission `gorm:"foreignKey:PermissionID;"` -} - -func (e *Endpoint) BeforeCreate(tx *gorm.DB) error { - e.ID = uuid.New() - return nil -} +//type Endpoint struct { +// gorm.Model +// +// ID uuid.UUID `gorm:"type:uuid;primary_key;"` +// +// Name string `gorm:"type:text;not null;unique"` +// Group string `gorm:"type:text;"` +// PermissionID uuid.UUID `gorm:"type:uuid;"` +// Permission Permission `gorm:"foreignKey:PermissionID;"` +//} func (e *EndpointRepository) Create(endpoint *domain.Endpoint) error { - obj := &Endpoint{ + obj := &domain.Endpoint{ Name: endpoint.Name, Group: endpoint.Group, PermissionID: endpoint.PermissionID, @@ -59,13 +54,13 @@ func (e *EndpointRepository) Create(endpoint *domain.Endpoint) error { func (e *EndpointRepository) List(pg *pagination.Pagination) ([]*domain.Endpoint, error) { var endpoints []*domain.Endpoint - var objs []*Endpoint + var objs []*domain.Endpoint if pg == nil { pg = pagination.NewDefaultPagination() } filterFunc := CombinedGormFilter("endpoints", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(e.db.Model(&Endpoint{})) + db := filterFunc(e.db.Model(&domain.Endpoint{})) db.Count(&pg.TotalRows) pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) @@ -81,7 +76,7 @@ func (e *EndpointRepository) List(pg *pagination.Pagination) ([]*domain.Endpoint Name: obj.Name, Group: obj.Group, PermissionID: obj.PermissionID, - Permission: *ConvertRepoToDomainPermission(&obj.Permission), + Permission: obj.Permission, }) } @@ -89,7 +84,7 @@ func (e *EndpointRepository) List(pg *pagination.Pagination) ([]*domain.Endpoint } func (e *EndpointRepository) Get(id uuid.UUID) (*domain.Endpoint, error) { - var obj Endpoint + var obj domain.Endpoint if err := e.db.Preload("Permission").First(&obj, "id = ?", id).Error; err != nil { return nil, err @@ -103,7 +98,7 @@ func (e *EndpointRepository) Get(id uuid.UUID) (*domain.Endpoint, error) { } func (e *EndpointRepository) Update(endpoint *domain.Endpoint) error { - obj := &Endpoint{ + obj := &domain.Endpoint{ ID: endpoint.ID, Name: endpoint.Name, Group: endpoint.Group, @@ -117,24 +112,25 @@ func (e *EndpointRepository) Update(endpoint *domain.Endpoint) error { return nil } -// domain.Endpoint to repository.Endpoint -func ConvertDomainToRepoEndpoint(endpoint *domain.Endpoint) *Endpoint { - return &Endpoint{ - ID: endpoint.ID, - Name: endpoint.Name, - Group: endpoint.Group, - PermissionID: endpoint.PermissionID, - Permission: *ConvertDomainToRepoPermission(&endpoint.Permission), - } -} - -// repository.Endpoint to domain.Endpoint -func ConvertRepoToDomainEndpoint(endpoint *Endpoint) *domain.Endpoint { - return &domain.Endpoint{ - ID: endpoint.ID, - Name: endpoint.Name, - Group: endpoint.Group, - PermissionID: endpoint.PermissionID, - Permission: *ConvertRepoToDomainPermission(&endpoint.Permission), - } -} +// +//// domain.Endpoint to repository.Endpoint +//func ConvertDomainToRepoEndpoint(endpoint *domain.Endpoint) *Endpoint { +// return &Endpoint{ +// ID: endpoint.ID, +// Name: endpoint.Name, +// Group: endpoint.Group, +// PermissionID: endpoint.PermissionID, +// Permission: *ConvertDomainToRepoPermission(&endpoint.Permission), +// } +//} +// +//// repository.Endpoint to domain.Endpoint +//func ConvertRepoToDomainEndpoint(endpoint *Endpoint) *domain.Endpoint { +// return &domain.Endpoint{ +// ID: endpoint.ID, +// Name: endpoint.Name, +// Group: endpoint.Group, +// PermissionID: endpoint.PermissionID, +// Permission: *ConvertRepoToDomainPermission(&endpoint.Permission), +// } +//} diff --git a/internal/repository/organization.go b/internal/repository/organization.go index 12e31962..2c23b54b 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -30,20 +30,21 @@ func NewOrganizationRepository(db *gorm.DB) IOrganizationRepository { } } -// Models -type Organization struct { - gorm.Model - - ID string `gorm:"primarykey;type:varchar(36);not null"` - Name string - Description string - Phone string - WorkflowId string - Status domain.OrganizationStatus - StatusDesc string - Creator uuid.UUID - PrimaryClusterId string // allow null -} +// +//// Models +//type Organization struct { +// gorm.Model +// +// ID string `gorm:"primarykey;type:varchar(36);not null"` +// Name string +// Description string +// Phone string +// WorkflowId string +// Status domain.OrganizationStatus +// StatusDesc string +// Creator uuid.UUID +// PrimaryClusterId string // allow null +//} //func (c *Organization) BeforeCreate(tx *gorm.DB) (err error) { // c.ID = helper.GenerateOrganizationId() @@ -52,10 +53,10 @@ type Organization struct { func (r *OrganizationRepository) Create(organizationId string, name string, creator uuid.UUID, phone string, description string) (domain.Organization, error) { - organization := Organization{ + organization := domain.Organization{ ID: organizationId, Name: name, - Creator: creator, + Creator: creator.String(), Phone: phone, Description: description, Status: domain.OrganizationStatus_PENDING, @@ -70,12 +71,19 @@ func (r *OrganizationRepository) Create(organizationId string, name string, crea } func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Organization, error) { - var organizations []Organization + var organizations []domain.Organization var out []domain.Organization if pg == nil { pg = pagination.NewPagination(nil) } + filterFunc := CombinedGormFilter("organizations", pg.GetFilters(), pg.CombinedFilter) + db := filterFunc(r.db.Model(&domain.Organization{})) + db.Count(&pg.TotalRows) + + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&organizations) _, res := pg.Fetch(r.db, &organizations) if res.Error != nil { return nil, res.Error @@ -89,7 +97,7 @@ func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Org } func (r *OrganizationRepository) Get(id string) (domain.Organization, error) { - var organization Organization + var organization domain.Organization res := r.db.First(&organization, "id = ?", id) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) @@ -100,8 +108,8 @@ func (r *OrganizationRepository) Get(id string) (domain.Organization, error) { } func (r *OrganizationRepository) Update(organizationId string, in domain.UpdateOrganizationRequest) (domain.Organization, error) { - var organization Organization - res := r.db.Model(&Organization{}). + var organization domain.Organization + res := r.db.Model(&domain.Organization{}). Where("id = ?", organizationId). Updates(map[string]interface{}{ "name": in.Name, @@ -113,7 +121,7 @@ func (r *OrganizationRepository) Update(organizationId string, in domain.UpdateO log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return domain.Organization{}, res.Error } - res = r.db.Model(&Organization{}).Where("id = ?", organizationId).Find(&organization) + res = r.db.Model(&domain.Organization{}).Where("id = ?", organizationId).Find(&organization) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return domain.Organization{}, res.Error @@ -123,7 +131,7 @@ func (r *OrganizationRepository) Update(organizationId string, in domain.UpdateO } func (r *OrganizationRepository) UpdatePrimaryClusterId(organizationId string, primaryClusterId string) error { - res := r.db.Model(&Organization{}). + res := r.db.Model(&domain.Organization{}). Where("id = ?", organizationId). Updates(map[string]interface{}{ "primary_cluster_id": primaryClusterId, @@ -137,7 +145,7 @@ func (r *OrganizationRepository) UpdatePrimaryClusterId(organizationId string, p } func (r *OrganizationRepository) Delete(organizationId string) error { - res := r.db.Delete(&Organization{}, "id = ?", organizationId) + res := r.db.Delete(&domain.Organization{}, "id = ?", organizationId) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return res.Error @@ -147,7 +155,7 @@ func (r *OrganizationRepository) Delete(organizationId string) error { } func (r *OrganizationRepository) InitWorkflow(organizationId string, workflowId string, status domain.OrganizationStatus) error { - res := r.db.Model(&Organization{}). + res := r.db.Model(&domain.Organization{}). Where("ID = ?", organizationId). Updates(map[string]interface{}{"Status": status, "WorkflowId": workflowId}) if res.Error != nil { @@ -157,35 +165,14 @@ func (r *OrganizationRepository) InitWorkflow(organizationId string, workflowId return nil } -func (r *OrganizationRepository) reflect(organization Organization) (out domain.Organization) { +func (r *OrganizationRepository) reflect(organization domain.Organization) (out domain.Organization) { if err := serializer.Map(organization.Model, &out); err != nil { log.Error(err) } if err := serializer.Map(organization, &out); err != nil { log.Error(err) } - out.Creator = organization.Creator.String() + out.Creator = organization.Creator return } - -func ConvertDomainToRepoOrganization(organization *domain.Organization) *Organization { - - return &Organization{ - ID: organization.ID, - Name: organization.Name, - Description: organization.Description, - Phone: organization.Phone, - Creator: uuid.MustParse(organization.Creator), - } -} - -func ConvertRepoToDomainOrganization(organization *Organization) *domain.Organization { - return &domain.Organization{ - ID: organization.ID, - Name: organization.Name, - Description: organization.Description, - Phone: organization.Phone, - Creator: organization.Creator.String(), - } -} diff --git a/internal/repository/permission.go b/internal/repository/permission.go index 0580244c..5a28a708 100644 --- a/internal/repository/permission.go +++ b/internal/repository/permission.go @@ -24,196 +24,197 @@ func NewPermissionRepository(db *gorm.DB) *PermissionRepository { } } -type Permission struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid;"` - Name string - - IsAllowed *bool `gorm:"type:boolean;"` - RoleID *uuid.UUID - Role *Role `gorm:"foreignKey:RoleID;references:ID;"` - Endpoints []*Endpoint `gorm:"one2many:endpoints;"` - - ParentID *uuid.UUID - Parent *Permission `gorm:"foreignKey:ParentID;references:ID;"` - Children []*Permission `gorm:"foreignKey:ParentID;references:ID;"` -} - -func (p *Permission) BeforeCreate(tx *gorm.DB) (err error) { - if p.ID == uuid.Nil { - p.ID = uuid.New() - } - return nil -} - -func (r PermissionRepository) Create(p *domain.Permission) error { - //var parent *Permission - //var children []*Permission - // - //if p.Parent != nil { - // parent = &Permission{} - // result := r.db.First(&parent, "id = ?", p.Parent.ID) - // if result.Error != nil { - // return result.Error - // } - //} - //if p.Children != nil { - // for _, child := range p.Children { - // newChild := &Permission{} - // result := r.db.First(&newChild, "id = ?", child.ID) - // if result.Error != nil { - // return result.Error - // } - // children = append(children, newChild) - // } - //} - - permission := ConvertDomainToRepoPermission(p) - - return r.db.Create(permission).Error -} - -func (r PermissionRepository) List() ([]*domain.Permission, error) { - var permissions []*Permission - var outs []*domain.Permission - - err := r.db.Preload("Children.Children.Children.Children").Where("parent_id IS NULL").Find(&permissions).Error - if err != nil { - return nil, err - } - - for _, permission := range permissions { - outs = append(outs, ConvertRepoToDomainPermission(permission)) - } - return outs, nil -} - -func (r PermissionRepository) Get(id uuid.UUID) (*domain.Permission, error) { - permission := &Permission{} - result := r.db.Preload("Children.Children.Children").Preload("Parent").First(&permission, "id = ?", id) - if result.Error != nil { - return nil, result.Error - } - - return ConvertRepoToDomainPermission(permission), nil -} - -func (r PermissionRepository) Delete(id uuid.UUID) error { - return r.db.Delete(&Permission{}, id).Error -} - -func (r PermissionRepository) Update(p *domain.Permission) error { - permission := ConvertDomainToRepoPermission(p) - - return r.db.Updates(permission).Error - - //var parent *Permission - //var children []*Permission - // - //if p.Parent != nil { - // parent = &Permission{} - // result := r.db.First(&parent, "id = ?", p.Parent.ID) - // if result.Error != nil { - // return result.Error - // } - //} - //if p.Children != nil { - // for _, child := range p.Children { - // newChild := &Permission{} - // result := r.db.First(&newChild, "id = ?", child.ID) - // if result.Error != nil { - // return result.Error - // } - // children = append(children, newChild) - // } - //} - // - //permission := &Permission{} - // - //result := r.db.First(&permission, "id = ?", p.ID) - //if result.Error != nil { - // return result.Error - //} - // - //permission.Name = p.Name - //permission.Parent = parent - //permission.Children = children - //permission.IsAllowed = p.IsAllowed - // - //return r.db.Save(permission).Error -} - -// repository.Permission to domain.Permission -func ConvertRepoToDomainPermission(repoPerm *Permission) *domain.Permission { - if repoPerm == nil { - return nil - } - - if repoPerm.Endpoints == nil { - repoPerm.Endpoints = []*Endpoint{} - } - var domainEndpoints []*domain.Endpoint - for _, endpoint := range repoPerm.Endpoints { - domainEndpoints = append(domainEndpoints, ConvertRepoToDomainEndpoint(endpoint)) - } - - // Domain Permission 객체 생성 - domainPerm := &domain.Permission{ - ID: repoPerm.ID, - Name: repoPerm.Name, - ParentID: repoPerm.ParentID, - IsAllowed: repoPerm.IsAllowed, - Endpoints: domainEndpoints, - } - - // 자식 권한들 변환 - for _, child := range repoPerm.Children { - domainChild := ConvertRepoToDomainPermission(child) - domainPerm.Children = append(domainPerm.Children, domainChild) - } - - // 부모 권한 변환 (부모 권한이 있을 경우만) - if repoPerm.Parent != nil { - domainPerm.Parent = ConvertRepoToDomainPermission(repoPerm.Parent) - } - - return domainPerm -} - -// domain.Permission to repository.Permission -func ConvertDomainToRepoPermission(domainPerm *domain.Permission) *Permission { - if domainPerm == nil { - return nil - } - - if domainPerm.Endpoints == nil { - domainPerm.Endpoints = []*domain.Endpoint{} - } - var repoEndpoints []*Endpoint - for _, endpoint := range domainPerm.Endpoints { - repoEndpoints = append(repoEndpoints, ConvertDomainToRepoEndpoint(endpoint)) - } - - // Domain Permission 객체 생성 - repoPerm := &Permission{ - ID: domainPerm.ID, - Name: domainPerm.Name, - ParentID: domainPerm.ParentID, - IsAllowed: domainPerm.IsAllowed, - Endpoints: repoEndpoints, - } - - // 자식 권한들 변환 - for _, child := range domainPerm.Children { - repoChild := ConvertDomainToRepoPermission(child) - repoPerm.Children = append(repoPerm.Children, repoChild) - } - - // 부모 권한 변환 (부모 권한이 있을 경우만) - if domainPerm.Parent != nil { - repoPerm.Parent = ConvertDomainToRepoPermission(domainPerm.Parent) - } - - return repoPerm -} +// +//type Permission struct { +// gorm.Model +// +// ID uuid.UUID `gorm:"primarykey;type:uuid;"` +// Name string +// +// IsAllowed *bool `gorm:"type:boolean;"` +// RoleID string +// Role *Role `gorm:"foreignKey:RoleID;references:ID;"` +// Endpoints []*Endpoint `gorm:"one2many:endpoints;"` +// +// ParentID *uuid.UUID +// Parent *Permission `gorm:"foreignKey:ParentID;references:ID;"` +// Children []*Permission `gorm:"foreignKey:ParentID;references:ID;"` +//} +// +//func (p *Permission) BeforeCreate(tx *gorm.DB) (err error) { +// if p.ID == uuid.Nil { +// p.ID = uuid.New() +// } +// return nil +//} +// +//func (r PermissionRepository) Create(p *domain.Permission) error { +// //var parent *Permission +// //var children []*Permission +// // +// //if p.Parent != nil { +// // parent = &Permission{} +// // result := r.db.First(&parent, "id = ?", p.Parent.ID) +// // if result.Error != nil { +// // return result.Error +// // } +// //} +// //if p.Children != nil { +// // for _, child := range p.Children { +// // newChild := &Permission{} +// // result := r.db.First(&newChild, "id = ?", child.ID) +// // if result.Error != nil { +// // return result.Error +// // } +// // children = append(children, newChild) +// // } +// //} +// +// permission := ConvertDomainToRepoPermission(p) +// +// return r.db.Create(permission).Error +//} +// +//func (r PermissionRepository) List() ([]*domain.Permission, error) { +// var permissions []*domain.Permission +// var outs []*domain.Permission +// +// err := r.db.Preload("Children.Children.Children.Children").Where("parent_id IS NULL").Find(&permissions).Error +// if err != nil { +// return nil, err +// } +// +// for _, permission := range permissions { +// outs = append(outs, ConvertRepoToDomainPermission(permission)) +// } +// return outs, nil +//} +// +//func (r PermissionRepository) Get(id uuid.UUID) (*domain.Permission, error) { +// permission := &domain.Permission{} +// result := r.db.Preload("Children.Children.Children").Preload("Parent").First(&permission, "id = ?", id) +// if result.Error != nil { +// return nil, result.Error +// } +// +// return ConvertRepoToDomainPermission(permission), nil +//} +// +//func (r PermissionRepository) Delete(id uuid.UUID) error { +// return r.db.Delete(&Permission{}, id).Error +//} + +//func (r PermissionRepository) Update(p *domain.Permission) error { +// permission := ConvertDomainToRepoPermission(p) +// +// return r.db.Updates(permission).Error +// +// //var parent *Permission +// //var children []*Permission +// // +// //if p.Parent != nil { +// // parent = &Permission{} +// // result := r.db.First(&parent, "id = ?", p.Parent.ID) +// // if result.Error != nil { +// // return result.Error +// // } +// //} +// //if p.Children != nil { +// // for _, child := range p.Children { +// // newChild := &Permission{} +// // result := r.db.First(&newChild, "id = ?", child.ID) +// // if result.Error != nil { +// // return result.Error +// // } +// // children = append(children, newChild) +// // } +// //} +// // +// //permission := &Permission{} +// // +// //result := r.db.First(&permission, "id = ?", p.ID) +// //if result.Error != nil { +// // return result.Error +// //} +// // +// //permission.Name = p.Name +// //permission.Parent = parent +// //permission.Children = children +// //permission.IsAllowed = p.IsAllowed +// // +// //return r.db.Save(permission).Error +//} +// +//// repository.Permission to domain.Permission +//func ConvertRepoToDomainPermission(repoPerm *domain.Permission) *domain.Permission { +// if repoPerm == nil { +// return nil +// } +// +// if repoPerm.Endpoints == nil { +// repoPerm.Endpoints = []*Endpoint{} +// } +// var domainEndpoints []*domain.Endpoint +// for _, endpoint := range repoPerm.Endpoints { +// domainEndpoints = append(domainEndpoints, ConvertRepoToDomainEndpoint(endpoint)) +// } +// +// // Domain Permission 객체 생성 +// domainPerm := &domain.Permission{ +// ID: repoPerm.ID, +// Name: repoPerm.Name, +// ParentID: repoPerm.ParentID, +// IsAllowed: repoPerm.IsAllowed, +// Endpoints: domainEndpoints, +// } +// +// // 자식 권한들 변환 +// for _, child := range repoPerm.Children { +// domainChild := ConvertRepoToDomainPermission(child) +// domainPerm.Children = append(domainPerm.Children, domainChild) +// } +// +// // 부모 권한 변환 (부모 권한이 있을 경우만) +// if repoPerm.Parent != nil { +// domainPerm.Parent = ConvertRepoToDomainPermission(repoPerm.Parent) +// } +// +// return domainPerm +//} +// +//// domain.Permission to repository.Permission +//func ConvertDomainToRepoPermission(domainPerm *domain.Permission) *Permission { +// if domainPerm == nil { +// return nil +// } +// +// if domainPerm.Endpoints == nil { +// domainPerm.Endpoints = []*domain.Endpoint{} +// } +// var repoEndpoints []*Endpoint +// for _, endpoint := range domainPerm.Endpoints { +// repoEndpoints = append(repoEndpoints, ConvertDomainToRepoEndpoint(endpoint)) +// } +// +// // Domain Permission 객체 생성 +// repoPerm := &Permission{ +// ID: domainPerm.ID, +// Name: domainPerm.Name, +// ParentID: domainPerm.ParentID, +// IsAllowed: domainPerm.IsAllowed, +// Endpoints: repoEndpoints, +// } +// +// // 자식 권한들 변환 +// for _, child := range domainPerm.Children { +// repoChild := ConvertDomainToRepoPermission(child) +// repoPerm.Children = append(repoPerm.Children, repoChild) +// } +// +// // 부모 권한 변환 (부모 권한이 있을 경우만) +// if domainPerm.Parent != nil { +// repoPerm.Parent = ConvertDomainToRepoPermission(domainPerm.Parent) +// } +// +// return repoPerm +//} diff --git a/internal/repository/role.go b/internal/repository/role.go index 03736c67..bd61871b 100644 --- a/internal/repository/role.go +++ b/internal/repository/role.go @@ -9,36 +9,35 @@ import ( "math" ) -type Role struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid;"` - Name string - OrganizationID string - Organization Organization `gorm:"foreignKey:OrganizationID;references:ID;"` - Type string - Creator uuid.UUID - Description string -} - -func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { - if r.ID == uuid.Nil { - r.ID = uuid.New() - } - return nil -} - -type TksRole struct { - RoleID uuid.UUID `gorm:"type:uuid;primary_key;"` - Role Role `gorm:"foreignKey:RoleID;references:ID;"` -} - -type ProjectRole struct { - RoleID uuid.UUID `gorm:"type:uuid;primary_key;"` - Role Role `gorm:"foreignKey:RoleID;references:ID;"` - ProjectID uuid.UUID - Project domain.Project `gorm:"foreignKey:ProjectID;references:ID;"` -} +// +//type Role struct { +// gorm.Model +// +// ID string `gorm:"primarykey;"` +// Name string +// OrganizationID string +// Organization Organization `gorm:"foreignKey:OrganizationID;references:ID;"` +// Type string +// Creator uuid.UUID +// Description string +//} +// +//func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { +// r.ID = uuid.New().String() +// return nil +//} +// +//type TksRole struct { +// RoleID string `gorm:"primarykey;"` +// Role Role `gorm:"foreignKey:RoleID;references:ID;"` +//} + +//type ProjectRole struct { +// RoleID string `gorm:"primarykey;"` +// Role Role `gorm:"foreignKey:RoleID;references:ID;"` +// ProjectID string +// Project domain.Project `gorm:"foreignKey:ProjectID;references:ID;"` +//} type IRoleRepository interface { Create(roleObj interface{}) error @@ -70,8 +69,11 @@ func (r RoleRepository) Create(roleObj interface{}) error { case *domain.ProjectRole: inputRole := roleObj.(*domain.ProjectRole) - role := ConvertDomainToRepoProjectRole(inputRole) - if err := r.db.Create(role).Error; err != nil { + //role := ConvertDomainToRepoProjectRole(inputRole) + //if err := r.db.Create(role).Error; err != nil { + // return err + //} + if err := r.db.Create(inputRole).Error; err != nil { return err } } @@ -81,13 +83,13 @@ func (r RoleRepository) Create(roleObj interface{}) error { func (r RoleRepository) List(pg *pagination.Pagination) ([]*domain.Role, error) { var roles []*domain.Role - var objs []*Role + var objs []*domain.Role if pg == nil { pg = pagination.NewDefaultPagination() } filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&Role{})) + db := filterFunc(r.db.Model(&domain.Role{})) db.Count(&pg.TotalRows) pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) @@ -109,13 +111,13 @@ func (r RoleRepository) List(pg *pagination.Pagination) ([]*domain.Role, error) func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) { var roles []*domain.TksRole - var objs []*TksRole + var objs []*domain.TksRole if pg == nil { pg = pagination.NewDefaultPagination() } filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&TksRole{})) + db := filterFunc(r.db.Model(&domain.TksRole{})) db.Count(&pg.TotalRows) pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) @@ -140,13 +142,13 @@ func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagin func (r RoleRepository) ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) { var roles []*domain.ProjectRole - var objs []*ProjectRole + var objs []*domain.ProjectRole if pg == nil { pg = pagination.NewDefaultPagination() } filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&ProjectRole{})) + db := filterFunc(r.db.Model(&domain.ProjectRole{})) db.Count(&pg.TotalRows) pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) @@ -160,14 +162,14 @@ func (r RoleRepository) ListProjectRoles(projectId string, pg *pagination.Pagina return nil, res.Error } for _, role := range objs { - roles = append(roles, ConvertRepoToDomainProjectRole(role)) + roles = append(roles, role) } return roles, nil -}- +} func (r RoleRepository) Get(id uuid.UUID) (*domain.Role, error) { - var role Role + var role domain.Role if err := r.db.First(&role, "id = ?", id).Error; err != nil { return nil, err } @@ -176,7 +178,7 @@ func (r RoleRepository) Get(id uuid.UUID) (*domain.Role, error) { } func (r RoleRepository) GetTksRole(id uuid.UUID) (*domain.TksRole, error) { - var role TksRole + var role domain.TksRole if err := r.db.Preload("Role").First(&role, "role_id = ?", id).Error; err != nil { return nil, err } @@ -185,24 +187,24 @@ func (r RoleRepository) GetTksRole(id uuid.UUID) (*domain.TksRole, error) { } func (r RoleRepository) GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error) { - var role ProjectRole + var role domain.ProjectRole if err := r.db.Preload("Role").Preload("Project").First(&role, "role_id = ?", id).Error; err != nil { return nil, err } - return ConvertRepoToDomainProjectRole(&role), nil + return &role, nil } func (r RoleRepository) DeleteCascade(id uuid.UUID) error { // manual cascade delete - if err := r.db.Delete(&TksRole{}, "role_id = ?", id).Error; err != nil { + if err := r.db.Delete(&domain.TksRole{}, "role_id = ?", id).Error; err != nil { return err } - if err := r.db.Delete(&ProjectRole{}, "role_id = ?", id).Error; err != nil { + if err := r.db.Delete(&domain.ProjectRole{}, "role_id = ?", id).Error; err != nil { return err } - if err := r.db.Delete(&Role{}, "id = ?", id).Error; err != nil { + if err := r.db.Delete(&domain.Role{}, "id = ?", id).Error; err != nil { return err } @@ -211,23 +213,23 @@ func (r RoleRepository) DeleteCascade(id uuid.UUID) error { func (r RoleRepository) Update(roleObj interface{}) error { switch roleObj.(type) { - case *TksRole: - inputRole := roleObj.(*TksRole) + case *domain.TksRole: + inputRole := roleObj.(*domain.TksRole) role := ConvertRepoToDomainTksRole(inputRole) - if err := r.db.Model(&TksRole{}).Where("id = ?", role.ID).Updates(Role{ - Name: role.Name, - Description: role.Description, + if err := r.db.Model(&domain.TksRole{}).Where("id = ?", role.RoleID).Updates(domain.Role{ + Name: role.Role.Name, + Description: role.Role.Description, }).Error; err != nil { return err } - case *ProjectRole: - inputRole := roleObj.(*ProjectRole) - projectRole := ConvertRepoToDomainProjectRole(inputRole) + case *domain.ProjectRole: + inputRole := roleObj.(*domain.ProjectRole) + //projectRole := ConvertRepoToDomainProjectRole(inputRole) // update role - if err := r.db.Model(&ProjectRole{}).Where("role_id = ?", projectRole.RoleID).Updates(Role{ - Name: projectRole.Role.Name, - Description: projectRole.Role.Description, + if err := r.db.Model(&domain.ProjectRole{}).Where("role_id = ?", inputRole.RoleID).Updates(domain.Role{ + Name: inputRole.Role.Name, + Description: inputRole.Role.Description, }).Error; err != nil { return err } @@ -243,8 +245,8 @@ func NewRoleRepository(db *gorm.DB) IRoleRepository { } // domain.Role to repository.Role -func ConverDomainToRepoRole(domainRole *domain.Role) *Role { - return &Role{ +func ConverDomainToRepoRole(domainRole *domain.Role) *domain.Role { + return &domain.Role{ ID: domainRole.ID, Name: domainRole.Name, OrganizationID: domainRole.OrganizationID, @@ -255,7 +257,7 @@ func ConverDomainToRepoRole(domainRole *domain.Role) *Role { } // repository.Role to domain.Role -func ConvertRepoToDomainRole(repoRole *Role) *domain.Role { +func ConvertRepoToDomainRole(repoRole *domain.Role) *domain.Role { return &domain.Role{ ID: repoRole.ID, Name: repoRole.Name, @@ -267,37 +269,37 @@ func ConvertRepoToDomainRole(repoRole *Role) *domain.Role { } // domain.TksRole to repository.TksRole -func ConvertDomainToRepoTksRole(domainRole *domain.TksRole) *TksRole { - return &TksRole{ +func ConvertDomainToRepoTksRole(domainRole *domain.TksRole) *domain.TksRole { + return &domain.TksRole{ RoleID: domainRole.Role.ID, Role: *ConverDomainToRepoRole(&domainRole.Role), } } // repository.TksRole to domain.TksRole -func ConvertRepoToDomainTksRole(repoRole *TksRole) *domain.TksRole { +func ConvertRepoToDomainTksRole(repoRole *domain.TksRole) *domain.TksRole { return &domain.TksRole{ RoleID: repoRole.RoleID, Role: *ConvertRepoToDomainRole(&repoRole.Role), } } -// domain.ProjectRole to repository.ProjectRole -func ConvertDomainToRepoProjectRole(domainRole *domain.ProjectRole) *ProjectRole { - return &ProjectRole{ - RoleID: domainRole.RoleID, - ProjectID: domainRole.ProjectID, - Role: *ConverDomainToRepoRole(&domainRole.Role), - Project: domainRole.Project, - } -} - -// repository.ProjectRole to domain.ProjectRole -func ConvertRepoToDomainProjectRole(repoRole *ProjectRole) *domain.ProjectRole { - return &domain.ProjectRole{ - RoleID: repoRole.RoleID, - ProjectID: repoRole.ProjectID, - Role: *ConvertRepoToDomainRole(&repoRole.Role), - Project: repoRole.Project, - } -} +//// domain.ProjectRole to repository.ProjectRole +//func ConvertDomainToRepoProjectRole(domainRole *domain.ProjectRole) *ProjectRole { +// return &ProjectRole{ +// RoleID: domainRole.RoleID, +// ProjectID: domainRole.ProjectID, +// Role: *ConverDomainToRepoRole(&domainRole.Role), +// Project: domainRole.Project, +// } +//} +// +//// repository.ProjectRole to domain.ProjectRole +//func ConvertRepoToDomainProjectRole(repoRole *ProjectRole) *domain.ProjectRole { +// return &domain.ProjectRole{ +// RoleID: repoRole.RoleID, +// ProjectID: repoRole.ProjectID, +// Role: *ConvertRepoToDomainRole(&repoRole.Role), +// Project: repoRole.Project, +// } +//} diff --git a/internal/repository/role_test.go b/internal/repository/role_test.go deleted file mode 100644 index 8c9d78a8..00000000 --- a/internal/repository/role_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package repository_test - -import ( - "encoding/json" - "github.com/openinfradev/tks-api/internal/repository" - "testing" -) - -func TestRole(t *testing.T) { - db := db_connection() - - db.AutoMigrate(&repository.Role{}, &repository.Organization{}, &repository.Project{}, &repository.User{}, &repository.TksRole{}, &repository.ProjectRole{}) - - repo := repository.NewRoleRepository(db) - - roles, err := repo.List() - if err != nil { - t.Fatal(err) - } - out, err := json.MarshalIndent(roles, "", " ") - if err != nil { - t.Fatal(err) - } - t.Log("start") - t.Logf("role: %s", string(out)) - t.Log("end") - -} diff --git a/internal/repository/stack-template.go b/internal/repository/stack-template.go index 4d3004c8..bb8b28ad 100644 --- a/internal/repository/stack-template.go +++ b/internal/repository/stack-template.go @@ -37,9 +37,9 @@ type StackTemplate struct { ID uuid.UUID `gorm:"primarykey"` OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` - Name string `gorm:"index"` - Description string `gorm:"index"` + Organization domain.Organization `gorm:"foreignKey:OrganizationId"` + Name string `gorm:"index"` + Description string `gorm:"index"` Template string TemplateType string Version string @@ -48,10 +48,10 @@ type StackTemplate struct { KubeVersion string KubeType string Services datatypes.JSON - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator domain.User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator domain.User `gorm:"foreignKey:UpdatorId"` } func (c *StackTemplate) BeforeCreate(tx *gorm.DB) (err error) { diff --git a/internal/repository/user.go b/internal/repository/user.go index 5bf6ad10..4663f30a 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -15,24 +15,22 @@ import ( type IUserRepository interface { Create(accountId string, organizationId string, password string, name string) (domain.User, error) CreateWithUuid(uuid uuid.UUID, accountId string, name string, password string, email string, - department string, description string, organizationId string, roleId uuid.UUID) (domain.User, error) + department string, description string, organizationId string, roleId string) (domain.User, error) List(filters ...FilterFunc) (out *[]domain.User, err error) ListWithPagination(pg *pagination.Pagination, organizationId string) (out *[]domain.User, err error) Get(accountId string, organizationId string) (domain.User, error) GetByUuid(userId uuid.UUID) (domain.User, error) - UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId uuid.UUID, email string, + UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId string, email string, department string, description string) (domain.User, error) UpdatePassword(userId uuid.UUID, organizationId string, password string, isTemporary bool) error DeleteWithUuid(uuid uuid.UUID) error Flush(organizationId string) error FetchRoles() (out *[]domain.Role, err error) - AssignRole(accountId string, organizationId string, roleName string) error AccountIdFilter(accountId string) FilterFunc OrganizationFilter(organization string) FilterFunc EmailFilter(email string) FilterFunc NameFilter(name string) FilterFunc - AssignRoleWithUuid(uuid uuid.UUID, roleName string) error } type UserRepository struct { @@ -40,7 +38,7 @@ type UserRepository struct { } func (r *UserRepository) Flush(organizationId string) error { - res := r.db.Where("organization_id = ?", organizationId).Delete(&User{}) + res := r.db.Where("organization_id = ?", organizationId).Delete(&domain.User{}) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return res.Error @@ -54,34 +52,34 @@ func NewUserRepository(db *gorm.DB) IUserRepository { } } -// Models -type User struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid"` - AccountId string - Name string - Password string - AuthType string `gorm:"authType"` - RoleId uuid.UUID - Role Role `gorm:"foreignKey:RoleId;references:ID"` - OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId;references:ID"` - Creator uuid.UUID - Email string - Department string - Description string - - PasswordUpdatedAt time.Time `json:"passwordUpdatedAt"` -} - -func (g *User) BeforeCreate(tx *gorm.DB) (err error) { - g.PasswordUpdatedAt = time.Now() - return nil -} +//// Models +//type User struct { +// gorm.Model +// +// ID uuid.UUID `gorm:"primarykey;type:uuid"` +// AccountId string +// Name string +// Password string +// AuthType string `gorm:"authType"` +// RoleId uuid.UUID +// Role Role `gorm:"foreignKey:RoleId;references:ID"` +// OrganizationId string +// Organization Organization `gorm:"foreignKey:OrganizationId;references:ID"` +// Creator uuid.UUID +// Email string +// Department string +// Description string +// +// PasswordUpdatedAt time.Time `json:"passwordUpdatedAt"` +//} + +//func (g *User) BeforeCreate(tx *gorm.DB) (err error) { +// g.PasswordUpdatedAt = time.Now() +// return nil +//} func (r *UserRepository) Create(accountId string, organizationId string, password string, name string) (domain.User, error) { - newUser := User{ + newUser := domain.User{ AccountId: accountId, Password: password, OrganizationId: organizationId, @@ -96,9 +94,9 @@ func (r *UserRepository) Create(accountId string, organizationId string, passwor return r.reflect(newUser), nil } func (r *UserRepository) CreateWithUuid(uuid uuid.UUID, accountId string, name string, password string, email string, - department string, description string, organizationId string, roleId uuid.UUID) (domain.User, error) { + department string, description string, organizationId string, roleId string) (domain.User, error) { - newUser := User{ + newUser := domain.User{ ID: uuid, AccountId: accountId, Password: password, @@ -143,11 +141,11 @@ func (r *UserRepository) NameFilter(name string) FilterFunc { } func (r *UserRepository) List(filters ...FilterFunc) (*[]domain.User, error) { - var users []User + var users []domain.User var res *gorm.DB if filters == nil { - res = r.db.Model(&User{}).Preload("Organization").Preload("Role").Find(&users) + res = r.db.Model(&domain.User{}).Preload("Organization").Preload("Role").Find(&users) } else { combinedFilter := func(filters ...FilterFunc) FilterFunc { return func(user *gorm.DB) *gorm.DB { @@ -158,7 +156,7 @@ func (r *UserRepository) List(filters ...FilterFunc) (*[]domain.User, error) { } } cFunc := combinedFilter(filters...) - res = cFunc(r.db.Model(&User{}).Preload("Organization").Preload("Role")).Find(&users) + res = cFunc(r.db.Model(&domain.User{}).Preload("Organization").Preload("Role")).Find(&users) } if res.Error != nil { @@ -178,7 +176,7 @@ func (r *UserRepository) List(filters ...FilterFunc) (*[]domain.User, error) { } func (r *UserRepository) ListWithPagination(pg *pagination.Pagination, organizationId string) (*[]domain.User, error) { - var users []User + var users []domain.User if pg == nil { pg = pagination.NewPagination(nil) @@ -207,8 +205,8 @@ func (r *UserRepository) Get(accountId string, organizationId string) (domain.Us return r.reflect(user), nil } func (r *UserRepository) GetByUuid(userId uuid.UUID) (respUser domain.User, err error) { - user := User{} - res := r.db.Model(&User{}).Preload("Organization").Preload("Role").Find(&user, "id = ?", userId) + user := domain.User{} + res := r.db.Model(&domain.User{}).Preload("Organization").Preload("Role").Find(&user, "id = ?", userId) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) @@ -220,10 +218,10 @@ func (r *UserRepository) GetByUuid(userId uuid.UUID) (respUser domain.User, err return r.reflect(user), nil } -func (r *UserRepository) UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId uuid.UUID, +func (r *UserRepository) UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId string, email string, department string, description string) (domain.User, error) { - var user User - res := r.db.Model(&User{}).Where("id = ?", uuid).Updates(User{ + var user domain.User + res := r.db.Model(&domain.User{}).Where("id = ?", uuid).Updates(domain.User{ AccountId: accountId, Name: name, Password: password, @@ -239,14 +237,14 @@ func (r *UserRepository) UpdateWithUuid(uuid uuid.UUID, accountId string, name s log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return domain.User{}, res.Error } - res = r.db.Model(&User{}).Preload("Organization").Preload("Role").Where("id = ?", uuid).Find(&user) + res = r.db.Model(&domain.User{}).Preload("Organization").Preload("Role").Where("id = ?", uuid).Find(&user) if res.Error != nil { return domain.User{}, res.Error } return r.reflect(user), nil } func (r *UserRepository) UpdatePassword(userId uuid.UUID, organizationId string, password string, isTemporary bool) error { - var updateUser = User{ + var updateUser = domain.User{ Password: password, } if isTemporary { @@ -254,7 +252,7 @@ func (r *UserRepository) UpdatePassword(userId uuid.UUID, organizationId string, } else { updateUser.PasswordUpdatedAt = time.Now() } - res := r.db.Model(&User{}).Where("id = ? AND organization_id = ?", userId, organizationId). + res := r.db.Model(&domain.User{}).Where("id = ? AND organization_id = ?", userId, organizationId). Select("password", "password_updated_at").Updates(updateUser) if res.RowsAffected == 0 || res.Error != nil { @@ -268,88 +266,33 @@ func (r *UserRepository) UpdatePassword(userId uuid.UUID, organizationId string, return nil } func (r *UserRepository) DeleteWithUuid(uuid uuid.UUID) error { - res := r.db.Unscoped().Delete(&User{}, "id = ?", uuid) - if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return res.Error - } - return nil -} - -// Deprecated: -type Policy struct { - gorm.Model - - //ID uuid.UUID `gorm:"primarykey;type:uuid;"` - RoleId uuid.UUID - Role Role `gorm:"references:ID"` - Name string - Description string - Create bool `gorm:"column:c"` - CreatePriviledge string - Update bool `gorm:"column:u"` - UpdatePriviledge string - Read bool `gorm:"column:r"` - ReadPriviledge string - Delete bool `gorm:"column:d"` - DeletePriviledge string - Creator uuid.UUID -} - -type UserRole struct { - UserId uuid.UUID - User User - RoleId uuid.UUID - Role Role -} - -func (r *UserRepository) AssignRoleWithUuid(uuid uuid.UUID, roleName string) error { - _, err := r.GetByUuid(uuid) - if err != nil { - return err - } - - role, err := r.getRoleByName(roleName) - if err != nil { - return err - } - - newRole := UserRole{ - UserId: uuid, - RoleId: role.ID, - } - res := r.db.Create(&newRole) + res := r.db.Unscoped().Delete(&domain.User{}, "id = ?", uuid) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return res.Error } - return nil } -func (r *UserRepository) AssignRole(accountId string, organizationId string, roleName string) error { - user, err := r.getUserByAccountId(accountId, organizationId) - if err != nil { - return err - } - - role, err := r.getRoleByName(roleName) - if err != nil { - return err - } - - newRole := UserRole{ - UserId: user.ID, - RoleId: role.ID, - } - res := r.db.Create(&newRole) - if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return res.Error - } - - return nil -} +//// Deprecated: +//type Policy struct { +// gorm.Model +// +// //ID uuid.UUID `gorm:"primarykey;type:uuid;"` +// RoleId uuid.UUID +// Role Role `gorm:"references:ID"` +// Name string +// Description string +// Create bool `gorm:"column:c"` +// CreatePriviledge string +// Update bool `gorm:"column:u"` +// UpdatePriviledge string +// Read bool `gorm:"column:r"` +// ReadPriviledge string +// Delete bool `gorm:"column:d"` +// DeletePriviledge string +// Creator uuid.UUID +//} func (r *UserRepository) GetRoleByName(roleName string) (domain.Role, error) { role, err := r.getRoleByName(roleName) @@ -361,7 +304,7 @@ func (r *UserRepository) GetRoleByName(roleName string) (domain.Role, error) { } func (r *UserRepository) FetchRoles() (*[]domain.Role, error) { - var roles []Role + var roles []domain.Role res := r.db.Find(&roles) if res.Error != nil { @@ -383,30 +326,30 @@ func (r *UserRepository) FetchRoles() (*[]domain.Role, error) { } // private members -func (r *UserRepository) getUserByAccountId(accountId string, organizationId string) (User, error) { - user := User{} - res := r.db.Model(&User{}).Preload("Organization").Preload("Role"). +func (r *UserRepository) getUserByAccountId(accountId string, organizationId string) (domain.User, error) { + user := domain.User{} + res := r.db.Model(&domain.User{}).Preload("Organization").Preload("Role"). Find(&user, "account_id = ? AND organization_id = ?", accountId, organizationId) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return User{}, res.Error + return domain.User{}, res.Error } if res.RowsAffected == 0 { - return User{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") + return domain.User{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") } return user, nil } -func (r *UserRepository) getRoleByName(roleName string) (Role, error) { - role := Role{} +func (r *UserRepository) getRoleByName(roleName string) (domain.Role, error) { + role := domain.Role{} res := r.db.First(&role, "name = ?", roleName) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return Role{}, res.Error + return domain.Role{}, res.Error } if res.RowsAffected == 0 { - return Role{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") + return domain.Role{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") } //if res.RowsAffected == 0 { @@ -416,7 +359,7 @@ func (r *UserRepository) getRoleByName(roleName string) (Role, error) { return role, nil } -func (r *UserRepository) reflect(user User) domain.User { +func (r *UserRepository) reflect(user domain.User) domain.User { role := domain.Role{ ID: user.Role.ID, Name: user.Role.Name, @@ -444,7 +387,7 @@ func (r *UserRepository) reflect(user User) domain.User { Phone: user.Organization.Phone, Status: user.Organization.Status, StatusDesc: user.Organization.StatusDesc, - Creator: user.Organization.Creator.String(), + Creator: user.Organization.Creator, CreatedAt: user.Organization.CreatedAt, UpdatedAt: user.Organization.UpdatedAt, } @@ -461,13 +404,13 @@ func (r *UserRepository) reflect(user User) domain.User { //} return domain.User{ - ID: user.ID.String(), + ID: user.ID, AccountId: user.AccountId, Password: user.Password, Name: user.Name, Role: role, Organization: organization, - Creator: user.Creator.String(), + Creator: user.Creator, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, Email: user.Email, @@ -477,7 +420,7 @@ func (r *UserRepository) reflect(user User) domain.User { } } -func (r *UserRepository) reflectRole(role Role) domain.Role { +func (r *UserRepository) reflectRole(role domain.Role) domain.Role { return domain.Role{ ID: role.ID, Name: role.Name, diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index a641fbee..141df683 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -173,11 +173,7 @@ func (u *AuthUsecase) FindId(code string, email string, userName string, organiz if err != nil { return "", httpErrors.NewInternalServerError(err, "", "") } - userUuid, err := uuid.Parse((*users)[0].ID) - if err != nil { - return "", httpErrors.NewInternalServerError(err, "", "") - } - emailCode, err := u.authRepository.GetEmailCode(userUuid) + emailCode, err := u.authRepository.GetEmailCode((*users)[0].ID) if err != nil { return "", httpErrors.NewInternalServerError(err, "", "") } @@ -187,7 +183,7 @@ func (u *AuthUsecase) FindId(code string, email string, userName string, organiz if emailCode.Code != code { return "", httpErrors.NewBadRequestError(fmt.Errorf("invalid code"), "A_INVALID_CODE", "") } - if err := u.authRepository.DeleteEmailCode(userUuid); err != nil { + if err := u.authRepository.DeleteEmailCode((*users)[0].ID); err != nil { return "", httpErrors.NewInternalServerError(err, "", "") } @@ -205,11 +201,7 @@ func (u *AuthUsecase) FindPassword(code string, accountId string, email string, return httpErrors.NewInternalServerError(err, "", "") } user := (*users)[0] - userUuid, err := uuid.Parse(user.ID) - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - emailCode, err := u.authRepository.GetEmailCode(userUuid) + emailCode, err := u.authRepository.GetEmailCode(user.ID) if err != nil { return httpErrors.NewInternalServerError(err, "", "") } @@ -239,7 +231,7 @@ func (u *AuthUsecase) FindPassword(code string, accountId string, email string, if user.Password, err = helper.HashPassword(randomPassword); err != nil { return httpErrors.NewInternalServerError(err, "", "") } - if err = u.userRepository.UpdatePassword(userUuid, organizationId, user.Password, true); err != nil { + if err = u.userRepository.UpdatePassword(user.ID, organizationId, user.Password, true); err != nil { return httpErrors.NewInternalServerError(err, "", "") } @@ -255,7 +247,7 @@ func (u *AuthUsecase) FindPassword(code string, accountId string, email string, return httpErrors.NewInternalServerError(err, "", "") } - if err = u.authRepository.DeleteEmailCode(userUuid); err != nil { + if err = u.authRepository.DeleteEmailCode(user.ID); err != nil { return httpErrors.NewInternalServerError(err, "", "") } @@ -285,17 +277,13 @@ func (u *AuthUsecase) VerifyIdentity(accountId string, email string, userName st if err != nil { return httpErrors.NewInternalServerError(err, "", "") } - userUuid, err := uuid.Parse((*users)[0].ID) - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - _, err = u.authRepository.GetEmailCode(userUuid) + _, err = u.authRepository.GetEmailCode((*users)[0].ID) if err != nil { - if err := u.authRepository.CreateEmailCode(userUuid, code); err != nil { + if err := u.authRepository.CreateEmailCode((*users)[0].ID, code); err != nil { return httpErrors.NewInternalServerError(err, "", "") } } else { - if err := u.authRepository.UpdateEmailCode(userUuid, code); err != nil { + if err := u.authRepository.UpdateEmailCode((*users)[0].ID, code); err != nil { return httpErrors.NewInternalServerError(err, "", "") } } diff --git a/internal/usecase/user.go b/internal/usecase/user.go index 12cfa33a..1c1ce670 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -77,11 +77,7 @@ func (u *UserUsecase) RenewalPasswordExpiredTimeByAccountId(ctx context.Context, } return httpErrors.NewInternalServerError(err, "", "") } - userId, err := uuid.Parse(user.ID) - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - return u.RenewalPasswordExpiredTime(ctx, userId) + return u.RenewalPasswordExpiredTime(ctx, user.ID) } func (u *UserUsecase) ResetPassword(userId uuid.UUID) error { @@ -141,11 +137,7 @@ func (u *UserUsecase) ResetPasswordByAccountId(accountId string, organizationId } return httpErrors.NewInternalServerError(err, "", "") } - userId, err := uuid.Parse(user.ID) - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - return u.ResetPassword(userId) + return u.ResetPassword(user.ID) } func (u *UserUsecase) ValidateAccount(userId uuid.UUID, password string, organizationId string) error { @@ -322,16 +314,12 @@ func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId s if err != nil { return errors.Wrap(err, "getting user from repository failed") } - userUuid, err := uuid.Parse(user.ID) - if err != nil { - return errors.Wrap(err, "parsing uuid failed") - } hashedPassword, err := helper.HashPassword(newPassword) if err != nil { return errors.Wrap(err, "hashing password failed") } - err = u.userRepository.UpdatePassword(userUuid, organizationId, hashedPassword, false) + err = u.userRepository.UpdatePassword(user.ID, organizationId, hashedPassword, false) if err != nil { return errors.Wrap(err, "updating user in repository failed") } @@ -437,11 +425,6 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u return nil, fmt.Errorf("multiple users found") } - userUuid, err := uuid.Parse((*users)[0].ID) - if err != nil { - return nil, err - } - originPassword := (*users)[0].Password roleUuid := (*users)[0].Role.ID @@ -449,7 +432,7 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u return nil, err } - *user, err = u.userRepository.UpdateWithUuid(userUuid, user.AccountId, user.Name, originPassword, roleUuid, user.Email, + *user, err = u.userRepository.UpdateWithUuid((*users)[0].ID, user.AccountId, user.Name, originPassword, roleUuid, user.Email, user.Department, user.Description) if err != nil { return nil, errors.Wrap(err, "updating user in repository failed") @@ -483,11 +466,7 @@ func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string, o return err } - userUuid, err := uuid.Parse(user.ID) - if err != nil { - return err - } - err = u.userRepository.DeleteWithUuid(userUuid) + err = u.userRepository.DeleteWithUuid(user.ID) if err != nil { return err } @@ -597,21 +576,16 @@ func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId st if user.Role.Name != (*users)[0].Role.Name { originGroupName := fmt.Sprintf("%s@%s", (*users)[0].Role.Name, userInfo.GetOrganizationId()) newGroupName := fmt.Sprintf("%s@%s", user.Role.Name, userInfo.GetOrganizationId()) - if err := u.kc.LeaveGroup(userInfo.GetOrganizationId(), (*users)[0].ID, originGroupName); err != nil { + if err := u.kc.LeaveGroup(userInfo.GetOrganizationId(), (*users)[0].ID.String(), originGroupName); err != nil { log.ErrorfWithContext(ctx, "leave group in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err, "", "") } - if err := u.kc.JoinGroup(userInfo.GetOrganizationId(), (*users)[0].ID, newGroupName); err != nil { + if err := u.kc.JoinGroup(userInfo.GetOrganizationId(), (*users)[0].ID.String(), newGroupName); err != nil { log.ErrorfWithContext(ctx, "join group in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err, "", "") } } - userUuid, err := uuid.Parse((*users)[0].ID) - if err != nil { - return nil, err - } - originPassword := (*users)[0].Password roles, err := u.userRepository.FetchRoles() @@ -628,7 +602,7 @@ func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId st return nil, err } - *user, err = u.userRepository.UpdateWithUuid(userUuid, user.AccountId, user.Name, originPassword, roleUuid, user.Email, + *user, err = u.userRepository.UpdateWithUuid((*users)[0].ID, user.AccountId, user.Name, originPassword, roleUuid, user.Email, user.Department, user.Description) if err != nil { return nil, errors.Wrap(err, "updating user in repository failed") diff --git a/pkg/domain/endpoint.go b/pkg/domain/endpoint.go index 84006eac..6926dbe5 100644 --- a/pkg/domain/endpoint.go +++ b/pkg/domain/endpoint.go @@ -1,11 +1,19 @@ package domain -import "github.com/google/uuid" +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) type Endpoint struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - Group string `json:"group"` - PermissionID uuid.UUID `json:"permissionId"` - Permission Permission `json:"permission"` + ID uuid.UUID `gorm:"type:uuid;primaryKey;" json:"id"` + Name string `gorm:"type:text;not null;unique" json:"name"` + Group string `gorm:"type:text;" json:"group"` + PermissionID uuid.UUID `gorm:"type:uuid;" json:"permissionId"` + Permission *Permission `gorm:"foreignKey:PermissionID;" json:"permission"` +} + +func (e *Endpoint) BeforeCreate(tx *gorm.DB) error { + e.ID = uuid.New() + return nil } diff --git a/pkg/domain/organization.go b/pkg/domain/organization.go index 31667dc3..87f94611 100644 --- a/pkg/domain/organization.go +++ b/pkg/domain/organization.go @@ -1,6 +1,7 @@ package domain import ( + "gorm.io/gorm" "time" ) @@ -48,8 +49,10 @@ func (m OrganizationStatus) FromString(s string) OrganizationStatus { return OrganizationStatus_ERROR } -type Organization = struct { - ID string `json:"id"` +type Organization struct { + gorm.Model + + ID string `gorm:"primarykey;type:varchar(36);not null" json:"id"` Name string `json:"name"` Description string `json:"description"` Phone string `json:"phone"` diff --git a/pkg/domain/permmision.go b/pkg/domain/permmision.go index c9b40762..c3cf0d84 100644 --- a/pkg/domain/permmision.go +++ b/pkg/domain/permmision.go @@ -1,18 +1,30 @@ package domain -import "github.com/google/uuid" +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) type Permission struct { - ID uuid.UUID `json:"id"` + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid;" json:"id"` Name string `json:"name"` - IsAllowed *bool `json:"is_allowed,omitempty"` - RoleID *uuid.UUID `json:"role_id,omitempty"` - Role *Role `json:"role,omitempty"` - Endpoints []*Endpoint `json:"endpoints,omitempty"` + IsAllowed *bool `gorm:"type:boolean;" json:"is_allowed,omitempty"` + RoleID *string `json:"role_id,omitempty"` + Role *Role `gorm:"foreignKey:RoleID;references:ID;" json:"role,omitempty"` + Endpoints []*Endpoint `gorm:"one2many:endpoints;" json:"endpoints,omitempty"` // omit empty ParentID *uuid.UUID `json:"parent_id,omitempty"` - Parent *Permission `json:"parent,omitempty"` - Children []*Permission `json:"children,omitempty"` + Parent *Permission `gorm:"foreignKey:ParentID;references:ID;" json:"parent,omitempty"` + Children []*Permission `gorm:"foreignKey:ParentID;references:ID;" json:"children,omitempty"` +} + +func (p *Permission) BeforeCreate(tx *gorm.DB) (err error) { + if p.ID == uuid.Nil { + p.ID = uuid.New() + } + return nil } diff --git a/pkg/domain/project.go b/pkg/domain/project.go index 38e670e6..c55d0a3d 100644 --- a/pkg/domain/project.go +++ b/pkg/domain/project.go @@ -99,7 +99,7 @@ type ProjectMember struct { ProjectUserId uuid.UUID `json:"projectUserId"` ProjectUser *ProjectUser `gorm:"foreignKey:ProjectUserId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectUser"` ProjectRoleId string `json:"projectRoleId"` - ProjectRole *ProjectRole `gorm:"foreignKey:ProjectRoleId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectRole"` + ProjectRole *ProjectRole `gorm:"foreignKey:ProjectRoleId;references:RoleID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectRole"` IsProjectLeader bool `gorm:"default:false" json:"projectLeader"` CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` diff --git a/pkg/domain/role.go b/pkg/domain/role.go index d2025254..a733e9a5 100644 --- a/pkg/domain/role.go +++ b/pkg/domain/role.go @@ -2,6 +2,7 @@ package domain import ( "github.com/google/uuid" + "gorm.io/gorm" "time" ) @@ -13,11 +14,18 @@ const ( RoleTypeProject RoleType = "project" ) +func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { + r.ID = uuid.New().String() + return nil +} + type Role struct { - ID uuid.UUID `json:"id"` + gorm.Model + + ID string `gorm:"primarykey;" json:"id"` Name string `json:"name"` OrganizationID string `json:"organizationId"` - Organization Organization `json:"organization"` + Organization Organization `gorm:"foreignKey:OrganizationID;references:ID;" json:"organization"` Type string `json:"type"` Description string `json:"description"` Creator uuid.UUID `json:"creator"` @@ -26,15 +34,15 @@ type Role struct { } type TksRole struct { - RoleID uuid.UUID `json:"roleId"` - Role + RoleID string `gorm:"primarykey;" json:"roleId"` + Role Role `gorm:"foreignKey:RoleID;references:ID;"` } type ProjectRole struct { - RoleID uuid.UUID `json:"roleId"` - Role Role `json:"role"` - ProjectID uuid.UUID `json:"projectID"` - Project Project `json:"project"` + RoleID string `gorm:"primaryKey" json:"roleId"` + Role Role `gorm:"foreignKey:RoleID;references:ID;" json:"role"` + ProjectID string `json:"projectID"` + Project Project `gorm:"foreignKey:ProjectID;references:ID;" json:"project"` } //type Role = struct { diff --git a/pkg/domain/user.go b/pkg/domain/user.go index 138b0edc..77c9092b 100644 --- a/pkg/domain/user.go +++ b/pkg/domain/user.go @@ -1,17 +1,21 @@ package domain import ( + "github.com/google/uuid" + "gorm.io/gorm" "time" ) -type User = struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Password string `json:"password"` - Name string `json:"name"` - Token string `json:"token"` - Role Role `json:"role"` - Organization Organization `json:"organization"` +type User struct { + ID uuid.UUID `gorm:"primarykey;type:uuid" json:"id"` + AccountId string `json:"accountId"` + Password string `json:"password"` + Name string `json:"name"` + Token string `json:"token"` + RoleId string + Role Role `gorm:"foreignKey:RoleId;references:ID" json:"role"` + OrganizationId string + Organization Organization `gorm:"foreignKey:OrganizationId;references:ID" json:"organization"` Creator string `json:"creator"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` @@ -23,27 +27,28 @@ type User = struct { Description string `json:"description"` } -type SimpleRoleResponse = struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` -} - -type Policy = struct { - ID string `json:"id"` - Name string `json:"name"` - Create bool `json:"create"` - CreatePriviledge string `json:"createPriviledge"` - Update bool `json:"update"` - UpdatePriviledge string `json:"updatePriviledge"` - Read bool `json:"read"` - ReadPriviledge string `json:"readPriviledge"` - Delete bool `json:"delete"` - DeletePriviledge string `json:"deletePriviledge"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} +func (g *User) BeforeCreate(tx *gorm.DB) (err error) { + g.PasswordUpdatedAt = time.Now() + return nil +} + +// +//// Deprecated: Policy is deprecated, use Permission instead. +//type Policy = struct { +// ID string `json:"id"` +// Name string `json:"name"` +// Create bool `json:"create"` +// CreatePriviledge string `json:"createPriviledge"` +// Update bool `json:"update"` +// UpdatePriviledge string `json:"updatePriviledge"` +// Read bool `json:"read"` +// ReadPriviledge string `json:"readPriviledge"` +// Delete bool `json:"delete"` +// DeletePriviledge string `json:"deletePriviledge"` +// Creator string `json:"creator"` +// CreatedAt time.Time `json:"createdAt"` +// UpdatedAt time.Time `json:"updatedAt"` +//} type CreateUserRequest struct { AccountId string `json:"accountId" validate:"required"` @@ -55,6 +60,12 @@ type CreateUserRequest struct { Description string `json:"description" validate:"min=0,max=100"` } +type SimpleRoleResponse = struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` +} + type SimpleUserResponse struct { ID string `json:"id"` AccountId string `json:"accountId"` From fa75ab629b5e6dc2cb817a11c02d462efc5d0b9c Mon Sep 17 00:00:00 2001 From: donggyu Date: Thu, 15 Feb 2024 15:55:19 +0900 Subject: [PATCH 04/15] refactoring: remove pw validation on DB which is duplicated action --- internal/usecase/auth.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index 141df683..564601a1 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -74,16 +74,19 @@ func (u *AuthUsecase) Login(accountId string, password string, organizationId st if err != nil { return domain.User{}, httpErrors.NewBadRequestError(err, "A_INVALID_ID", "") } - if !helper.CheckPasswordHash(user.Password, password) { - return domain.User{}, httpErrors.NewBadRequestError(fmt.Errorf("Mismatch password"), "A_INVALID_PASSWORD", "") - } + var accountToken *domain.User - // Authentication with Keycloak - if organizationId == "master" && accountId == "admin" { - accountToken, err = u.kc.LoginAdmin(accountId, password) - } else { - accountToken, err = u.kc.Login(accountId, password, organizationId) + accountToken, err = u.kc.Login(accountId, password, organizationId) + if err != nil { + apiErr, ok := err.(*gocloak.APIError) + if ok { + if apiErr.Code == 401 { + return domain.User{}, httpErrors.NewBadRequestError(fmt.Errorf("Mismatch password"), "A_INVALID_PASSWORD", "") + } + } + return domain.User{}, httpErrors.NewInternalServerError(err, "", "") } + log.Errorf("err: %v", err) if err != nil { //TODO: implement not found handling return domain.User{}, err From 49f32a12ad25bc08cbe03499f62167ce8f2e53e1 Mon Sep 17 00:00:00 2001 From: donggyu Date: Thu, 15 Feb 2024 16:01:37 +0900 Subject: [PATCH 05/15] improvement: add/remove tks roles on creation/deletion of organization --- internal/usecase/organization.go | 58 +++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/internal/usecase/organization.go b/internal/usecase/organization.go index 3e4cc97d..ad074123 100644 --- a/internal/usecase/organization.go +++ b/internal/usecase/organization.go @@ -28,16 +28,18 @@ type IOrganizationUsecase interface { } type OrganizationUsecase struct { - repo repository.IOrganizationRepository - argo argowf.ArgoClient - kc keycloak.IKeycloak + repo repository.IOrganizationRepository + roleRepo repository.IRoleRepository + argo argowf.ArgoClient + kc keycloak.IKeycloak } func NewOrganizationUsecase(r repository.Repository, argoClient argowf.ArgoClient, kc keycloak.IKeycloak) IOrganizationUsecase { return &OrganizationUsecase{ - repo: r.Organization, - argo: argoClient, - kc: kc, + repo: r.Organization, + roleRepo: r.Role, + argo: argoClient, + kc: kc, } } @@ -55,10 +57,36 @@ func (u *OrganizationUsecase) Create(ctx context.Context, in *domain.Organizatio return "", err } + // Create organization in DB _, err = u.repo.Create(organizationId, in.Name, creator, in.Phone, in.Description) if err != nil { return "", err } + + // Create admin roles in DB + if err := u.roleRepo.Create(domain.TksRole{ + Role: domain.Role{ + OrganizationID: organizationId, + Name: "admin", + Description: "admin", + Type: string(domain.RoleTypeTks), + }, + }); err != nil { + return "", err + } + + // Create user roles in DB + if err := u.roleRepo.Create(domain.TksRole{ + Role: domain.Role{ + OrganizationID: organizationId, + Name: "user", + Description: "user", + Type: string(domain.RoleTypeTks), + }, + }); err != nil { + return "", err + } + workflowId, err := u.argo.SumbitWorkflowFromWftpl( "tks-create-contract-repo", argowf.SubmitOptions{ @@ -106,8 +134,22 @@ func (u *OrganizationUsecase) Delete(organizationId string, accessToken string) return err } - // [TODO] validation - // cluster 나 appgroup 등이 삭제 되었는지 확인 + // delete roles in DB + roles, err := u.roleRepo.ListTksRoles(organizationId, nil) + if err != nil { + return err + } + for _, role := range roles { + uuid, err := uuid.Parse(role.RoleID) + if err != nil { + return err + } + if err := u.roleRepo.DeleteCascade(uuid); err != nil { + return err + } + } + + // delete organization in DB err = u.repo.Delete(organizationId) if err != nil { return err From 6c0f93de5e5341a29c14b3a04b870a3e7239b8b4 Mon Sep 17 00:00:00 2001 From: donggyu Date: Thu, 15 Feb 2024 17:54:35 +0900 Subject: [PATCH 06/15] refactoring: remove pw on DB which is duplicated in keycloak --- internal/keycloak/keycloak.go | 10 +- internal/repository/role.go | 10 ++ internal/repository/user.go | 79 ++++++-------- internal/usecase/auth.go | 14 +-- internal/usecase/user.go | 188 ++++++++++------------------------ pkg/domain/user.go | 2 +- 6 files changed, 98 insertions(+), 205 deletions(-) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 4c41f89c..8d2252f5 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -28,7 +28,7 @@ type IKeycloak interface { DeleteRealm(organizationId string) error UpdateRealm(organizationId string, organizationConfig domain.Organization) error - CreateUser(organizationId string, user *gocloak.User) error + CreateUser(organizationId string, user *gocloak.User) (string, error) GetUser(organizationId string, userAccountId string) (*gocloak.User, error) GetUsers(organizationId string) ([]*gocloak.User, error) DeleteUser(organizationId string, userAccountId string) error @@ -297,16 +297,16 @@ func (k *Keycloak) DeleteRealm(organizationId string) error { return nil } -func (k *Keycloak) CreateUser(organizationId string, user *gocloak.User) error { +func (k *Keycloak) CreateUser(organizationId string, user *gocloak.User) (string, error) { ctx := context.Background() token := k.adminCliToken user.Enabled = gocloak.BoolP(true) - _, err := k.client.CreateUser(ctx, token.AccessToken, organizationId, *user) + uuid, err := k.client.CreateUser(ctx, token.AccessToken, organizationId, *user) if err != nil { - return err + return "", err } - return nil + return uuid, nil } func (k *Keycloak) GetUser(organizationId string, accountId string) (*gocloak.User, error) { diff --git a/internal/repository/role.go b/internal/repository/role.go index bd61871b..9f63ba2a 100644 --- a/internal/repository/role.go +++ b/internal/repository/role.go @@ -46,6 +46,7 @@ type IRoleRepository interface { ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) Get(id uuid.UUID) (*domain.Role, error) GetTksRole(id uuid.UUID) (*domain.TksRole, error) + GetTksRoleByRoleName(roleName string) (*domain.TksRole, error) GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error) DeleteCascade(id uuid.UUID) error Update(roleObj interface{}) error @@ -55,6 +56,15 @@ type RoleRepository struct { db *gorm.DB } +func (r RoleRepository) GetTksRoleByRoleName(roleName string) (*domain.TksRole, error) { + var role domain.TksRole + if err := r.db.Preload("Role").First(&role, "Role.name = ?", roleName).Error; err != nil { + return nil, err + } + + return ConvertRepoToDomainTksRole(&role), nil +} + func (r RoleRepository) Create(roleObj interface{}) error { if roleObj == nil { return fmt.Errorf("roleObj is nil") diff --git a/internal/repository/user.go b/internal/repository/user.go index 4663f30a..9de16cd2 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -13,20 +13,18 @@ import ( // Interface type IUserRepository interface { - Create(accountId string, organizationId string, password string, name string) (domain.User, error) - CreateWithUuid(uuid uuid.UUID, accountId string, name string, password string, email string, + CreateWithUuid(uuid uuid.UUID, accountId string, name string, email string, department string, description string, organizationId string, roleId string) (domain.User, error) List(filters ...FilterFunc) (out *[]domain.User, err error) ListWithPagination(pg *pagination.Pagination, organizationId string) (out *[]domain.User, err error) Get(accountId string, organizationId string) (domain.User, error) GetByUuid(userId uuid.UUID) (domain.User, error) - UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId string, email string, + UpdateWithUuid(uuid uuid.UUID, accountId string, name string, roleId string, email string, department string, description string) (domain.User, error) - UpdatePassword(userId uuid.UUID, organizationId string, password string, isTemporary bool) error + UpdatePasswordAt(userId uuid.UUID, organizationId string, isTemporary bool) error DeleteWithUuid(uuid uuid.UUID) error Flush(organizationId string) error - FetchRoles() (out *[]domain.Role, err error) AccountIdFilter(accountId string) FilterFunc OrganizationFilter(organization string) FilterFunc EmailFilter(email string) FilterFunc @@ -78,28 +76,12 @@ func NewUserRepository(db *gorm.DB) IUserRepository { // return nil //} -func (r *UserRepository) Create(accountId string, organizationId string, password string, name string) (domain.User, error) { - newUser := domain.User{ - AccountId: accountId, - Password: password, - OrganizationId: organizationId, - Name: name, - } - res := r.db.Create(&newUser) - if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.User{}, res.Error - } - - return r.reflect(newUser), nil -} -func (r *UserRepository) CreateWithUuid(uuid uuid.UUID, accountId string, name string, password string, email string, +func (r *UserRepository) CreateWithUuid(uuid uuid.UUID, accountId string, name string, email string, department string, description string, organizationId string, roleId string) (domain.User, error) { newUser := domain.User{ ID: uuid, AccountId: accountId, - Password: password, Name: name, Email: email, Department: department, @@ -218,13 +200,12 @@ func (r *UserRepository) GetByUuid(userId uuid.UUID) (respUser domain.User, err return r.reflect(user), nil } -func (r *UserRepository) UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId string, +func (r *UserRepository) UpdateWithUuid(uuid uuid.UUID, accountId string, name string, roleId string, email string, department string, description string) (domain.User, error) { var user domain.User res := r.db.Model(&domain.User{}).Where("id = ?", uuid).Updates(domain.User{ AccountId: accountId, Name: name, - Password: password, Email: email, Department: department, Description: description, @@ -243,17 +224,15 @@ func (r *UserRepository) UpdateWithUuid(uuid uuid.UUID, accountId string, name s } return r.reflect(user), nil } -func (r *UserRepository) UpdatePassword(userId uuid.UUID, organizationId string, password string, isTemporary bool) error { - var updateUser = domain.User{ - Password: password, - } +func (r *UserRepository) UpdatePasswordAt(userId uuid.UUID, organizationId string, isTemporary bool) error { + var updateUser = domain.User{} if isTemporary { updateUser.PasswordUpdatedAt = time.Time{} } else { updateUser.PasswordUpdatedAt = time.Now() } res := r.db.Model(&domain.User{}).Where("id = ? AND organization_id = ?", userId, organizationId). - Select("password", "password_updated_at").Updates(updateUser) + Select("password_updated_at").Updates(updateUser) if res.RowsAffected == 0 || res.Error != nil { return httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") @@ -303,27 +282,27 @@ func (r *UserRepository) GetRoleByName(roleName string) (domain.Role, error) { return r.reflectRole(role), nil } -func (r *UserRepository) FetchRoles() (*[]domain.Role, error) { - var roles []domain.Role - res := r.db.Find(&roles) - - if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return nil, res.Error - } - - if res.RowsAffected == 0 { - return nil, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") - } - - var out []domain.Role - for _, role := range roles { - outRole := r.reflectRole(role) - out = append(out, outRole) - } - - return &out, nil -} +//func (r *UserRepository) FetchRoles() (*[]domain.Role, error) { +// var roles []domain.Role +// res := r.db.Find(&roles) +// +// if res.Error != nil { +// log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) +// return nil, res.Error +// } +// +// if res.RowsAffected == 0 { +// return nil, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") +// } +// +// var out []domain.Role +// for _, role := range roles { +// outRole := r.reflectRole(role) +// out = append(out, outRole) +// } +// +// return &out, nil +//} // private members func (r *UserRepository) getUserByAccountId(accountId string, organizationId string) (domain.User, error) { diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index 564601a1..4fe4fd15 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -36,7 +36,6 @@ type IAuthUsecase interface { FindId(code string, email string, userName string, organizationId string) (string, error) FindPassword(code string, accountId string, email string, userName string, organizationId string) error VerifyIdentity(accountId string, email string, userName string, organizationId string) error - FetchRoles() (out []domain.Role, err error) SingleSignIn(organizationId, accountId, password string) ([]*http.Cookie, error) SingleSignOut(organizationId string) (string, []*http.Cookie, error) VerifyToken(token string) (bool, error) @@ -231,10 +230,7 @@ func (u *AuthUsecase) FindPassword(code string, accountId string, email string, return httpErrors.NewInternalServerError(err, "", "") } - if user.Password, err = helper.HashPassword(randomPassword); err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - if err = u.userRepository.UpdatePassword(user.ID, organizationId, user.Password, true); err != nil { + if err = u.userRepository.UpdatePasswordAt(user.ID, organizationId, true); err != nil { return httpErrors.NewInternalServerError(err, "", "") } @@ -307,14 +303,6 @@ func (u *AuthUsecase) VerifyIdentity(accountId string, email string, userName st return nil } -func (u *AuthUsecase) FetchRoles() (out []domain.Role, err error) { - roles, err := u.userRepository.FetchRoles() - if err != nil { - return nil, err - } - return *roles, nil -} - func (u *AuthUsecase) SingleSignIn(organizationId, accountId, password string) ([]*http.Cookie, error) { cookies, err := makingCookie(organizationId, accountId, password) if err != nil { diff --git a/internal/usecase/user.go b/internal/usecase/user.go index 1c1ce670..e07d2cbb 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -47,6 +47,7 @@ type IUserUsecase interface { type UserUsecase struct { userRepository repository.IUserRepository + roleRepository repository.IRoleRepository organizationRepository repository.IOrganizationRepository kc keycloak.IKeycloak } @@ -60,7 +61,7 @@ func (u *UserUsecase) RenewalPasswordExpiredTime(ctx context.Context, userId uui return httpErrors.NewInternalServerError(err, "", "") } - err = u.userRepository.UpdatePassword(userId, user.Organization.ID, user.Password, false) + err = u.userRepository.UpdatePasswordAt(userId, user.Organization.ID, false) if err != nil { log.ErrorfWithContext(ctx, "failed to update password expired time: %v", err) return httpErrors.NewInternalServerError(err, "", "") @@ -107,10 +108,7 @@ func (u *UserUsecase) ResetPassword(userId uuid.UUID) error { return httpErrors.NewInternalServerError(err, "", "") } - if user.Password, err = helper.HashPassword(randomPassword); err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - if err = u.userRepository.UpdatePassword(userId, user.Organization.ID, user.Password, true); err != nil { + if err = u.userRepository.UpdatePasswordAt(userId, user.Organization.ID, true); err != nil { return httpErrors.NewInternalServerError(err, "", "") } @@ -194,6 +192,7 @@ func (u *UserUsecase) DeleteAdmin(organizationId string) error { } func (u *UserUsecase) CreateAdmin(orgainzationId string, email string) (*domain.User, error) { + // Generate Admin user object randomPassword := helper.GenerateRandomString(passwordLength) user := domain.User{ AccountId: "admin", @@ -208,79 +207,27 @@ func (u *UserUsecase) CreateAdmin(orgainzationId string, email string) (*domain. Name: "admin", } - // Create user in keycloak - groups := []string{fmt.Sprintf("%s@%s", user.Role.Name, orgainzationId)} - err := u.kc.CreateUser(orgainzationId, &gocloak.User{ - Username: gocloak.StringP(user.AccountId), - Email: gocloak.StringP(user.Email), - Credentials: &[]gocloak.CredentialRepresentation{ - { - Type: gocloak.StringP("password"), - Value: gocloak.StringP(user.Password), - Temporary: gocloak.BoolP(false), - }, - }, - Groups: &groups, - FirstName: gocloak.StringP(user.Name), - }) - if err != nil { - return nil, errors.Wrap(err, "creating user in keycloak failed") - } - keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId) - if err != nil { - return nil, errors.Wrap(err, "getting user from keycloak failed") - } - - userUuid, err := uuid.Parse(*keycloakUser.ID) - if err != nil { - return nil, err - } - - hashedPassword, err := helper.HashPassword(user.Password) - if err != nil { - return nil, err - } - - roles, err := u.userRepository.FetchRoles() - if err != nil { - return nil, err - } - for _, role := range *roles { - if role.Name == user.Role.Name { - user.Role.ID = role.ID - } - } - roleUuid := user.Role.ID - if err != nil { - return nil, err - } - resUser, err := u.userRepository.CreateWithUuid(userUuid, user.AccountId, user.Name, hashedPassword, user.Email, - user.Department, user.Description, user.Organization.ID, roleUuid) - if err != nil { - return nil, err - } - err = u.userRepository.UpdatePassword(userUuid, user.Organization.ID, hashedPassword, true) + // Create Admin user in keycloak & DB + resUser, err := u.Create(context.Background(), &user) if err != nil { return nil, err } + // Send mail of temporary password organizationInfo, err := u.organizationRepository.Get(orgainzationId) if err != nil { return nil, err } - message, err := mail.MakeGeneratingOrganizationMessage(orgainzationId, organizationInfo.Name, user.Email, user.AccountId, randomPassword) if err != nil { return nil, httpErrors.NewInternalServerError(err, "", "") } - mailer := mail.New(message) - if err := mailer.SendMail(); err != nil { return nil, httpErrors.NewInternalServerError(err, "", "") } - return &resUser, nil + return resUser, nil } func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId string, originPassword string, newPassword string, @@ -308,18 +255,13 @@ func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId s return errors.Wrap(err, "updating user in keycloak failed") } - // update password in DB - + // update password UpdateAt in DB user, err := u.userRepository.Get(accountId, organizationId) if err != nil { return errors.Wrap(err, "getting user from repository failed") } - hashedPassword, err := helper.HashPassword(newPassword) - if err != nil { - return errors.Wrap(err, "hashing password failed") - } - err = u.userRepository.UpdatePassword(user.ID, organizationId, hashedPassword, false) + err = u.userRepository.UpdatePasswordAt(user.ID, organizationId, false) if err != nil { return errors.Wrap(err, "updating user in repository failed") } @@ -402,6 +344,7 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u if err != nil { return nil, err } + if (originUser.Email == nil || *originUser.Email != user.Email) || (originUser.FirstName == nil || *originUser.FirstName != user.Name) { originUser.Email = gocloak.StringP(user.Email) originUser.FirstName = gocloak.StringP(user.Name) @@ -425,14 +368,12 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u return nil, fmt.Errorf("multiple users found") } - originPassword := (*users)[0].Password - roleUuid := (*users)[0].Role.ID if err != nil { return nil, err } - *user, err = u.userRepository.UpdateWithUuid((*users)[0].ID, user.AccountId, user.Name, originPassword, roleUuid, user.Email, + *user, err = u.userRepository.UpdateWithUuid((*users)[0].ID, user.AccountId, user.Name, roleUuid, user.Email, user.Department, user.Description) if err != nil { return nil, errors.Wrap(err, "updating user in repository failed") @@ -483,7 +424,7 @@ func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string, o func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.User, error) { // Create user in keycloak groups := []string{fmt.Sprintf("%s@%s", user.Role.Name, user.Organization.ID)} - err := u.kc.CreateUser(user.Organization.ID, &gocloak.User{ + userUuidStr, err := u.kc.CreateUser(user.Organization.ID, &gocloak.User{ Username: gocloak.StringP(user.AccountId), Credentials: &[]gocloak.CredentialRepresentation{ { @@ -496,43 +437,36 @@ func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.Us Groups: &groups, FirstName: gocloak.StringP(user.Name), }) - if err != nil { - if _, err := u.kc.GetUser(user.Organization.ID, user.AccountId); err == nil { - return nil, httpErrors.NewConflictError(errors.New("user already exists"), "", "") - } - - return nil, errors.Wrap(err, "creating user in keycloak failed") - } - keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId) - if err != nil { - return nil, errors.Wrap(err, "getting user from keycloak failed") - } - - userUuid, err := uuid.Parse(*keycloakUser.ID) if err != nil { return nil, err } - hashedPassword, err := helper.HashPassword(user.Password) + // Get user role + var roleUuid string + tksRoles, err := u.roleRepository.ListTksRoles(user.Organization.ID, nil) if err != nil { return nil, err } - - roles, err := u.userRepository.FetchRoles() - if err != nil { - return nil, err + if len(tksRoles) == 0 { + return nil, httpErrors.NewInternalServerError(fmt.Errorf("role not found"), "", "") } - for _, role := range *roles { - if role.Name == user.Role.Name { - user.Role.ID = role.ID + for _, role := range tksRoles { + if role.Role.Name == user.Role.Name { + roleUuid = role.RoleID } } - roleUuid := user.Role.ID + if roleUuid == "" { + return nil, httpErrors.NewInternalServerError(fmt.Errorf("role not found"), "", "") + } + + // Generate user uuid + userUuid, err := uuid.Parse(userUuidStr) if err != nil { return nil, err } - resUser, err := u.userRepository.CreateWithUuid(userUuid, user.AccountId, user.Name, hashedPassword, user.Email, + // Create user in DB + resUser, err := u.userRepository.CreateWithUuid(userUuid, user.AccountId, user.Name, user.Email, user.Department, user.Description, user.Organization.ID, roleUuid) if err != nil { return nil, err @@ -541,68 +475,49 @@ func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.Us return &resUser, nil } -func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId string, user *domain.User) (*domain.User, error) { - userInfo, ok := request.UserFrom(ctx) - if !ok { - return nil, fmt.Errorf("user in the context is empty") - } - - originUser, err := u.kc.GetUser(userInfo.GetOrganizationId(), accountId) +func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId string, newUser *domain.User) (*domain.User, error) { + user, err := u.UpdateByAccountId(ctx, accountId, newUser) if err != nil { return nil, err } - if (originUser.Email == nil || *originUser.Email != user.Email) || (originUser.FirstName == nil || *originUser.FirstName != user.Name) { - originUser.Email = gocloak.StringP(user.Email) - originUser.FirstName = gocloak.StringP(user.Name) - err = u.kc.UpdateUser(userInfo.GetOrganizationId(), originUser) - if err != nil { - return nil, err - } - } - users, err := u.userRepository.List(u.userRepository.OrganizationFilter(userInfo.GetOrganizationId()), - u.userRepository.AccountIdFilter(accountId)) - if err != nil { - if _, code := httpErrors.ErrorResponse(err); code == http.StatusNotFound { - return nil, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") - } - return nil, errors.Wrap(err, "getting users from repository failed") - } - if len(*users) == 0 { - return nil, fmt.Errorf("user not found") - } else if len(*users) > 1 { - return nil, fmt.Errorf("multiple users found") + userInfo, ok := request.UserFrom(ctx) + if !ok { + return nil, fmt.Errorf("user in the context is empty") } - if user.Role.Name != (*users)[0].Role.Name { - originGroupName := fmt.Sprintf("%s@%s", (*users)[0].Role.Name, userInfo.GetOrganizationId()) + + if newUser.Role.Name != user.Role.Name { + originGroupName := fmt.Sprintf("%s@%s", user.Role.Name, userInfo.GetOrganizationId()) newGroupName := fmt.Sprintf("%s@%s", user.Role.Name, userInfo.GetOrganizationId()) - if err := u.kc.LeaveGroup(userInfo.GetOrganizationId(), (*users)[0].ID.String(), originGroupName); err != nil { + if err := u.kc.LeaveGroup(userInfo.GetOrganizationId(), user.ID.String(), originGroupName); err != nil { log.ErrorfWithContext(ctx, "leave group in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err, "", "") } - if err := u.kc.JoinGroup(userInfo.GetOrganizationId(), (*users)[0].ID.String(), newGroupName); err != nil { + if err := u.kc.JoinGroup(userInfo.GetOrganizationId(), user.ID.String(), newGroupName); err != nil { log.ErrorfWithContext(ctx, "join group in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err, "", "") } } - originPassword := (*users)[0].Password - - roles, err := u.userRepository.FetchRoles() + // Get user role + var roleUuid string + tksRoles, err := u.roleRepository.ListTksRoles(user.Organization.ID, nil) if err != nil { return nil, err } - for _, role := range *roles { - if role.Name == user.Role.Name { - user.Role.ID = role.ID + if len(tksRoles) == 0 { + return nil, httpErrors.NewInternalServerError(fmt.Errorf("role not found"), "", "") + } + for _, role := range tksRoles { + if role.Role.Name == user.Role.Name { + roleUuid = role.RoleID } } - roleUuid := user.Role.ID - if err != nil { - return nil, err + if roleUuid == "" { + return nil, httpErrors.NewInternalServerError(fmt.Errorf("role not found"), "", "") } - *user, err = u.userRepository.UpdateWithUuid((*users)[0].ID, user.AccountId, user.Name, originPassword, roleUuid, user.Email, + *user, err = u.userRepository.UpdateWithUuid(user.ID, user.AccountId, user.Name, roleUuid, user.Email, user.Department, user.Description) if err != nil { return nil, errors.Wrap(err, "updating user in repository failed") @@ -614,6 +529,7 @@ func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId st func NewUserUsecase(r repository.Repository, kc keycloak.IKeycloak) IUserUsecase { return &UserUsecase{ userRepository: r.User, + roleRepository: r.Role, kc: kc, organizationRepository: r.Organization, } diff --git a/pkg/domain/user.go b/pkg/domain/user.go index 77c9092b..d63b4605 100644 --- a/pkg/domain/user.go +++ b/pkg/domain/user.go @@ -9,7 +9,7 @@ import ( type User struct { ID uuid.UUID `gorm:"primarykey;type:uuid" json:"id"` AccountId string `json:"accountId"` - Password string `json:"password"` + Password string `gorm:"-:all" json:"password"` Name string `json:"name"` Token string `json:"token"` RoleId string From bef7a4c67cd63244f1587dfc397a9f3185229a6a Mon Sep 17 00:00:00 2001 From: donggyu Date: Thu, 15 Feb 2024 18:41:26 +0900 Subject: [PATCH 07/15] ignore rbac due to refactoring --- internal/middleware/auth/authorizer/authorizer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/middleware/auth/authorizer/authorizer.go b/internal/middleware/auth/authorizer/authorizer.go index 9983dbd5..ff4af222 100644 --- a/internal/middleware/auth/authorizer/authorizer.go +++ b/internal/middleware/auth/authorizer/authorizer.go @@ -19,8 +19,8 @@ func NewDefaultAuthorization(repo repository.Repository) *defaultAuthorization { repo: repo, } d.addFilters(PasswordFilter) - d.addFilters(RBACFilter) - d.addFilters(RBACFilterWithEndpoint) + //d.addFilters(RBACFilter) + //d.addFilters(RBACFilterWithEndpoint) return d } From 13304b4f1a1bf12e77e29130fed248106691955d Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 16 Feb 2024 16:10:46 +0900 Subject: [PATCH 08/15] remove tks role struct and project role in role domain --- internal/delivery/http/role.go | 300 +++------------------------------ internal/repository/role.go | 205 ++++------------------ internal/usecase/role.go | 81 ++------- pkg/domain/role.go | 5 - 4 files changed, 68 insertions(+), 523 deletions(-) diff --git a/internal/delivery/http/role.go b/internal/delivery/http/role.go index 605005e9..601440a9 100644 --- a/internal/delivery/http/role.go +++ b/internal/delivery/http/role.go @@ -13,19 +13,10 @@ import ( type IRoleHandler interface { CreateTksRole(w http.ResponseWriter, r *http.Request) - CreateProjectRole(w http.ResponseWriter, r *http.Request) - ListTksRoles(w http.ResponseWriter, r *http.Request) - ListProjectRoles(w http.ResponseWriter, r *http.Request) - GetTksRole(w http.ResponseWriter, r *http.Request) - GetProjectRole(w http.ResponseWriter, r *http.Request) - DeleteTksRole(w http.ResponseWriter, r *http.Request) - DeleteProjectRole(w http.ResponseWriter, r *http.Request) - UpdateTksRole(w http.ResponseWriter, r *http.Request) - UpdateProjectRole(w http.ResponseWriter, r *http.Request) } type RoleHandler struct { @@ -70,13 +61,11 @@ func (h RoleHandler) CreateTksRole(w http.ResponseWriter, r *http.Request) { } // input to dto - dto := domain.TksRole{ - Role: domain.Role{ - OrganizationID: organizationId, - Name: input.Name, - Description: input.Description, - Type: string(domain.RoleTypeTks), - }, + dto := domain.Role{ + OrganizationID: organizationId, + Name: input.Name, + Description: input.Description, + Type: string(domain.RoleTypeTks), } if err := h.roleUsecase.CreateTksRole(&dto); err != nil { @@ -86,60 +75,6 @@ func (h RoleHandler) CreateTksRole(w http.ResponseWriter, r *http.Request) { } -// CreateTksRole godoc -// @Tags Role -// @Summary Create Project Role -// @Description Create Project Role -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param body body domain.CreateProjectRoleRequest true "Create Project Role Request" -// @Success 200 {object} domain.CreateProjectRoleResponse -// @Router /organizations/{organizationId}/projects/{projectId}/roles [post] - -func (h RoleHandler) CreateProjectRole(w http.ResponseWriter, r *http.Request) { - // path parameter - var organizationId, projectId string - - vars := mux.Vars(r) - if v, ok := vars["organizationId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - organizationId = v - } - if v, ok := vars["projectId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - projectId = v - } - - // request body - input := domain.CreateProjectRoleRequest{} - err := UnmarshalRequestInput(r, &input) - if err != nil { - ErrorJSON(w, r, err) - return - } - - // input to dto - dto := domain.ProjectRole{ - Role: domain.Role{ - OrganizationID: organizationId, - Name: input.Name, - Description: input.Description, - Type: string(domain.RoleTypeProject), - }, - ProjectID: projectId, - } - - if err := h.roleUsecase.CreateProjectRole(&dto); err != nil { - ErrorJSON(w, r, err) - return - } -} - // ListTksRoles godoc // @Tags Role // @Summary List Tks Roles @@ -179,73 +114,13 @@ func (h RoleHandler) ListTksRoles(w http.ResponseWriter, r *http.Request) { out.Roles = make([]domain.GetTksRoleResponse, len(roles)) for i, role := range roles { out.Roles[i] = domain.GetTksRoleResponse{ - ID: role.Role.ID, - Name: role.Role.Name, - OrganizationID: role.Role.OrganizationID, - Description: role.Role.Description, - Creator: role.Role.Creator.String(), - CreatedAt: role.Role.CreatedAt, - UpdatedAt: role.Role.UpdatedAt, - } - } - - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) - } - - // response - ResponseJSON(w, r, http.StatusOK, out) -} - -// ListProjectRoles godoc -// @Tags Role -// @Summary List Project Roles -// @Description List Project Roles -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param projectId path string true "Project ID" -// @Success 200 {object} domain.ListProjectRoleResponse -// @Router /organizations/{organizationId}/projects/{projectId}/roles [get] - -func (h RoleHandler) ListProjectRoles(w http.ResponseWriter, r *http.Request) { - // path parameter - var projectId string - - vars := mux.Vars(r) - if v, ok := vars["projectId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - projectId = v - } - - // query parameter - urlParams := r.URL.Query() - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } - - // list roles - roles, err := h.roleUsecase.ListProjectRoles(projectId, pg) - if err != nil { - ErrorJSON(w, r, err) - return - } - - var out domain.ListProjectRoleResponse - out.Roles = make([]domain.GetProjectRoleResponse, len(roles)) - for i, role := range roles { - out.Roles[i] = domain.GetProjectRoleResponse{ - ID: role.RoleID, - Name: role.Role.Name, - OrganizationID: role.Role.OrganizationID, - ProjectID: role.ProjectID, - Description: role.Role.Description, - Creator: role.Role.Creator.String(), - CreatedAt: role.Role.CreatedAt, - UpdatedAt: role.Role.UpdatedAt, + ID: role.ID, + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, } } @@ -286,57 +161,13 @@ func (h RoleHandler) GetTksRole(w http.ResponseWriter, r *http.Request) { // response out := domain.GetTksRoleResponse{ - ID: role.Role.ID, - Name: role.Role.Name, - OrganizationID: role.Role.OrganizationID, - Description: role.Role.Description, - Creator: role.Role.Creator.String(), - CreatedAt: role.Role.CreatedAt, - UpdatedAt: role.Role.UpdatedAt, - } - - ResponseJSON(w, r, http.StatusOK, out) -} - -// GetProjectRole godoc -// @Tags Role -// @Summary Get Project Role -// @Description Get Project Role -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param projectId path string true "Project ID" -// @Param roleId path string true "Role ID" -// @Success 200 {object} domain.GetProjectRoleResponse -// @Router /organizations/{organizationId}/projects/{projectId}/roles/{roleId} [get] - -func (h RoleHandler) GetProjectRole(w http.ResponseWriter, r *http.Request) { - // path parameter - vars := mux.Vars(r) - var roleId string - if v, ok := vars["roleId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - roleId = v - } - - // get role - role, err := h.roleUsecase.GetProjectRole(roleId) - if err != nil { - ErrorJSON(w, r, err) - return - } - - // response - out := domain.GetProjectRoleResponse{ - ID: role.RoleID, - Name: role.Role.Name, - OrganizationID: role.Role.OrganizationID, - ProjectID: role.ProjectID, - Description: role.Role.Description, - Creator: role.Role.Creator.String(), - CreatedAt: role.Role.CreatedAt, - UpdatedAt: role.Role.UpdatedAt, + ID: role.ID, + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, } ResponseJSON(w, r, http.StatusOK, out) @@ -373,38 +204,6 @@ func (h RoleHandler) DeleteTksRole(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, nil) } -// DeleteProjectRole godoc -// @Tags Role -// @Summary Delete Project Role -// @Description Delete Project Role -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param projectId path string true "Project ID" -// @Param roleId path string true "Role ID" -// @Success 200 -// @Router /organizations/{organizationId}/projects/{projectId}/roles/{roleId} [delete] - -func (h RoleHandler) DeleteProjectRole(w http.ResponseWriter, r *http.Request) { - // path parameter - vars := mux.Vars(r) - var roleId string - if v, ok := vars["roleId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - roleId = v - } - - // delete role - if err := h.roleUsecase.DeleteProjectRole(roleId); err != nil { - ErrorJSON(w, r, err) - return - } - - // response - ResponseJSON(w, r, http.StatusOK, nil) -} - // UpdateTksRole godoc // @Tags Role // @Summary Update Tks Role @@ -437,12 +236,10 @@ func (h RoleHandler) UpdateTksRole(w http.ResponseWriter, r *http.Request) { } // input to dto - dto := domain.TksRole{ - Role: domain.Role{ - ID: roleId, - Name: input.Name, - Description: input.Description, - }, + dto := domain.Role{ + ID: roleId, + Name: input.Name, + Description: input.Description, } // update role @@ -454,54 +251,3 @@ func (h RoleHandler) UpdateTksRole(w http.ResponseWriter, r *http.Request) { // response ResponseJSON(w, r, http.StatusOK, nil) } - -// UpdateProjectRole godoc -// @Tags Role -// @Summary Update Project Role -// @Description Update Project Role -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param projectId path string true "Project ID" -// @Param roleId path string true "Role ID" -// @Param body body domain.UpdateProjectRoleRequest true "Update Project Role Request" -// @Success 200 -// @Router /organizations/{organizationId}/projects/{projectId}/roles/{roleId} [put] - -func (h RoleHandler) UpdateProjectRole(w http.ResponseWriter, r *http.Request) { - // path parameter - vars := mux.Vars(r) - var roleId string - if v, ok := vars["roleId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - roleId = v - } - - // request body - input := domain.UpdateProjectRoleRequest{} - err := UnmarshalRequestInput(r, &input) - if err != nil { - ErrorJSON(w, r, err) - return - } - - // input to dto - dto := domain.ProjectRole{ - Role: domain.Role{ - ID: roleId, - Name: input.Name, - Description: input.Description, - }, - } - - // update role - if err := h.roleUsecase.UpdateProjectRole(&dto); err != nil { - ErrorJSON(w, r, err) - return - } - - // response - ResponseJSON(w, r, http.StatusOK, nil) -} diff --git a/internal/repository/role.go b/internal/repository/role.go index 9f63ba2a..dedbb876 100644 --- a/internal/repository/role.go +++ b/internal/repository/role.go @@ -9,83 +9,36 @@ import ( "math" ) -// -//type Role struct { -// gorm.Model -// -// ID string `gorm:"primarykey;"` -// Name string -// OrganizationID string -// Organization Organization `gorm:"foreignKey:OrganizationID;references:ID;"` -// Type string -// Creator uuid.UUID -// Description string -//} -// -//func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { -// r.ID = uuid.New().String() -// return nil -//} -// -//type TksRole struct { -// RoleID string `gorm:"primarykey;"` -// Role Role `gorm:"foreignKey:RoleID;references:ID;"` -//} - -//type ProjectRole struct { -// RoleID string `gorm:"primarykey;"` -// Role Role `gorm:"foreignKey:RoleID;references:ID;"` -// ProjectID string -// Project domain.Project `gorm:"foreignKey:ProjectID;references:ID;"` -//} - type IRoleRepository interface { - Create(roleObj interface{}) error + Create(roleObj *domain.Role) error List(pg *pagination.Pagination) ([]*domain.Role, error) - ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) - ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) - Get(id uuid.UUID) (*domain.Role, error) - GetTksRole(id uuid.UUID) (*domain.TksRole, error) - GetTksRoleByRoleName(roleName string) (*domain.TksRole, error) - GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error) - DeleteCascade(id uuid.UUID) error - Update(roleObj interface{}) error + ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.Role, error) + Get(id string) (*domain.Role, error) + GetTksRole(id string) (*domain.Role, error) + GetTksRoleByRoleName(roleName string) (*domain.Role, error) + Delete(id string) error + Update(roleObj *domain.Role) error } type RoleRepository struct { db *gorm.DB } -func (r RoleRepository) GetTksRoleByRoleName(roleName string) (*domain.TksRole, error) { - var role domain.TksRole +func (r RoleRepository) GetTksRoleByRoleName(roleName string) (*domain.Role, error) { + var role domain.Role if err := r.db.Preload("Role").First(&role, "Role.name = ?", roleName).Error; err != nil { return nil, err } - return ConvertRepoToDomainTksRole(&role), nil + return &role, nil } -func (r RoleRepository) Create(roleObj interface{}) error { +func (r RoleRepository) Create(roleObj *domain.Role) error { if roleObj == nil { return fmt.Errorf("roleObj is nil") } - switch roleObj.(type) { - case *domain.TksRole: - inputRole := roleObj.(*domain.TksRole) - role := ConvertDomainToRepoTksRole(inputRole) - if err := r.db.Create(role).Error; err != nil { - return err - } - - case *domain.ProjectRole: - inputRole := roleObj.(*domain.ProjectRole) - //role := ConvertDomainToRepoProjectRole(inputRole) - //if err := r.db.Create(role).Error; err != nil { - // return err - //} - if err := r.db.Create(inputRole).Error; err != nil { - return err - } + if err := r.db.Create(roleObj).Error; err != nil { + return err } return nil @@ -93,7 +46,6 @@ func (r RoleRepository) Create(roleObj interface{}) error { func (r RoleRepository) List(pg *pagination.Pagination) ([]*domain.Role, error) { var roles []*domain.Role - var objs []*domain.Role if pg == nil { pg = pagination.NewDefaultPagination() @@ -105,29 +57,23 @@ func (r RoleRepository) List(pg *pagination.Pagination) ([]*domain.Role, error) pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - //res := db.Joins("JOIN roles as r on r.id = tks_roles.role_id"). - // Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&roles) if res.Error != nil { return nil, res.Error } - for _, role := range objs { - roles = append(roles, ConvertRepoToDomainRole(role)) - } return roles, nil } -func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) { - var roles []*domain.TksRole - var objs []*domain.TksRole +func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.Role, error) { + var roles []*domain.Role if pg == nil { pg = pagination.NewDefaultPagination() } filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&domain.TksRole{})) + db := filterFunc(r.db.Model(&domain.Role{})) db.Count(&pg.TotalRows) pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) @@ -138,14 +84,11 @@ func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagin Offset(pg.GetOffset()). Limit(pg.GetLimit()). Order(orderQuery). - Find(&objs) + Find(&roles) //res := db.Preload("Role").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) if res.Error != nil { return nil, res.Error } - for _, role := range objs { - roles = append(roles, ConvertRepoToDomainTksRole(role)) - } return roles, nil } @@ -178,22 +121,22 @@ func (r RoleRepository) ListProjectRoles(projectId string, pg *pagination.Pagina return roles, nil } -func (r RoleRepository) Get(id uuid.UUID) (*domain.Role, error) { +func (r RoleRepository) Get(id string) (*domain.Role, error) { var role domain.Role if err := r.db.First(&role, "id = ?", id).Error; err != nil { return nil, err } - return ConvertRepoToDomainRole(&role), nil + return &role, nil } -func (r RoleRepository) GetTksRole(id uuid.UUID) (*domain.TksRole, error) { - var role domain.TksRole +func (r RoleRepository) GetTksRole(id string) (*domain.Role, error) { + var role domain.Role if err := r.db.Preload("Role").First(&role, "role_id = ?", id).Error; err != nil { return nil, err } - return ConvertRepoToDomainTksRole(&role), nil + return &role, nil } func (r RoleRepository) GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error) { @@ -205,44 +148,26 @@ func (r RoleRepository) GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error return &role, nil } -func (r RoleRepository) DeleteCascade(id uuid.UUID) error { - // manual cascade delete - if err := r.db.Delete(&domain.TksRole{}, "role_id = ?", id).Error; err != nil { - return err - } - if err := r.db.Delete(&domain.ProjectRole{}, "role_id = ?", id).Error; err != nil { - return err +func (r RoleRepository) Update(roleObj *domain.Role) error { + if roleObj == nil { + return fmt.Errorf("roleObj is nil") } - if err := r.db.Delete(&domain.Role{}, "id = ?", id).Error; err != nil { + err := r.db.Model(&domain.Role{}).Where("id = ?", roleObj.ID).Updates(domain.Role{ + Name: roleObj.Name, + Description: roleObj.Description, + }).Error + + if err != nil { return err } return nil } -func (r RoleRepository) Update(roleObj interface{}) error { - switch roleObj.(type) { - case *domain.TksRole: - inputRole := roleObj.(*domain.TksRole) - role := ConvertRepoToDomainTksRole(inputRole) - if err := r.db.Model(&domain.TksRole{}).Where("id = ?", role.RoleID).Updates(domain.Role{ - Name: role.Role.Name, - Description: role.Role.Description, - }).Error; err != nil { - return err - } - - case *domain.ProjectRole: - inputRole := roleObj.(*domain.ProjectRole) - //projectRole := ConvertRepoToDomainProjectRole(inputRole) - // update role - if err := r.db.Model(&domain.ProjectRole{}).Where("role_id = ?", inputRole.RoleID).Updates(domain.Role{ - Name: inputRole.Role.Name, - Description: inputRole.Role.Description, - }).Error; err != nil { - return err - } +func (r RoleRepository) Delete(id string) error { + if err := r.db.Delete(&domain.Role{}, "id = ?", id).Error; err != nil { + return err } return nil @@ -253,63 +178,3 @@ func NewRoleRepository(db *gorm.DB) IRoleRepository { db: db, } } - -// domain.Role to repository.Role -func ConverDomainToRepoRole(domainRole *domain.Role) *domain.Role { - return &domain.Role{ - ID: domainRole.ID, - Name: domainRole.Name, - OrganizationID: domainRole.OrganizationID, - Type: domainRole.Type, - Creator: domainRole.Creator, - Description: domainRole.Description, - } -} - -// repository.Role to domain.Role -func ConvertRepoToDomainRole(repoRole *domain.Role) *domain.Role { - return &domain.Role{ - ID: repoRole.ID, - Name: repoRole.Name, - OrganizationID: repoRole.OrganizationID, - Type: repoRole.Type, - Creator: repoRole.Creator, - Description: repoRole.Description, - } -} - -// domain.TksRole to repository.TksRole -func ConvertDomainToRepoTksRole(domainRole *domain.TksRole) *domain.TksRole { - return &domain.TksRole{ - RoleID: domainRole.Role.ID, - Role: *ConverDomainToRepoRole(&domainRole.Role), - } -} - -// repository.TksRole to domain.TksRole -func ConvertRepoToDomainTksRole(repoRole *domain.TksRole) *domain.TksRole { - return &domain.TksRole{ - RoleID: repoRole.RoleID, - Role: *ConvertRepoToDomainRole(&repoRole.Role), - } -} - -//// domain.ProjectRole to repository.ProjectRole -//func ConvertDomainToRepoProjectRole(domainRole *domain.ProjectRole) *ProjectRole { -// return &ProjectRole{ -// RoleID: domainRole.RoleID, -// ProjectID: domainRole.ProjectID, -// Role: *ConverDomainToRepoRole(&domainRole.Role), -// Project: domainRole.Project, -// } -//} -// -//// repository.ProjectRole to domain.ProjectRole -//func ConvertRepoToDomainProjectRole(repoRole *ProjectRole) *domain.ProjectRole { -// return &domain.ProjectRole{ -// RoleID: repoRole.RoleID, -// ProjectID: repoRole.ProjectID, -// Role: *ConvertRepoToDomainRole(&repoRole.Role), -// Project: repoRole.Project, -// } -//} diff --git a/internal/usecase/role.go b/internal/usecase/role.go index fb6dbc56..31d28b0e 100644 --- a/internal/usecase/role.go +++ b/internal/usecase/role.go @@ -1,24 +1,18 @@ package usecase import ( - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/pkg/domain" ) type IRoleUsecase interface { - CreateTksRole(role *domain.TksRole) error - CreateProjectRole(role *domain.ProjectRole) error + CreateTksRole(role *domain.Role) error ListRoles(pg *pagination.Pagination) ([]*domain.Role, error) - ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) - ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) - GetTksRole(id string) (*domain.TksRole, error) - GetProjectRole(id string) (*domain.ProjectRole, error) + ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.Role, error) + GetTksRole(id string) (*domain.Role, error) DeleteTksRole(id string) error - DeleteProjectRole(id string) error - UpdateTksRole(role *domain.TksRole) error - UpdateProjectRole(role *domain.ProjectRole) error + UpdateTksRole(role *domain.Role) error } type RoleUsecase struct { @@ -31,15 +25,11 @@ func NewRoleUsecase(repo repository.Repository) *RoleUsecase { } } -func (r RoleUsecase) CreateTksRole(role *domain.TksRole) error { +func (r RoleUsecase) CreateTksRole(role *domain.Role) error { return r.repo.Create(role) } -func (r RoleUsecase) CreateProjectRole(role *domain.ProjectRole) error { - return r.repo.Create(role) -} - -func (r RoleUsecase) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.TksRole, error) { +func (r RoleUsecase) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.Role, error) { roles, err := r.repo.ListTksRoles(organizationId, pg) if err != nil { return nil, err @@ -48,40 +38,12 @@ func (r RoleUsecase) ListTksRoles(organizationId string, pg *pagination.Paginati return roles, nil } -func (r RoleUsecase) ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) { - roles, err := r.repo.ListProjectRoles(projectId, pg) - if err != nil { - return nil, err - } - - return roles, nil -} - func (r RoleUsecase) ListRoles(pg *pagination.Pagination) ([]*domain.Role, error) { return r.repo.List(nil) } -func (r RoleUsecase) GetTksRole(id string) (*domain.TksRole, error) { - roldId, err := uuid.Parse(id) - if err != nil { - return nil, err - } - - role, err := r.repo.GetTksRole(roldId) - if err != nil { - return nil, err - } - - return role, nil -} - -func (r RoleUsecase) GetProjectRole(id string) (*domain.ProjectRole, error) { - roleId, err := uuid.Parse(id) - if err != nil { - return nil, err - } - - role, err := r.repo.GetProjectRole(roleId) +func (r RoleUsecase) GetTksRole(id string) (*domain.Role, error) { + role, err := r.repo.GetTksRole(id) if err != nil { return nil, err } @@ -90,33 +52,10 @@ func (r RoleUsecase) GetProjectRole(id string) (*domain.ProjectRole, error) { } func (r RoleUsecase) DeleteTksRole(id string) error { - roleId, err := uuid.Parse(id) - if err != nil { - return err - } - - return r.repo.DeleteCascade(roleId) -} - -func (r RoleUsecase) DeleteProjectRole(id string) error { - roleId, err := uuid.Parse(id) - if err != nil { - return err - } - - return r.repo.DeleteCascade(roleId) -} - -func (r RoleUsecase) UpdateTksRole(role *domain.TksRole) error { - err := r.repo.Update(role) - if err != nil { - return err - } - - return nil + return r.repo.Delete(id) } -func (r RoleUsecase) UpdateProjectRole(role *domain.ProjectRole) error { +func (r RoleUsecase) UpdateTksRole(role *domain.Role) error { err := r.repo.Update(role) if err != nil { return err diff --git a/pkg/domain/role.go b/pkg/domain/role.go index a733e9a5..ae1d5cc3 100644 --- a/pkg/domain/role.go +++ b/pkg/domain/role.go @@ -33,11 +33,6 @@ type Role struct { UpdatedAt time.Time `json:"updatedAt"` } -type TksRole struct { - RoleID string `gorm:"primarykey;" json:"roleId"` - Role Role `gorm:"foreignKey:RoleID;references:ID;"` -} - type ProjectRole struct { RoleID string `gorm:"primaryKey" json:"roleId"` Role Role `gorm:"foreignKey:RoleID;references:ID;" json:"role"` From faf21f4bd041e441353dee009c82442d20c362ef Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 16 Feb 2024 16:16:32 +0900 Subject: [PATCH 09/15] remove tks role struct and project role in role domain --- internal/repository/role.go | 38 ---------------------------- pkg/domain/project.go | 9 +++++++ pkg/domain/role.go | 49 ------------------------------------- 3 files changed, 9 insertions(+), 87 deletions(-) diff --git a/internal/repository/role.go b/internal/repository/role.go index dedbb876..67607666 100644 --- a/internal/repository/role.go +++ b/internal/repository/role.go @@ -2,7 +2,6 @@ package repository import ( "fmt" - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/pkg/domain" "gorm.io/gorm" @@ -93,34 +92,6 @@ func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagin return roles, nil } -func (r RoleRepository) ListProjectRoles(projectId string, pg *pagination.Pagination) ([]*domain.ProjectRole, error) { - var roles []*domain.ProjectRole - var objs []*domain.ProjectRole - - if pg == nil { - pg = pagination.NewDefaultPagination() - } - filterFunc := CombinedGormFilter("roles", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&domain.ProjectRole{})) - - db.Count(&pg.TotalRows) - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Joins("JOIN roles as r on r.id = project_roles.role_id"). - Where("project_roles.project_id = ?", projectId). - Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) - //res := db.Preload("Role").Preload("Project").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) - if res.Error != nil { - return nil, res.Error - } - for _, role := range objs { - roles = append(roles, role) - } - - return roles, nil -} - func (r RoleRepository) Get(id string) (*domain.Role, error) { var role domain.Role if err := r.db.First(&role, "id = ?", id).Error; err != nil { @@ -139,15 +110,6 @@ func (r RoleRepository) GetTksRole(id string) (*domain.Role, error) { return &role, nil } -func (r RoleRepository) GetProjectRole(id uuid.UUID) (*domain.ProjectRole, error) { - var role domain.ProjectRole - if err := r.db.Preload("Role").Preload("Project").First(&role, "role_id = ?", id).Error; err != nil { - return nil, err - } - - return &role, nil -} - func (r RoleRepository) Update(roleObj *domain.Role) error { if roleObj == nil { return fmt.Errorf("roleObj is nil") diff --git a/pkg/domain/project.go b/pkg/domain/project.go index c55d0a3d..386d68ed 100644 --- a/pkg/domain/project.go +++ b/pkg/domain/project.go @@ -78,6 +78,15 @@ type GetProjectResponse struct { Project *ProjectDetailResponse `json:"project"` } +type ProjectRole struct { + ID string `gorm:"primarykey" json:"id"` + Name string `json:"name"` // project-leader, project-member, project-viewer + Description string `json:"description,omitempty"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt" ` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + type ProjectUser struct { ID uuid.UUID `gorm:"primarykey;type:uuid" json:"id"` AccountId string `json:"accountId"` diff --git a/pkg/domain/role.go b/pkg/domain/role.go index ae1d5cc3..204baa5d 100644 --- a/pkg/domain/role.go +++ b/pkg/domain/role.go @@ -33,25 +33,6 @@ type Role struct { UpdatedAt time.Time `json:"updatedAt"` } -type ProjectRole struct { - RoleID string `gorm:"primaryKey" json:"roleId"` - Role Role `gorm:"foreignKey:RoleID;references:ID;" json:"role"` - ProjectID string `json:"projectID"` - Project Project `gorm:"foreignKey:ProjectID;references:ID;" json:"project"` -} - -//type Role = struct { -// ID uuid.UUID `json:"id"` -// Name string `json:"name"` -// OrganizationID string `json:"organizationId"` -// Organization Organization `json:"organization"` -// Type string `json:"type"` -// Description string `json:"description"` -// Creator uuid.UUID `json:"creator"` -// CreatedAt time.Time `json:"createdAt"` -// UpdatedAt time.Time `json:"updatedAt"` -//} - type CreateTksRoleRequest struct { Name string `json:"name" validate:"required"` Description string `json:"description" validate:"omitempty,min=0,max=100"` @@ -80,33 +61,3 @@ type UpdateTksRoleRequest struct { Name string `json:"name" validate:"required"` Description string `json:"description" validate:"omitempty,min=0,max=100"` } - -type CreateProjectRoleRequest struct { - Name string `json:"name" validate:"required"` - Description string `json:"description" validate:"omitempty,min=0,max=100"` -} - -type CreateProjectRoleResponse struct { - ID string `json:"id"` -} - -type GetProjectRoleResponse struct { - ID string `json:"id"` - Name string `json:"name"` - OrganizationID string `json:"organizationId"` - ProjectID string `json:"projectId"` - Description string `json:"description"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type ListProjectRoleResponse struct { - Roles []GetProjectRoleResponse `json:"roles"` - Pagination PaginationResponse `json:"pagination"` -} - -type UpdateProjectRoleRequest struct { - Name string `json:"name" validate:"required"` - Description string `json:"description" validate:"omitempty,min=0,max=100"` -} From d02514f752140a1f5ab499b57618e313da9038ab Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 16 Feb 2024 16:23:04 +0900 Subject: [PATCH 10/15] rollback to original project --- internal/repository/project.go | 50 +++++++++++++++++++++++++++++++++- pkg/domain/project.go | 14 ++++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/internal/repository/project.go b/internal/repository/project.go index 67f57e95..c042cdc2 100644 --- a/internal/repository/project.go +++ b/internal/repository/project.go @@ -20,6 +20,9 @@ type IProjectRepository interface { GetProjectByIdAndLeader(organizationId string, projectId string) (*domain.Project, error) GetProjectByName(organizationId string, projectName string) (*domain.Project, error) UpdateProject(p *domain.Project) error + GetAllProjectRoles() ([]domain.ProjectRole, error) + GetProjectRoleByName(name string) (*domain.ProjectRole, error) + GetProjectRoleById(id string) (*domain.ProjectRole, error) AddProjectMember(*domain.ProjectMember) (string, error) GetProjectMembersByProjectId(projectId string, pg *pagination.Pagination) ([]domain.ProjectMember, error) GetProjectMembersByProjectIdAndRoleName(projectId string, memberRole string, pg *pagination.Pagination) ([]domain.ProjectMember, error) @@ -203,7 +206,6 @@ func (r *ProjectRepository) GetProjectByIdAndLeader(organizationId string, proje res := r.db.Limit(1). Preload("ProjectMembers", "is_project_leader = ?", true). Preload("ProjectMembers.ProjectRole"). - Preload("ProjectMembers.ProjectRole.Role"). Preload("ProjectMembers.ProjectUser"). First(&p, "organization_id = ? and id = ?", organizationId, projectId) @@ -246,6 +248,52 @@ func (r *ProjectRepository) UpdateProject(p *domain.Project) error { return nil } +func (r *ProjectRepository) GetProjectRoleById(id string) (*domain.ProjectRole, error) { + var pr = &domain.ProjectRole{ID: id} + res := r.db.First(pr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Cannot find project role") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } + } + + return pr, nil +} + +func (r *ProjectRepository) GetAllProjectRoles() (prs []domain.ProjectRole, err error) { + res := r.db.Find(&prs) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Cannot find project roles") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } + } + + return prs, nil +} + +func (r *ProjectRepository) GetProjectRoleByName(name string) (pr *domain.ProjectRole, err error) { + res := r.db.Where("name = ?", name).First(&pr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Cannot find project roles") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } + } + + return pr, nil +} + func (r *ProjectRepository) AddProjectMember(pm *domain.ProjectMember) (string, error) { res := r.db.Create(&pm) if res.Error != nil { diff --git a/pkg/domain/project.go b/pkg/domain/project.go index 386d68ed..23882b28 100644 --- a/pkg/domain/project.go +++ b/pkg/domain/project.go @@ -1,9 +1,10 @@ package domain import ( + "time" + "github.com/google/uuid" "gorm.io/gorm" - "time" ) func (a *Project) BeforeCreate(*gorm.DB) (err error) { @@ -11,6 +12,11 @@ func (a *Project) BeforeCreate(*gorm.DB) (err error) { return nil } +func (t *ProjectRole) BeforeCreate(*gorm.DB) (err error) { + t.ID = uuid.New().String() + return nil +} + func (t *ProjectMember) BeforeCreate(*gorm.DB) (err error) { t.ID = uuid.New().String() return nil @@ -108,7 +114,7 @@ type ProjectMember struct { ProjectUserId uuid.UUID `json:"projectUserId"` ProjectUser *ProjectUser `gorm:"foreignKey:ProjectUserId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectUser"` ProjectRoleId string `json:"projectRoleId"` - ProjectRole *ProjectRole `gorm:"foreignKey:ProjectRoleId;references:RoleID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectRole"` + ProjectRole *ProjectRole `gorm:"foreignKey:ProjectRoleId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectRole"` IsProjectLeader bool `gorm:"default:false" json:"projectLeader"` CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` @@ -151,6 +157,10 @@ type UpdateProjectRequest struct { CreateProjectRequest } +type GetProjectRoleResponse struct { + ProjectRole ProjectRole `json:"projectRole"` +} + type GetProjectRolesResponse struct { ProjectRoles []ProjectRole `json:"projectRoles"` } From 0a1ca86408837426d968ca97b75e499336aea738 Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 16 Feb 2024 16:28:09 +0900 Subject: [PATCH 11/15] rollback to original project --- internal/delivery/api/endpoint.go | 54 ++++++++----------------------- internal/route/route.go | 5 --- 2 files changed, 14 insertions(+), 45 deletions(-) diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 2a20f6ed..e15c06a2 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -130,6 +130,8 @@ const ( // Project CreateProject + GetProjectRoles + GetProjectRole GetProjects GetProject UpdateProject @@ -545,6 +547,14 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "CreateProject", Group: "Project", }, + GetProjectRoles: { + Name: "GetProjectRoles", + Group: "Project", + }, + GetProjectRole: { + Name: "GetProjectRole", + Group: "Project", + }, GetProjects: { Name: "GetProjects", Group: "Project", @@ -641,42 +651,22 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "CreateTksRole", Group: "Role", }, - CreateProjectRole: { - Name: "CreateProjectRole", - Group: "Role", - }, ListTksRoles: { Name: "ListTksRoles", Group: "Role", }, - ListProjectRoles: { - Name: "ListProjectRoles", - Group: "Role", - }, GetTksRole: { Name: "GetTksRole", Group: "Role", }, - GetProjectRole: { - Name: "GetProjectRole", - Group: "Role", - }, DeleteTksRole: { Name: "DeleteTksRole", Group: "Role", }, - DeleteProjectRole: { - Name: "DeleteProjectRole", - Group: "Role", - }, UpdateTksRole: { Name: "UpdateTksRole", Group: "Role", }, - UpdateProjectRole: { - Name: "UpdateProjectRole", - Group: "Role", - }, } func (e Endpoint) String() string { @@ -871,6 +861,10 @@ func (e Endpoint) String() string { return "InstallStack" case CreateProject: return "CreateProject" + case GetProjectRoles: + return "GetProjectRoles" + case GetProjectRole: + return "GetProjectRole" case GetProjects: return "GetProjects" case GetProject: @@ -919,24 +913,14 @@ func (e Endpoint) String() string { return "DeleteAudit" case CreateTksRole: return "CreateTksRole" - case CreateProjectRole: - return "CreateProjectRole" case ListTksRoles: return "ListTksRoles" - case ListProjectRoles: - return "ListProjectRoles" case GetTksRole: return "GetTksRole" - case GetProjectRole: - return "GetProjectRole" case DeleteTksRole: return "DeleteTksRole" - case DeleteProjectRole: - return "DeleteProjectRole" case UpdateTksRole: return "UpdateTksRole" - case UpdateProjectRole: - return "UpdateProjectRole" default: return "" } @@ -1185,24 +1169,14 @@ func GetEndpoint(name string) Endpoint { return DeleteAudit case "CreateTksRole": return CreateTksRole - case "CreateProjectRole": - return CreateProjectRole case "ListTksRoles": return ListTksRoles - case "ListProjectRoles": - return ListProjectRoles case "GetTksRole": return GetTksRole - case "GetProjectRole": - return GetProjectRole case "DeleteTksRole": return DeleteTksRole - case "DeleteProjectRole": - return DeleteProjectRole case "UpdateTksRole": return UpdateTksRole - case "UpdateProjectRole": - return UpdateProjectRole default: return -1 } diff --git a/internal/route/route.go b/internal/route/route.go index ede3ff36..432aede0 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -238,11 +238,6 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.GetTksRole, http.HandlerFunc(roleHandler.GetTksRole))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.DeleteTksRole, http.HandlerFunc(roleHandler.DeleteTksRole))).Methods(http.MethodDelete) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.UpdateTksRole, http.HandlerFunc(roleHandler.UpdateTksRole))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles", customMiddleware.Handle(internalApi.CreateProjectRole, http.HandlerFunc(roleHandler.CreateProjectRole))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles", customMiddleware.Handle(internalApi.ListProjectRoles, http.HandlerFunc(roleHandler.ListProjectRoles))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles/{roleId}", customMiddleware.Handle(internalApi.GetProjectRole, http.HandlerFunc(roleHandler.GetProjectRole))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles/{roleId}", customMiddleware.Handle(internalApi.DeleteProjectRole, http.HandlerFunc(roleHandler.DeleteProjectRole))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/roles/{roleId}", customMiddleware.Handle(internalApi.UpdateProjectRole, http.HandlerFunc(roleHandler.UpdateProjectRole))).Methods(http.MethodPut) r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", alertHandler.CreateAlert).Methods(http.MethodPost) // assets From 26a0981de349e9b3c759f4d0583b340da9ec0911 Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 16 Feb 2024 17:49:46 +0900 Subject: [PATCH 12/15] Automatically import endpoints on initializing server --- cmd/server/main.go | 3 ++ internal/database/database.go | 59 +++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index a194abf8..b33b705e 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -117,6 +117,9 @@ func main() { log.Fatal("cannot connect gormDB") } + // Ensure default rows in database + err = database.EnsureDefaultRows(db) + // Initialize external client var argoClient argowf.ArgoClient if viper.GetString("argo-address") == "" || viper.GetInt("argo-port") == 0 { diff --git a/internal/database/database.go b/internal/database/database.go index 6e27de84..40d6046e 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -2,6 +2,7 @@ package database import ( "fmt" + "github.com/openinfradev/tks-api/internal/delivery/api" "os" "strings" @@ -118,9 +119,6 @@ func migrateSchema(db *gorm.DB) error { if err := db.AutoMigrate(&domain.Permission{}); err != nil { return err } - if err := db.AutoMigrate(&domain.ProjectRole{}); err != nil { - return err - } if err := db.AutoMigrate(&domain.Endpoint{}); err != nil { return err } @@ -135,6 +133,61 @@ func migrateSchema(db *gorm.DB) error { if err := db.AutoMigrate(&domain.ProjectNamespace{}); err != nil { return err } + if err := db.AutoMigrate(&domain.ProjectRole{}); err != nil { + return err + } + + return nil +} + +func EnsureDefaultRows(db *gorm.DB) error { + // Create default rows + repoFactory := repository.Repository{ + Auth: repository.NewAuthRepository(db), + User: repository.NewUserRepository(db), + Cluster: repository.NewClusterRepository(db), + Organization: repository.NewOrganizationRepository(db), + AppGroup: repository.NewAppGroupRepository(db), + AppServeApp: repository.NewAppServeAppRepository(db), + CloudAccount: repository.NewCloudAccountRepository(db), + StackTemplate: repository.NewStackTemplateRepository(db), + Alert: repository.NewAlertRepository(db), + Role: repository.NewRoleRepository(db), + Permission: repository.NewPermissionRepository(db), + Endpoint: repository.NewEndpointRepository(db), + Project: repository.NewProjectRepository(db), + } + + // + eps, err := repoFactory.Endpoint.List(nil) + if err != nil { + return err + } + if len(eps) == 0 { + for _, ep := range api.ApiMap { + if err := repoFactory.Endpoint.Create(&domain.Endpoint{ + Name: ep.Name, + Group: ep.Group, + }); err != nil { + return err + } + } + } else { + var storedEps = make(map[string]struct{}) + for _, ep := range eps { + storedEps[ep.Name] = struct{}{} + } + for _, ep := range api.ApiMap { + if _, ok := storedEps[ep.Name]; !ok { + if err := repoFactory.Endpoint.Create(&domain.Endpoint{ + Name: ep.Name, + Group: ep.Group, + }); err != nil { + return err + } + } + } + } // Audit if err := db.AutoMigrate(&repository.Audit{}); err != nil { From 58b5679369dd16b99a6469e320da8a7cfa568306 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 21 Feb 2024 18:50:12 +0900 Subject: [PATCH 13/15] merge up-to-date --- internal/database/database.go | 24 +- internal/delivery/api/endpoint.go | 74 +++-- internal/rbac/permission.go | 504 ++++++++++++++++++++++++++++++ internal/repository/endpoint.go | 74 +---- internal/repository/permission.go | 280 ++++++----------- internal/repository/repository.go | 2 + internal/repository/role.go | 10 +- internal/usecase/organization.go | 30 +- internal/usecase/role.go | 4 +- internal/usecase/user.go | 20 +- pkg/domain/endpoint.go | 17 +- pkg/domain/mapper_test.go | 3 +- pkg/domain/permmision.go | 2 +- 13 files changed, 681 insertions(+), 363 deletions(-) create mode 100644 internal/rbac/permission.go diff --git a/internal/database/database.go b/internal/database/database.go index 40d6046e..cb3f0aee 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -163,8 +163,13 @@ func EnsureDefaultRows(db *gorm.DB) error { if err != nil { return err } - if len(eps) == 0 { - for _, ep := range api.ApiMap { + + var storedEps = make(map[string]struct{}) + for _, ep := range eps { + storedEps[ep.Name] = struct{}{} + } + for _, ep := range api.ApiMap { + if _, ok := storedEps[ep.Name]; !ok { if err := repoFactory.Endpoint.Create(&domain.Endpoint{ Name: ep.Name, Group: ep.Group, @@ -172,21 +177,6 @@ func EnsureDefaultRows(db *gorm.DB) error { return err } } - } else { - var storedEps = make(map[string]struct{}) - for _, ep := range eps { - storedEps[ep.Name] = struct{}{} - } - for _, ep := range api.ApiMap { - if _, ok := storedEps[ep.Name]; !ok { - if err := repoFactory.Endpoint.Create(&domain.Endpoint{ - Name: ep.Name, - Group: ep.Group, - }); err != nil { - return err - } - } - } } // Audit diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index e15c06a2..137cb4b6 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -82,6 +82,18 @@ const ( UpdateAppServeAppStatus UpdateAppServeAppEndpoint RollbackAppServeApp + CreateAppServeApp // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + GetAppServeApps // 프로젝트 관리/앱 서빙/조회 + GetNumOfAppsOnStack // 프로젝트 관리/앱 서빙/조회 + GetAppServeApp // 프로젝트 관리/앱 서빙/조회 + GetAppServeAppLatestTask // 프로젝트 관리/앱 서빙/조회 + IsAppServeAppExist // 프로젝트 관리/앱 서빙/조회 // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + IsAppServeAppNameExist // 프로젝트 관리/앱 서빙/조회 // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + DeleteAppServeApp // 프로젝트 관리/앱 서빙/삭제 + UpdateAppServeApp // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + UpdateAppServeAppStatus // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + UpdateAppServeAppEndpoint // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + RollbackAppServeApp // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 // CloudAccount GetCloudAccounts @@ -102,10 +114,10 @@ const ( DeleteStackTemplate // Dashboard - GetChartsDashboard - GetChartDashboard - GetStacksDashboard - GetResourcesDashboard + GetChartsDashboard // 대시보드/대시보드/조회 + GetChartDashboard // 대시보드/대시보드/조회 + GetStacksDashboard // 대시보드/대시보드/조회 + GetResourcesDashboard // 대시보드/대시보드/조회 // Alert CreateAlert @@ -116,36 +128,36 @@ const ( CreateAlertAction // Stack - GetStacks - CreateStack - CheckStackName - GetStack - UpdateStack - DeleteStack - GetStackKubeConfig - GetStackStatus - SetFavoriteStack - DeleteFavoriteStack - InstallStack + GetStacks // 스택관리/조회 + CreateStack // // 스택관리/생성 + CheckStackName // 스택관리/조회 + GetStack // 스택관리/조회 + UpdateStack // 스택관리/수정 + DeleteStack // 스택관리/삭제 + GetStackKubeConfig // 스택관리/조회 + GetStackStatus // 스택관리/조회 + SetFavoriteStack // 스택관리/조회 + DeleteFavoriteStack // 스택관리/조회 + InstallStack // 스택관리 / 조회 // Project - CreateProject - GetProjectRoles - GetProjectRole - GetProjects - GetProject - UpdateProject - DeleteProject - AddProjectMember - GetProjectMember - GetProjectMembers - RemoveProjectMember - UpdateProjectMemberRole - CreateProjectNamespace - GetProjectNamespaces - GetProjectNamespace + CreateProject // 프로젝트 관리/프로젝트/생성 + GetProjectRoles // 프로젝트 관리/설정-일반/조회 // 프로젝트 관리/설정-멤버/조회 + GetProjectRole // 프로젝트 관리/설정-일반/조회 // 프로젝트 관리/설정-멤버/조회 + GetProjects // 프로젝트 관리/프로젝트/조회 // 프로젝트 관리/설정-일반/조회 + GetProject // 프로젝트 관리/프로젝트/조회 // 프로젝트 관리/설정-일반/조회 + UpdateProject // 프로젝트 관리/설정-일반/수정 + DeleteProject // 프로젝트 관리/설정-일반/삭제 + AddProjectMember // 프로젝트 관리/설정-멤버/생성 + GetProjectMember // 프로젝트 관리/설정-멤버/조회 + GetProjectMembers // 프로젝트 관리/설정-멤버/조회 + RemoveProjectMember // 프로젝트 관리/설정-멤버/삭제 + UpdateProjectMemberRole // 프로젝트 관리/설정-멤버/수정 + CreateProjectNamespace // 프로젝트 관리/설정-네임스페이스/생성 + GetProjectNamespaces // 프로젝트 관리/설정-네임스페이스/조회 + GetProjectNamespace // 프로젝트 관리/설정-네임스페이스/조회 UpdateProjectNamespace - DeleteProjectNamespace + DeleteProjectNamespace // 프로젝트 관리/설정-네임스페이스/삭제 SetFavoriteProject SetFavoriteProjectNamespace UnSetFavoriteProject diff --git a/internal/rbac/permission.go b/internal/rbac/permission.go new file mode 100644 index 00000000..4b0ea9ed --- /dev/null +++ b/internal/rbac/permission.go @@ -0,0 +1,504 @@ +package rbac + +import ( + "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/pkg/domain" +) + +type PermissionSet struct { + Dashboard *domain.Permission + Stack *domain.Permission + SecurityPolicy *domain.Permission + ProjectManagement *domain.Permission + Notification *domain.Permission + Configuration *domain.Permission +} + +func NewDefaultPermission() *PermissionSet { + return &PermissionSet{ + Dashboard: newDashboard(), + Stack: newStack(), + SecurityPolicy: newSecurityPolicy(), + ProjectManagement: newProjectManagement(), + Notification: newNotification(), + Configuration: newConfiguration(), + } +} + +func endpointObjects(eps ...api.Endpoint) []*domain.Endpoint { + var result []*domain.Endpoint + for _, ep := range eps { + result = append(result, &domain.Endpoint{ + Name: api.ApiMap[ep].Name, + Group: api.ApiMap[ep].Group, + }) + } + return result +} + +func newDashboard() *domain.Permission { + dashboard := &domain.Permission{ + Name: "대시보드", + Children: []*domain.Permission{ + { + Name: "대시보드", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetChartsDashboard, + api.GetChartDashboard, + api.GetStacksDashboard, + api.GetResourcesDashboard, + ), + }, + }, + }, + { + Name: "대시보드 설정", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, + } + + return dashboard +} + +func newStack() *domain.Permission { + stack := &domain.Permission{ + Name: "스택 관리", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetStacks, + api.GetStack, + api.CheckStackName, + api.GetStackStatus, + api.GetStackKubeConfig, + + api.SetFavoriteStack, + api.DeleteFavoriteStack, + ), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateStack, + api.InstallStack, + ), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateStack, + ), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteStack, + ), + }, + }, + } + + return stack +} + +func newSecurityPolicy() *domain.Permission { + security_policy := &domain.Permission{ + Name: "보안/정책 관리", + Children: []*domain.Permission{ + { + Name: "보안/정책", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, + } + + return security_policy +} + +func newProjectManagement() *domain.Permission { + projectManagement := &domain.Permission{ + Name: "프로젝트 관리", + Children: []*domain.Permission{ + { + Name: "프로젝트", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjects, + api.GetProject, + ), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateProject, + ), + }, + }, + }, + { + Name: "앱 서빙", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetAppServeApps, + api.GetAppServeApp, + api.GetNumOfAppsOnStack, + api.GetAppServeAppLatestTask, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + ), + }, + { + Name: "빌드", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateAppServeApp, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.UpdateAppServeApp, + api.UpdateAppServeAppEndpoint, + api.UpdateAppServeAppStatus, + api.RollbackAppServeApp, + ), + }, + { + Name: "배포", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateAppServeApp, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.UpdateAppServeApp, + api.UpdateAppServeAppEndpoint, + api.UpdateAppServeAppStatus, + api.RollbackAppServeApp, + ), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteAppServeApp, + ), + }, + }, + }, + { + Name: "설정-일반", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjects, + api.GetProject, + + api.GetProjectRoles, + api.GetProjectRole, + ), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateProject, + ), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteProject, + ), + }, + }, + }, + { + Name: "설정-멤버", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjectMembers, + api.GetProjectMember, + api.GetProjectRoles, + api.GetProjectRole, + ), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.AddProjectMember, + ), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateProjectMemberRole, + ), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.RemoveProjectMember, + ), + }, + }, + }, + { + Name: "설정-네임스페이스", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjectNamespaces, + api.GetProjectNamespace, + ), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateProjectNamespace, + ), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects(), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteProjectNamespace, + ), + }, + }, + }, + }, + } + + return projectManagement +} + +func newNotification() *domain.Permission { + notification := &domain.Permission{ + Name: "알림", + Children: []*domain.Permission{ + { + Name: "시스템 경고", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "보안/정책 감사로그", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, + } + + return notification +} + +func newConfiguration() *domain.Permission { + configuration := &domain.Permission{ + Name: "설정", + Children: []*domain.Permission{ + { + Name: "일반", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "클라우드 계정", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "스택 템플릿", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "프로젝트 관리", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "사용자", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "사용자 권한 관리", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + Name: "알림 설정", + Children: []*domain.Permission{ + { + Name: "조회", + IsAllowed: helper.BoolP(false), + }, + { + Name: "생성", + IsAllowed: helper.BoolP(false), + }, + { + Name: "수정", + IsAllowed: helper.BoolP(false), + }, + { + Name: "삭제", + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, + } + + return configuration +} diff --git a/internal/repository/endpoint.go b/internal/repository/endpoint.go index 1bd4d4cc..c90589ac 100644 --- a/internal/repository/endpoint.go +++ b/internal/repository/endpoint.go @@ -2,7 +2,6 @@ package repository import ( "fmt" - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/pkg/domain" "gorm.io/gorm" @@ -12,9 +11,7 @@ import ( type IEndpointRepository interface { Create(endpoint *domain.Endpoint) error List(pg *pagination.Pagination) ([]*domain.Endpoint, error) - Get(id uuid.UUID) (*domain.Endpoint, error) - Update(endpoint *domain.Endpoint) error - Delete(id uuid.UUID) error + Get(id uint) (*domain.Endpoint, error) } type EndpointRepository struct { @@ -27,22 +24,10 @@ func NewEndpointRepository(db *gorm.DB) *EndpointRepository { } } -//type Endpoint struct { -// gorm.Model -// -// ID uuid.UUID `gorm:"type:uuid;primary_key;"` -// -// Name string `gorm:"type:text;not null;unique"` -// Group string `gorm:"type:text;"` -// PermissionID uuid.UUID `gorm:"type:uuid;"` -// Permission Permission `gorm:"foreignKey:PermissionID;"` -//} - func (e *EndpointRepository) Create(endpoint *domain.Endpoint) error { obj := &domain.Endpoint{ - Name: endpoint.Name, - Group: endpoint.Group, - PermissionID: endpoint.PermissionID, + Name: endpoint.Name, + Group: endpoint.Group, } if err := e.db.Create(obj).Error; err != nil { @@ -54,7 +39,6 @@ func (e *EndpointRepository) Create(endpoint *domain.Endpoint) error { func (e *EndpointRepository) List(pg *pagination.Pagination) ([]*domain.Endpoint, error) { var endpoints []*domain.Endpoint - var objs []*domain.Endpoint if pg == nil { pg = pagination.NewDefaultPagination() @@ -66,24 +50,15 @@ func (e *EndpointRepository) List(pg *pagination.Pagination) ([]*domain.Endpoint pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Preload("Permissions").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&endpoints) if res.Error != nil { return nil, res.Error } - for _, obj := range objs { - endpoints = append(endpoints, &domain.Endpoint{ - ID: obj.ID, - Name: obj.Name, - Group: obj.Group, - PermissionID: obj.PermissionID, - Permission: obj.Permission, - }) - } return endpoints, nil } -func (e *EndpointRepository) Get(id uuid.UUID) (*domain.Endpoint, error) { +func (e *EndpointRepository) Get(id uint) (*domain.Endpoint, error) { var obj domain.Endpoint if err := e.db.Preload("Permission").First(&obj, "id = ?", id).Error; err != nil { @@ -91,46 +66,7 @@ func (e *EndpointRepository) Get(id uuid.UUID) (*domain.Endpoint, error) { } return &domain.Endpoint{ - ID: obj.ID, Name: obj.Name, Group: obj.Group, }, nil } - -func (e *EndpointRepository) Update(endpoint *domain.Endpoint) error { - obj := &domain.Endpoint{ - ID: endpoint.ID, - Name: endpoint.Name, - Group: endpoint.Group, - PermissionID: endpoint.PermissionID, - } - - if err := e.db.Save(obj).Error; err != nil { - return err - } - - return nil -} - -// -//// domain.Endpoint to repository.Endpoint -//func ConvertDomainToRepoEndpoint(endpoint *domain.Endpoint) *Endpoint { -// return &Endpoint{ -// ID: endpoint.ID, -// Name: endpoint.Name, -// Group: endpoint.Group, -// PermissionID: endpoint.PermissionID, -// Permission: *ConvertDomainToRepoPermission(&endpoint.Permission), -// } -//} -// -//// repository.Endpoint to domain.Endpoint -//func ConvertRepoToDomainEndpoint(endpoint *Endpoint) *domain.Endpoint { -// return &domain.Endpoint{ -// ID: endpoint.ID, -// Name: endpoint.Name, -// Group: endpoint.Group, -// PermissionID: endpoint.PermissionID, -// Permission: *ConvertRepoToDomainPermission(&endpoint.Permission), -// } -//} diff --git a/internal/repository/permission.go b/internal/repository/permission.go index 5a28a708..09c50939 100644 --- a/internal/repository/permission.go +++ b/internal/repository/permission.go @@ -24,197 +24,91 @@ func NewPermissionRepository(db *gorm.DB) *PermissionRepository { } } -// -//type Permission struct { -// gorm.Model -// -// ID uuid.UUID `gorm:"primarykey;type:uuid;"` -// Name string -// -// IsAllowed *bool `gorm:"type:boolean;"` -// RoleID string -// Role *Role `gorm:"foreignKey:RoleID;references:ID;"` -// Endpoints []*Endpoint `gorm:"one2many:endpoints;"` -// -// ParentID *uuid.UUID -// Parent *Permission `gorm:"foreignKey:ParentID;references:ID;"` -// Children []*Permission `gorm:"foreignKey:ParentID;references:ID;"` -//} -// -//func (p *Permission) BeforeCreate(tx *gorm.DB) (err error) { -// if p.ID == uuid.Nil { -// p.ID = uuid.New() -// } -// return nil -//} -// -//func (r PermissionRepository) Create(p *domain.Permission) error { -// //var parent *Permission -// //var children []*Permission -// // -// //if p.Parent != nil { -// // parent = &Permission{} -// // result := r.db.First(&parent, "id = ?", p.Parent.ID) -// // if result.Error != nil { -// // return result.Error -// // } -// //} -// //if p.Children != nil { -// // for _, child := range p.Children { -// // newChild := &Permission{} -// // result := r.db.First(&newChild, "id = ?", child.ID) -// // if result.Error != nil { -// // return result.Error -// // } -// // children = append(children, newChild) -// // } -// //} -// -// permission := ConvertDomainToRepoPermission(p) -// -// return r.db.Create(permission).Error -//} -// -//func (r PermissionRepository) List() ([]*domain.Permission, error) { -// var permissions []*domain.Permission -// var outs []*domain.Permission -// -// err := r.db.Preload("Children.Children.Children.Children").Where("parent_id IS NULL").Find(&permissions).Error -// if err != nil { -// return nil, err -// } -// -// for _, permission := range permissions { -// outs = append(outs, ConvertRepoToDomainPermission(permission)) -// } -// return outs, nil -//} -// -//func (r PermissionRepository) Get(id uuid.UUID) (*domain.Permission, error) { -// permission := &domain.Permission{} -// result := r.db.Preload("Children.Children.Children").Preload("Parent").First(&permission, "id = ?", id) -// if result.Error != nil { -// return nil, result.Error -// } -// -// return ConvertRepoToDomainPermission(permission), nil -//} -// -//func (r PermissionRepository) Delete(id uuid.UUID) error { -// return r.db.Delete(&Permission{}, id).Error -//} +func (r PermissionRepository) Create(p *domain.Permission) error { + //var parent *Permission + //var children []*Permission + // + //if p.Parent != nil { + // parent = &Permission{} + // result := r.db.First(&parent, "id = ?", p.Parent.ID) + // if result.Error != nil { + // return result.Error + // } + //} + //if p.Children != nil { + // for _, child := range p.Children { + // newChild := &Permission{} + // result := r.db.First(&newChild, "id = ?", child.ID) + // if result.Error != nil { + // return result.Error + // } + // children = append(children, newChild) + // } + //} -//func (r PermissionRepository) Update(p *domain.Permission) error { -// permission := ConvertDomainToRepoPermission(p) -// -// return r.db.Updates(permission).Error -// -// //var parent *Permission -// //var children []*Permission -// // -// //if p.Parent != nil { -// // parent = &Permission{} -// // result := r.db.First(&parent, "id = ?", p.Parent.ID) -// // if result.Error != nil { -// // return result.Error -// // } -// //} -// //if p.Children != nil { -// // for _, child := range p.Children { -// // newChild := &Permission{} -// // result := r.db.First(&newChild, "id = ?", child.ID) -// // if result.Error != nil { -// // return result.Error -// // } -// // children = append(children, newChild) -// // } -// //} -// // -// //permission := &Permission{} -// // -// //result := r.db.First(&permission, "id = ?", p.ID) -// //if result.Error != nil { -// // return result.Error -// //} -// // -// //permission.Name = p.Name -// //permission.Parent = parent -// //permission.Children = children -// //permission.IsAllowed = p.IsAllowed -// // -// //return r.db.Save(permission).Error -//} -// -//// repository.Permission to domain.Permission -//func ConvertRepoToDomainPermission(repoPerm *domain.Permission) *domain.Permission { -// if repoPerm == nil { -// return nil -// } -// -// if repoPerm.Endpoints == nil { -// repoPerm.Endpoints = []*Endpoint{} -// } -// var domainEndpoints []*domain.Endpoint -// for _, endpoint := range repoPerm.Endpoints { -// domainEndpoints = append(domainEndpoints, ConvertRepoToDomainEndpoint(endpoint)) -// } -// -// // Domain Permission 객체 생성 -// domainPerm := &domain.Permission{ -// ID: repoPerm.ID, -// Name: repoPerm.Name, -// ParentID: repoPerm.ParentID, -// IsAllowed: repoPerm.IsAllowed, -// Endpoints: domainEndpoints, -// } -// -// // 자식 권한들 변환 -// for _, child := range repoPerm.Children { -// domainChild := ConvertRepoToDomainPermission(child) -// domainPerm.Children = append(domainPerm.Children, domainChild) -// } -// -// // 부모 권한 변환 (부모 권한이 있을 경우만) -// if repoPerm.Parent != nil { -// domainPerm.Parent = ConvertRepoToDomainPermission(repoPerm.Parent) -// } -// -// return domainPerm -//} -// -//// domain.Permission to repository.Permission -//func ConvertDomainToRepoPermission(domainPerm *domain.Permission) *Permission { -// if domainPerm == nil { -// return nil -// } -// -// if domainPerm.Endpoints == nil { -// domainPerm.Endpoints = []*domain.Endpoint{} -// } -// var repoEndpoints []*Endpoint -// for _, endpoint := range domainPerm.Endpoints { -// repoEndpoints = append(repoEndpoints, ConvertDomainToRepoEndpoint(endpoint)) -// } -// -// // Domain Permission 객체 생성 -// repoPerm := &Permission{ -// ID: domainPerm.ID, -// Name: domainPerm.Name, -// ParentID: domainPerm.ParentID, -// IsAllowed: domainPerm.IsAllowed, -// Endpoints: repoEndpoints, -// } -// -// // 자식 권한들 변환 -// for _, child := range domainPerm.Children { -// repoChild := ConvertDomainToRepoPermission(child) -// repoPerm.Children = append(repoPerm.Children, repoChild) -// } -// -// // 부모 권한 변환 (부모 권한이 있을 경우만) -// if domainPerm.Parent != nil { -// repoPerm.Parent = ConvertDomainToRepoPermission(domainPerm.Parent) -// } -// -// return repoPerm -//} + return r.db.Create(p).Error +} + +func (r PermissionRepository) List() ([]*domain.Permission, error) { + var permissions []*domain.Permission + + err := r.db.Preload("Children.Children.Children.Children").Where("parent_id IS NULL").Find(&permissions).Error + if err != nil { + return nil, err + } + + return permissions, nil +} + +func (r PermissionRepository) Get(id uuid.UUID) (*domain.Permission, error) { + permission := &domain.Permission{} + result := r.db.Preload("Children.Children.Children").Preload("Parent").First(&permission, "id = ?", id) + if result.Error != nil { + return nil, result.Error + } + + return permission, nil +} + +func (r PermissionRepository) Delete(id uuid.UUID) error { + return r.db.Delete(&domain.Permission{}, "id = ?", id).Error +} + +func (r PermissionRepository) Update(p *domain.Permission) error { + return r.db.Save(p).Error + + //var parent *Permission + //var children []*Permission + // + //if p.Parent != nil { + // parent = &Permission{} + // result := r.db.First(&parent, "id = ?", p.Parent.ID) + // if result.Error != nil { + // return result.Error + // } + //} + //if p.Children != nil { + // for _, child := range p.Children { + // newChild := &Permission{} + // result := r.db.First(&newChild, "id = ?", child.ID) + // if result.Error != nil { + // return result.Error + // } + // children = append(children, newChild) + // } + //} + // + //permission := &Permission{} + // + //result := r.db.First(&permission, "id = ?", p.ID) + //if result.Error != nil { + // return result.Error + //} + // + //permission.Name = p.Name + //permission.Parent = parent + //permission.Children = children + //permission.IsAllowed = p.IsAllowed + // + //return r.db.Save(permission).Error +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go index e176a7c8..46138c9d 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -22,6 +22,8 @@ type Repository struct { StackTemplate IStackTemplateRepository Alert IAlertRepository Role IRoleRepository + Permission IPermissionRepository + Endpoint IEndpointRepository Project IProjectRepository Audit IAuditRepository } diff --git a/internal/repository/role.go b/internal/repository/role.go index 67607666..dbae6ed9 100644 --- a/internal/repository/role.go +++ b/internal/repository/role.go @@ -9,7 +9,7 @@ import ( ) type IRoleRepository interface { - Create(roleObj *domain.Role) error + Create(roleObj *domain.Role) (string, error) List(pg *pagination.Pagination) ([]*domain.Role, error) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.Role, error) Get(id string) (*domain.Role, error) @@ -32,15 +32,15 @@ func (r RoleRepository) GetTksRoleByRoleName(roleName string) (*domain.Role, err return &role, nil } -func (r RoleRepository) Create(roleObj *domain.Role) error { +func (r RoleRepository) Create(roleObj *domain.Role) (string, error) { if roleObj == nil { - return fmt.Errorf("roleObj is nil") + return "", fmt.Errorf("roleObj is nil") } if err := r.db.Create(roleObj).Error; err != nil { - return err + return "", err } - return nil + return roleObj.ID, nil } func (r RoleRepository) List(pg *pagination.Pagination) ([]*domain.Role, error) { diff --git a/internal/usecase/organization.go b/internal/usecase/organization.go index ad074123..9246d018 100644 --- a/internal/usecase/organization.go +++ b/internal/usecase/organization.go @@ -64,25 +64,21 @@ func (u *OrganizationUsecase) Create(ctx context.Context, in *domain.Organizatio } // Create admin roles in DB - if err := u.roleRepo.Create(domain.TksRole{ - Role: domain.Role{ - OrganizationID: organizationId, - Name: "admin", - Description: "admin", - Type: string(domain.RoleTypeTks), - }, + if _, err := u.roleRepo.Create(&domain.Role{ + OrganizationID: organizationId, + Name: "admin", + Description: "admin", + Type: string(domain.RoleTypeTks), }); err != nil { return "", err } // Create user roles in DB - if err := u.roleRepo.Create(domain.TksRole{ - Role: domain.Role{ - OrganizationID: organizationId, - Name: "user", - Description: "user", - Type: string(domain.RoleTypeTks), - }, + if _, err := u.roleRepo.Create(&domain.Role{ + OrganizationID: organizationId, + Name: "user", + Description: "user", + Type: string(domain.RoleTypeTks), }); err != nil { return "", err } @@ -140,11 +136,7 @@ func (u *OrganizationUsecase) Delete(organizationId string, accessToken string) return err } for _, role := range roles { - uuid, err := uuid.Parse(role.RoleID) - if err != nil { - return err - } - if err := u.roleRepo.DeleteCascade(uuid); err != nil { + if err := u.roleRepo.Delete(role.ID); err != nil { return err } } diff --git a/internal/usecase/role.go b/internal/usecase/role.go index 31d28b0e..70d51081 100644 --- a/internal/usecase/role.go +++ b/internal/usecase/role.go @@ -7,7 +7,7 @@ import ( ) type IRoleUsecase interface { - CreateTksRole(role *domain.Role) error + CreateTksRole(role *domain.Role) (string, error) ListRoles(pg *pagination.Pagination) ([]*domain.Role, error) ListTksRoles(organizationId string, pg *pagination.Pagination) ([]*domain.Role, error) GetTksRole(id string) (*domain.Role, error) @@ -25,7 +25,7 @@ func NewRoleUsecase(repo repository.Repository) *RoleUsecase { } } -func (r RoleUsecase) CreateTksRole(role *domain.Role) error { +func (r RoleUsecase) CreateTksRole(role *domain.Role) (string, error) { return r.repo.Create(role) } diff --git a/internal/usecase/user.go b/internal/usecase/user.go index e07d2cbb..3be12a0a 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -443,16 +443,16 @@ func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.Us // Get user role var roleUuid string - tksRoles, err := u.roleRepository.ListTksRoles(user.Organization.ID, nil) + roles, err := u.roleRepository.ListTksRoles(user.Organization.ID, nil) if err != nil { return nil, err } - if len(tksRoles) == 0 { + if len(roles) == 0 { return nil, httpErrors.NewInternalServerError(fmt.Errorf("role not found"), "", "") } - for _, role := range tksRoles { - if role.Role.Name == user.Role.Name { - roleUuid = role.RoleID + for _, role := range roles { + if role.Name == user.Role.Name { + roleUuid = role.ID } } if roleUuid == "" { @@ -501,16 +501,16 @@ func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId st // Get user role var roleUuid string - tksRoles, err := u.roleRepository.ListTksRoles(user.Organization.ID, nil) + roles, err := u.roleRepository.ListTksRoles(user.Organization.ID, nil) if err != nil { return nil, err } - if len(tksRoles) == 0 { + if len(roles) == 0 { return nil, httpErrors.NewInternalServerError(fmt.Errorf("role not found"), "", "") } - for _, role := range tksRoles { - if role.Role.Name == user.Role.Name { - roleUuid = role.RoleID + for _, role := range roles { + if role.Name == user.Role.Name { + roleUuid = role.ID } } if roleUuid == "" { diff --git a/pkg/domain/endpoint.go b/pkg/domain/endpoint.go index 6926dbe5..95e0292a 100644 --- a/pkg/domain/endpoint.go +++ b/pkg/domain/endpoint.go @@ -1,19 +1,6 @@ package domain -import ( - "github.com/google/uuid" - "gorm.io/gorm" -) - type Endpoint struct { - ID uuid.UUID `gorm:"type:uuid;primaryKey;" json:"id"` - Name string `gorm:"type:text;not null;unique" json:"name"` - Group string `gorm:"type:text;" json:"group"` - PermissionID uuid.UUID `gorm:"type:uuid;" json:"permissionId"` - Permission *Permission `gorm:"foreignKey:PermissionID;" json:"permission"` -} - -func (e *Endpoint) BeforeCreate(tx *gorm.DB) error { - e.ID = uuid.New() - return nil + Name string `gorm:"primary_key;type:text;not null;unique" json:"name"` + Group string `gorm:"type:text;" json:"group"` } diff --git a/pkg/domain/mapper_test.go b/pkg/domain/mapper_test.go index f1d57270..c235e360 100644 --- a/pkg/domain/mapper_test.go +++ b/pkg/domain/mapper_test.go @@ -2,6 +2,7 @@ package domain import ( "fmt" + "github.com/google/uuid" "testing" "time" ) @@ -84,7 +85,7 @@ func TestConvert(t *testing.T) { name: "test case User->GetUserResponse", args: args{ src: User{ - ID: "", + ID: uuid.New(), AccountId: "testAccount", Password: "testPassword", Name: "testName", diff --git a/pkg/domain/permmision.go b/pkg/domain/permmision.go index c3cf0d84..4bea6726 100644 --- a/pkg/domain/permmision.go +++ b/pkg/domain/permmision.go @@ -14,7 +14,7 @@ type Permission struct { IsAllowed *bool `gorm:"type:boolean;" json:"is_allowed,omitempty"` RoleID *string `json:"role_id,omitempty"` Role *Role `gorm:"foreignKey:RoleID;references:ID;" json:"role,omitempty"` - Endpoints []*Endpoint `gorm:"one2many:endpoints;" json:"endpoints,omitempty"` + Endpoints []*Endpoint `gorm:"many2many:permission_endpoints;" json:"endpoints,omitempty"` // omit empty ParentID *uuid.UUID `json:"parent_id,omitempty"` From e42120f823038f625e9db6b1df8cf55c89039b19 Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 4 Mar 2024 14:55:56 +0900 Subject: [PATCH 14/15] permission add --- internal/delivery/api/endpoint.go | 19 +- internal/delivery/http/organization.go | 58 ++++- internal/delivery/http/permission.go | 141 ++++++++++++ internal/delivery/http/role.go | 24 +- internal/helper/util.go | 4 + internal/repository/permission.go | 44 +--- internal/repository/role.go | 7 +- internal/route/route.go | 11 +- internal/usecase/organization.go | 20 -- internal/usecase/permission.go | 94 +++++++- internal/usecase/usecase.go | 2 + pkg/domain/endpoint.go | 9 +- {internal/rbac => pkg/domain}/permission.go | 229 +++++++++++++++----- pkg/domain/permmision.go | 30 --- pkg/domain/role.go | 1 - 15 files changed, 511 insertions(+), 182 deletions(-) create mode 100644 internal/delivery/http/permission.go rename {internal/rbac => pkg/domain}/permission.go (57%) delete mode 100644 pkg/domain/permmision.go diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 137cb4b6..4d457214 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -68,20 +68,8 @@ const ( CreateApplication // AppServeApp - CreateAppServeApp - GetAppServeApps - GetNumOfAppsOnStack - GetAppServeApp GetAppServeAppTasksByAppId GetAppServeAppTaskDetail - GetAppServeAppLatestTask - IsAppServeAppExist - IsAppServeAppNameExist - DeleteAppServeApp - UpdateAppServeApp - UpdateAppServeAppStatus - UpdateAppServeAppEndpoint - RollbackAppServeApp CreateAppServeApp // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 GetAppServeApps // 프로젝트 관리/앱 서빙/조회 GetNumOfAppsOnStack // 프로젝트 관리/앱 서빙/조회 @@ -157,7 +145,7 @@ const ( GetProjectNamespaces // 프로젝트 관리/설정-네임스페이스/조회 GetProjectNamespace // 프로젝트 관리/설정-네임스페이스/조회 UpdateProjectNamespace - DeleteProjectNamespace // 프로젝트 관리/설정-네임스페이스/삭제 + DeleteProjectNamespace // 프로젝트 관리/설정-네임스페이스/삭제 SetFavoriteProject SetFavoriteProjectNamespace UnSetFavoriteProject @@ -176,6 +164,11 @@ const ( GetTksRole DeleteTksRole UpdateTksRole + + // Permission + GetPermissionTemplates + GetPermissionsByRoleId + UpdatePermissionsByRoleId ) var ApiMap = map[Endpoint]EndpointInfo{ diff --git a/internal/delivery/http/organization.go b/internal/delivery/http/organization.go index 569d5145..8a3b7868 100644 --- a/internal/delivery/http/organization.go +++ b/internal/delivery/http/organization.go @@ -15,14 +15,18 @@ import ( ) type OrganizationHandler struct { - usecase usecase.IOrganizationUsecase - userUsecase usecase.IUserUsecase + usecase usecase.IOrganizationUsecase + userUsecase usecase.IUserUsecase + roleUsecase usecase.IRoleUsecase + permissionUsecase usecase.IPermissionUsecase } func NewOrganizationHandler(u usecase.Usecase) *OrganizationHandler { return &OrganizationHandler{ - usecase: u.Organization, - userUsecase: u.User, + usecase: u.Organization, + userUsecase: u.User, + roleUsecase: u.Role, + permissionUsecase: u.Permission, } } @@ -59,6 +63,52 @@ func (h *OrganizationHandler) CreateOrganization(w http.ResponseWriter, r *http. return } organization.ID = organizationId + + // Role 생성 + adminRole := domain.Role{ + OrganizationID: organizationId, + Name: "admin", + Description: "admin", + Type: string(domain.RoleTypeTks), + } + adminRoleId, err := h.roleUsecase.CreateTksRole(&adminRole) + if err != nil { + log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + userRole := domain.Role{ + OrganizationID: organizationId, + Name: "user", + Description: "user", + Type: string(domain.RoleTypeTks), + } + userRoleId, err := h.roleUsecase.CreateTksRole(&userRole) + if err != nil { + log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + // Permission 생성 + adminPermissionSet := h.permissionUsecase.GetAllowedPermissionSet() + h.permissionUsecase.SetRoleIdToPermissionSet(adminRoleId, adminPermissionSet) + err = h.permissionUsecase.CreatePermissionSet(adminPermissionSet) + if err != nil { + log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + userPermissionSet := h.permissionUsecase.GetUserPermissionSet() + h.permissionUsecase.SetRoleIdToPermissionSet(userRoleId, userPermissionSet) + err = h.permissionUsecase.CreatePermissionSet(userPermissionSet) + if err != nil { + log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + // Admin user 생성 _, err = h.userUsecase.CreateAdmin(organizationId, input.Email) if err != nil { diff --git a/internal/delivery/http/permission.go b/internal/delivery/http/permission.go new file mode 100644 index 00000000..b67a6b8e --- /dev/null +++ b/internal/delivery/http/permission.go @@ -0,0 +1,141 @@ +package http + +import ( + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "net/http" +) + +type IPermissionHandler interface { + GetPermissionTemplates(w http.ResponseWriter, r *http.Request) + GetPermissionsByRoleId(w http.ResponseWriter, r *http.Request) + UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) +} + +type PermissionHandler struct { + permissionUsecase usecase.IPermissionUsecase +} + +func NewPermissionHandler(usecase usecase.Usecase) *PermissionHandler { + return &PermissionHandler{ + permissionUsecase: usecase.Permission, + } +} + +// GetPermissionTemplates godoc +// @Tags Permission +// @Summary Get Permission Templates +// @Description Get Permission Templates +// @Accept json +// @Produce json +// @Success 200 {object} domain.PermissionSet +// @Router /permissions/templates [get] +func (h PermissionHandler) GetPermissionTemplates(w http.ResponseWriter, r *http.Request) { + permissionSet := domain.NewDefaultPermissionSet() + + var out domain.GetPermissionTemplatesResponse + out.Permissions = append(out.Permissions, permissionSet.Dashboard) + out.Permissions = append(out.Permissions, permissionSet.Stack) + out.Permissions = append(out.Permissions, permissionSet.SecurityPolicy) + out.Permissions = append(out.Permissions, permissionSet.ProjectManagement) + out.Permissions = append(out.Permissions, permissionSet.Notification) + out.Permissions = append(out.Permissions, permissionSet.Configuration) + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPermissionsByRoleId godoc +// @Tags Permission +// @Summary Get Permissions By Role ID +// @Description Get Permissions By Role ID +// @Accept json +// @Produce json +// @Success 200 {object} domain.PermissionSet +// @Router /roles/{roleId}/permissions [get] +func (h PermissionHandler) GetPermissionsByRoleId(w http.ResponseWriter, r *http.Request) { + // path parameter + var roleId string + + vars := mux.Vars(r) + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + permissionSet, err := h.permissionUsecase.GetPermissionSetByRoleId(roleId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + var out domain.GetPermissionsByRoleIdResponse + out.Permissions = append(out.Permissions, permissionSet.Dashboard) + out.Permissions = append(out.Permissions, permissionSet.Stack) + out.Permissions = append(out.Permissions, permissionSet.SecurityPolicy) + out.Permissions = append(out.Permissions, permissionSet.ProjectManagement) + out.Permissions = append(out.Permissions, permissionSet.Notification) + out.Permissions = append(out.Permissions, permissionSet.Configuration) + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdatePermissionsByRoleId godoc +// @Tags Permission +// @Summary Update Permissions By Role ID +// @Description Update Permissions By Role ID +// @Accept json +// @Produce json +// @Param roleId path string true "Role ID" +// @Param body body domain.UpdatePermissionsByRoleIdRequest true "Update Permissions By Role ID Request" +// @Success 200 +// @Router /roles/{roleId}/permissions [put] +func (h PermissionHandler) UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) { + // path parameter + log.Debug("UpdatePermissionsByRoleId Called") + var roleId string + _ = roleId + vars := mux.Vars(r) + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // request + input := domain.UpdatePermissionsByRoleIdRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + log.Debugf("input: %+v", input) + + for _, permission := range input.Permissions { + log.Debugf("permission: %+v", permission) + if err := h.permissionUsecase.UpdatePermission(permission); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + //var edgePermissions []*domain.Permission + //for _, permission := range input.Permissions { + // domain.GetEdgePermission(permission, edgePermissions, nil) + //} + //log.Debugf("edgePermissions: %+v", edgePermissions) + //for _, permission := range edgePermissions { + // err := h.permissionUsecase.UpdatePermission(permission) + // if err != nil { + // ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + // return + // } + //} + + ResponseJSON(w, r, http.StatusOK, nil) +} diff --git a/internal/delivery/http/role.go b/internal/delivery/http/role.go index 601440a9..8f357117 100644 --- a/internal/delivery/http/role.go +++ b/internal/delivery/http/role.go @@ -20,12 +20,14 @@ type IRoleHandler interface { } type RoleHandler struct { - roleUsecase usecase.IRoleUsecase + roleUsecase usecase.IRoleUsecase + permissionUsecase usecase.IPermissionUsecase } -func NewRoleHandler(roleUsecase usecase.IRoleUsecase) *RoleHandler { +func NewRoleHandler(usecase usecase.Usecase) *RoleHandler { return &RoleHandler{ - roleUsecase: roleUsecase, + roleUsecase: usecase.Role, + permissionUsecase: usecase.Permission, } } @@ -68,11 +70,24 @@ func (h RoleHandler) CreateTksRole(w http.ResponseWriter, r *http.Request) { Type: string(domain.RoleTypeTks), } - if err := h.roleUsecase.CreateTksRole(&dto); err != nil { + // create role + var roleId string + if roleId, err = h.roleUsecase.CreateTksRole(&dto); err != nil { ErrorJSON(w, r, err) return } + // create permission + defaultPermissionSet := domain.NewDefaultPermissionSet() + h.permissionUsecase.SetRoleIdToPermissionSet(roleId, defaultPermissionSet) + err = h.permissionUsecase.CreatePermissionSet(defaultPermissionSet) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, domain.CreateTksRoleResponse{ID: roleId}) } // ListTksRoles godoc @@ -238,7 +253,6 @@ func (h RoleHandler) UpdateTksRole(w http.ResponseWriter, r *http.Request) { // input to dto dto := domain.Role{ ID: roleId, - Name: input.Name, Description: input.Description, } diff --git a/internal/helper/util.go b/internal/helper/util.go index 5919d7b7..5a7cae86 100644 --- a/internal/helper/util.go +++ b/internal/helper/util.go @@ -96,6 +96,10 @@ func BoolP(value bool) *bool { return &value } +func StringP(value string) *string { + return &value +} + func UUIDP(value uuid.UUID) *uuid.UUID { return &value } diff --git a/internal/repository/permission.go b/internal/repository/permission.go index 09c50939..db097922 100644 --- a/internal/repository/permission.go +++ b/internal/repository/permission.go @@ -8,7 +8,7 @@ import ( type IPermissionRepository interface { Create(permission *domain.Permission) error - List() ([]*domain.Permission, error) + List(roleId string) ([]*domain.Permission, error) Get(id uuid.UUID) (*domain.Permission, error) Delete(id uuid.UUID) error Update(permission *domain.Permission) error @@ -49,10 +49,10 @@ func (r PermissionRepository) Create(p *domain.Permission) error { return r.db.Create(p).Error } -func (r PermissionRepository) List() ([]*domain.Permission, error) { +func (r PermissionRepository) List(roleId string) ([]*domain.Permission, error) { var permissions []*domain.Permission - err := r.db.Preload("Children.Children.Children.Children").Where("parent_id IS NULL").Find(&permissions).Error + err := r.db.Preload("Children.Children.Children.Children").Where("parent_id IS NULL AND role_id = ?", roleId).Find(&permissions).Error if err != nil { return nil, err } @@ -75,40 +75,6 @@ func (r PermissionRepository) Delete(id uuid.UUID) error { } func (r PermissionRepository) Update(p *domain.Permission) error { - return r.db.Save(p).Error - - //var parent *Permission - //var children []*Permission - // - //if p.Parent != nil { - // parent = &Permission{} - // result := r.db.First(&parent, "id = ?", p.Parent.ID) - // if result.Error != nil { - // return result.Error - // } - //} - //if p.Children != nil { - // for _, child := range p.Children { - // newChild := &Permission{} - // result := r.db.First(&newChild, "id = ?", child.ID) - // if result.Error != nil { - // return result.Error - // } - // children = append(children, newChild) - // } - //} - // - //permission := &Permission{} - // - //result := r.db.First(&permission, "id = ?", p.ID) - //if result.Error != nil { - // return result.Error - //} - // - //permission.Name = p.Name - //permission.Parent = parent - //permission.Children = children - //permission.IsAllowed = p.IsAllowed - // - //return r.db.Save(permission).Error + // update on is_allowed + return r.db.Model(&domain.Permission{}).Where("id = ?", p.ID).Updates(map[string]interface{}{"is_allowed": p.IsAllowed}).Error } diff --git a/internal/repository/role.go b/internal/repository/role.go index dbae6ed9..176dad48 100644 --- a/internal/repository/role.go +++ b/internal/repository/role.go @@ -78,12 +78,11 @@ func (r RoleRepository) ListTksRoles(organizationId string, pg *pagination.Pagin pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Joins("JOIN roles as r on r.id = tks_roles.role_id"). - Where("r.organization_id = ?", organizationId). + res := db. Offset(pg.GetOffset()). Limit(pg.GetLimit()). Order(orderQuery). - Find(&roles) + Find(&roles, "organization_id = ?", organizationId) //res := db.Preload("Role").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&objs) if res.Error != nil { return nil, res.Error @@ -103,7 +102,7 @@ func (r RoleRepository) Get(id string) (*domain.Role, error) { func (r RoleRepository) GetTksRole(id string) (*domain.Role, error) { var role domain.Role - if err := r.db.Preload("Role").First(&role, "role_id = ?", id).Error; err != nil { + if err := r.db.First(&role, "id = ?", id).Error; err != nil { return nil, err } diff --git a/internal/route/route.go b/internal/route/route.go index 432aede0..82e765fd 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -52,6 +52,8 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa Alert: repository.NewAlertRepository(db), Role: repository.NewRoleRepository(db), Project: repository.NewProjectRepository(db), + Permission: repository.NewPermissionRepository(db), + Endpoint: repository.NewEndpointRepository(db), Audit: repository.NewAuditRepository(db), } @@ -69,6 +71,8 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa Stack: usecase.NewStackUsecase(repoFactory, argoClient, usecase.NewDashboardUsecase(repoFactory, cache)), Project: usecase.NewProjectUsecase(repoFactory, kc, argoClient), Audit: usecase.NewAuditUsecase(repoFactory), + Role: usecase.NewRoleUsecase(repoFactory), + Permission: usecase.NewPermissionUsecase(repoFactory), } customMiddleware := internalMiddleware.NewMiddleware( @@ -232,13 +236,18 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/audits/{auditId}", customMiddleware.Handle(internalApi.GetAudit, http.HandlerFunc(auditHandler.GetAudit))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/audits/{auditId}", customMiddleware.Handle(internalApi.DeleteAudit, http.HandlerFunc(auditHandler.DeleteAudit))).Methods(http.MethodDelete) - roleHandler := delivery.NewRoleHandler(usecase.NewRoleUsecase(repoFactory)) + roleHandler := delivery.NewRoleHandler(usecaseFactory) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.CreateTksRole, http.HandlerFunc(roleHandler.CreateTksRole))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.ListTksRoles, http.HandlerFunc(roleHandler.ListTksRoles))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.GetTksRole, http.HandlerFunc(roleHandler.GetTksRole))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.DeleteTksRole, http.HandlerFunc(roleHandler.DeleteTksRole))).Methods(http.MethodDelete) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.UpdateTksRole, http.HandlerFunc(roleHandler.UpdateTksRole))).Methods(http.MethodPut) + permissionHandler := delivery.NewPermissionHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/permissions/templates", customMiddleware.Handle(internalApi.GetPermissionTemplates, http.HandlerFunc(permissionHandler.GetPermissionTemplates))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/permissions", customMiddleware.Handle(internalApi.GetPermissionsByRoleId, http.HandlerFunc(permissionHandler.GetPermissionsByRoleId))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/permissions", customMiddleware.Handle(internalApi.UpdatePermissionsByRoleId, http.HandlerFunc(permissionHandler.UpdatePermissionsByRoleId))).Methods(http.MethodPut) + r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", alertHandler.CreateAlert).Methods(http.MethodPost) // assets r.PathPrefix("/api/").HandlerFunc(http.NotFound) diff --git a/internal/usecase/organization.go b/internal/usecase/organization.go index 9246d018..832430a1 100644 --- a/internal/usecase/organization.go +++ b/internal/usecase/organization.go @@ -63,26 +63,6 @@ func (u *OrganizationUsecase) Create(ctx context.Context, in *domain.Organizatio return "", err } - // Create admin roles in DB - if _, err := u.roleRepo.Create(&domain.Role{ - OrganizationID: organizationId, - Name: "admin", - Description: "admin", - Type: string(domain.RoleTypeTks), - }); err != nil { - return "", err - } - - // Create user roles in DB - if _, err := u.roleRepo.Create(&domain.Role{ - OrganizationID: organizationId, - Name: "user", - Description: "user", - Type: string(domain.RoleTypeTks), - }); err != nil { - return "", err - } - workflowId, err := u.argo.SumbitWorkflowFromWftpl( "tks-create-contract-repo", argowf.SubmitOptions{ diff --git a/internal/usecase/permission.go b/internal/usecase/permission.go index df3c8072..4d38cca3 100644 --- a/internal/usecase/permission.go +++ b/internal/usecase/permission.go @@ -7,10 +7,15 @@ import ( ) type IPermissionUsecase interface { - CreatePermission(permission *domain.Permission) error - ListPermissions() ([]*domain.Permission, error) - GetPermission(id uuid.UUID) (*domain.Permission, error) - DeletePermission(id uuid.UUID) error + CreatePermissionSet(permissionSet *domain.PermissionSet) error + GetPermissionSetByRoleId(roleId string) (*domain.PermissionSet, error) + ListPermissions(roleId string) ([]*domain.Permission, error) + //GetPermission(id uuid.UUID) (*domain.Permission, error) + //DeletePermission(id uuid.UUID) error + //UpdatePermission(permission *domain.Permission) error + SetRoleIdToPermissionSet(roleId string, permissionSet *domain.PermissionSet) + GetAllowedPermissionSet() *domain.PermissionSet + GetUserPermissionSet() *domain.PermissionSet UpdatePermission(permission *domain.Permission) error } @@ -18,18 +23,71 @@ type PermissionUsecase struct { repo repository.IPermissionRepository } -func NewPermissionUsecase(repo repository.IPermissionRepository) *PermissionUsecase { +func NewPermissionUsecase(repo repository.Repository) *PermissionUsecase { return &PermissionUsecase{ - repo: repo, + repo: repo.Permission, } } -func (p PermissionUsecase) CreatePermission(permission *domain.Permission) error { - return p.repo.Create(permission) +func (p PermissionUsecase) CreatePermissionSet(permissionSet *domain.PermissionSet) error { + var err error + if err = p.repo.Create(permissionSet.Dashboard); err != nil { + return err + } + if err = p.repo.Create(permissionSet.Stack); err != nil { + return err + } + if err = p.repo.Create(permissionSet.SecurityPolicy); err != nil { + return err + } + if err = p.repo.Create(permissionSet.ProjectManagement); err != nil { + return err + } + if err = p.repo.Create(permissionSet.Notification); err != nil { + return err + } + if err = p.repo.Create(permissionSet.Configuration); err != nil { + return err + } + + return nil } +func (p PermissionUsecase) GetPermissionSetByRoleId(roleId string) (*domain.PermissionSet, error) { + permissionSet := &domain.PermissionSet{ + Dashboard: nil, + Stack: nil, + SecurityPolicy: nil, + ProjectManagement: nil, + Notification: nil, + Configuration: nil, + } -func (p PermissionUsecase) ListPermissions() ([]*domain.Permission, error) { - return p.repo.List() + permissionList, err := p.repo.List(roleId) + if err != nil { + return nil, err + } + for _, permission := range permissionList { + switch permission.Name { + case string(domain.DashBoardPermission): + permissionSet.Dashboard = permission + case string(domain.StackPermission): + permissionSet.Stack = permission + case string(domain.SecurityPolicyPermission): + permissionSet.SecurityPolicy = permission + case string(domain.ProjectManagementPermission): + permissionSet.ProjectManagement = permission + case string(domain.NotificationPermission): + permissionSet.Notification = permission + case string(domain.ConfigurationPermission): + permissionSet.Configuration = permission + } + } + + return permissionSet, nil +} + +func (p PermissionUsecase) ListPermissions(roleId string) ([]*domain.Permission, error) { + return p.repo.List(roleId) } func (p PermissionUsecase) GetPermission(id uuid.UUID) (*domain.Permission, error) { @@ -43,3 +101,19 @@ func (p PermissionUsecase) DeletePermission(id uuid.UUID) error { func (p PermissionUsecase) UpdatePermission(permission *domain.Permission) error { return p.repo.Update(permission) } + +func (p PermissionUsecase) SetRoleIdToPermissionSet(roleId string, permissionSet *domain.PermissionSet) { + permissionSet.SetRoleId(roleId) +} + +func (p PermissionUsecase) GetAllowedPermissionSet() *domain.PermissionSet { + permissionSet := domain.NewDefaultPermissionSet() + permissionSet.SetAllowedPermissionSet() + return permissionSet +} + +func (p PermissionUsecase) GetUserPermissionSet() *domain.PermissionSet { + permissionSet := domain.NewDefaultPermissionSet() + permissionSet.SetUserPermissionSet() + return permissionSet +} diff --git a/internal/usecase/usecase.go b/internal/usecase/usecase.go index 66111e99..4e7da85b 100644 --- a/internal/usecase/usecase.go +++ b/internal/usecase/usecase.go @@ -13,5 +13,7 @@ type Usecase struct { Alert IAlertUsecase Stack IStackUsecase Project IProjectUsecase + Role IRoleUsecase + Permission IPermissionUsecase Audit IAuditUsecase } diff --git a/pkg/domain/endpoint.go b/pkg/domain/endpoint.go index 95e0292a..c34a45ae 100644 --- a/pkg/domain/endpoint.go +++ b/pkg/domain/endpoint.go @@ -1,6 +1,11 @@ package domain +import ( + "time" +) + type Endpoint struct { - Name string `gorm:"primary_key;type:text;not null;unique" json:"name"` - Group string `gorm:"type:text;" json:"group"` + Name string `gorm:"primary_key;type:text;not null;unique" json:"name"` + Group string `gorm:"type:text;" json:"group"` + CreatedAt time.Time } diff --git a/internal/rbac/permission.go b/pkg/domain/permission.go similarity index 57% rename from internal/rbac/permission.go rename to pkg/domain/permission.go index 4b0ea9ed..ad95656e 100644 --- a/internal/rbac/permission.go +++ b/pkg/domain/permission.go @@ -1,21 +1,57 @@ -package rbac +package domain import ( + "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/delivery/api" "github.com/openinfradev/tks-api/internal/helper" - "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" ) +type PermissionKind string + +const ( + DashBoardPermission PermissionKind = "대시보드" + StackPermission PermissionKind = "스택 관리" + SecurityPolicyPermission PermissionKind = "보안/정책 관리" + ProjectManagementPermission PermissionKind = "프로젝트 관리" + NotificationPermission PermissionKind = "알림" + ConfigurationPermission PermissionKind = "설정" +) + +type Permission struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid;" json:"ID"` + Name string `json:"name"` + + IsAllowed *bool `gorm:"type:boolean;" json:"is_allowed,omitempty"` + RoleID *string `json:"role_id,omitempty"` + Role *Role `gorm:"foreignKey:RoleID;references:ID;" json:"role,omitempty"` + Endpoints []*Endpoint `gorm:"many2many:permission_endpoints;" json:"endpoints,omitempty"` + // omit empty + + ParentID *uuid.UUID `json:"parent_id,omitempty"` + Parent *Permission `gorm:"foreignKey:ParentID;references:ID;" json:"parent,omitempty"` + Children []*Permission `gorm:"foreignKey:ParentID;references:ID;" json:"children,omitempty"` +} + +func (p *Permission) BeforeCreate(tx *gorm.DB) (err error) { + if p.ID == uuid.Nil { + p.ID = uuid.New() + } + return nil +} + type PermissionSet struct { - Dashboard *domain.Permission - Stack *domain.Permission - SecurityPolicy *domain.Permission - ProjectManagement *domain.Permission - Notification *domain.Permission - Configuration *domain.Permission + Dashboard *Permission `gorm:"-:all" json:"dashboard,omitempty"` + Stack *Permission `gorm:"-:all" json:"stack,omitempty"` + SecurityPolicy *Permission `gorm:"-:all" json:"security_policy,omitempty"` + ProjectManagement *Permission `gorm:"-:all" json:"project_management,omitempty"` + Notification *Permission `gorm:"-:all" json:"notification,omitempty"` + Configuration *Permission `gorm:"-:all" json:"configuration,omitempty"` } -func NewDefaultPermission() *PermissionSet { +func NewDefaultPermissionSet() *PermissionSet { return &PermissionSet{ Dashboard: newDashboard(), Stack: newStack(), @@ -26,10 +62,41 @@ func NewDefaultPermission() *PermissionSet { } } -func endpointObjects(eps ...api.Endpoint) []*domain.Endpoint { - var result []*domain.Endpoint +type GetPermissionTemplatesResponse struct { + Permissions []*Permission `json:"permissions"` +} + +type GetPermissionsByRoleIdResponse struct { + Permissions []*Permission `json:"permissions"` +} + +type UpdatePermissionsByRoleIdRequest struct { + Permissions []*Permission `json:"permissions"` +} + +func GetEdgePermission(root *Permission, edgePermissions []*Permission, f *func(permission Permission) bool) []*Permission { + if root.Children == nil { + return append(edgePermissions, root) + } + + for _, child := range root.Children { + if f != nil && !(*f)(*child) { + continue + } + edgePermissions = GetEdgePermission(child, edgePermissions, f) + } + + return edgePermissions +} + +func SetRoleIDToPermission(roleID string, permission *Permission) { + permission.RoleID = helper.StringP(roleID) +} + +func endpointObjects(eps ...api.Endpoint) []*Endpoint { + var result []*Endpoint for _, ep := range eps { - result = append(result, &domain.Endpoint{ + result = append(result, &Endpoint{ Name: api.ApiMap[ep].Name, Group: api.ApiMap[ep].Group, }) @@ -37,13 +104,13 @@ func endpointObjects(eps ...api.Endpoint) []*domain.Endpoint { return result } -func newDashboard() *domain.Permission { - dashboard := &domain.Permission{ - Name: "대시보드", - Children: []*domain.Permission{ +func newDashboard() *Permission { + dashboard := &Permission{ + Name: string(DashBoardPermission), + Children: []*Permission{ { Name: "대시보드", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -58,7 +125,7 @@ func newDashboard() *domain.Permission { }, { Name: "대시보드 설정", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -83,10 +150,10 @@ func newDashboard() *domain.Permission { return dashboard } -func newStack() *domain.Permission { - stack := &domain.Permission{ - Name: "스택 관리", - Children: []*domain.Permission{ +func newStack() *Permission { + stack := &Permission{ + Name: string(StackPermission), + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -129,13 +196,13 @@ func newStack() *domain.Permission { return stack } -func newSecurityPolicy() *domain.Permission { - security_policy := &domain.Permission{ - Name: "보안/정책 관리", - Children: []*domain.Permission{ +func newSecurityPolicy() *Permission { + security_policy := &Permission{ + Name: string(SecurityPolicyPermission), + Children: []*Permission{ { Name: "보안/정책", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -160,13 +227,13 @@ func newSecurityPolicy() *domain.Permission { return security_policy } -func newProjectManagement() *domain.Permission { - projectManagement := &domain.Permission{ - Name: "프로젝트 관리", - Children: []*domain.Permission{ +func newProjectManagement() *Permission { + projectManagement := &Permission{ + Name: string(ProjectManagementPermission), + Children: []*Permission{ { Name: "프로젝트", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -186,7 +253,7 @@ func newProjectManagement() *domain.Permission { }, { Name: "앱 서빙", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -236,7 +303,7 @@ func newProjectManagement() *domain.Permission { }, { Name: "설정-일반", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -266,7 +333,7 @@ func newProjectManagement() *domain.Permission { }, { Name: "설정-멤버", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -302,7 +369,7 @@ func newProjectManagement() *domain.Permission { }, { Name: "설정-네임스페이스", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -338,13 +405,13 @@ func newProjectManagement() *domain.Permission { return projectManagement } -func newNotification() *domain.Permission { - notification := &domain.Permission{ - Name: "알림", - Children: []*domain.Permission{ +func newNotification() *Permission { + notification := &Permission{ + Name: string(NotificationPermission), + Children: []*Permission{ { Name: "시스템 경고", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -353,7 +420,7 @@ func newNotification() *domain.Permission { }, { Name: "보안/정책 감사로그", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -366,13 +433,13 @@ func newNotification() *domain.Permission { return notification } -func newConfiguration() *domain.Permission { - configuration := &domain.Permission{ - Name: "설정", - Children: []*domain.Permission{ +func newConfiguration() *Permission { + configuration := &Permission{ + Name: string(ConfigurationPermission), + Children: []*Permission{ { Name: "일반", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -385,7 +452,7 @@ func newConfiguration() *domain.Permission { }, { Name: "클라우드 계정", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -406,7 +473,7 @@ func newConfiguration() *domain.Permission { }, { Name: "스택 템플릿", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -415,7 +482,7 @@ func newConfiguration() *domain.Permission { }, { Name: "프로젝트 관리", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -436,7 +503,7 @@ func newConfiguration() *domain.Permission { }, { Name: "사용자", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -457,7 +524,7 @@ func newConfiguration() *domain.Permission { }, { Name: "사용자 권한 관리", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -478,7 +545,7 @@ func newConfiguration() *domain.Permission { }, { Name: "알림 설정", - Children: []*domain.Permission{ + Children: []*Permission{ { Name: "조회", IsAllowed: helper.BoolP(false), @@ -502,3 +569,59 @@ func newConfiguration() *domain.Permission { return configuration } + +func (p *PermissionSet) SetAllowedPermissionSet() { + edgePermissions := make([]*Permission, 0) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Dashboard, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Stack, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.SecurityPolicy, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.ProjectManagement, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Notification, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Configuration, edgePermissions, nil)...) + + for _, permission := range edgePermissions { + permission.IsAllowed = helper.BoolP(true) + } + + return +} + +func (p *PermissionSet) SetUserPermissionSet() { + f := func(permission Permission) bool { + return permission.Name == "조회" + } + edgePermissions := make([]*Permission, 0) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Dashboard, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Stack, edgePermissions, &f)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.SecurityPolicy, edgePermissions, &f)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.ProjectManagement, edgePermissions, &f)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Notification, edgePermissions, &f)...) + //edgePermissions = append(edgePermissions, GetEdgePermission(p.Configuration, edgePermissions, &f)...) + + for _, permission := range edgePermissions { + permission.IsAllowed = helper.BoolP(true) + } + + return +} + +func (p *PermissionSet) SetRoleId(roleId string) { + setRoleIdToPermission(p.Dashboard, roleId) + setRoleIdToPermission(p.Stack, roleId) + setRoleIdToPermission(p.SecurityPolicy, roleId) + setRoleIdToPermission(p.ProjectManagement, roleId) + setRoleIdToPermission(p.Notification, roleId) + setRoleIdToPermission(p.Configuration, roleId) +} + +func setRoleIdToPermission(root *Permission, roleId string) { + root.RoleID = helper.StringP(roleId) + + if root.Children == nil { + return + } + + for _, child := range root.Children { + setRoleIdToPermission(child, roleId) + } +} diff --git a/pkg/domain/permmision.go b/pkg/domain/permmision.go deleted file mode 100644 index 4bea6726..00000000 --- a/pkg/domain/permmision.go +++ /dev/null @@ -1,30 +0,0 @@ -package domain - -import ( - "github.com/google/uuid" - "gorm.io/gorm" -) - -type Permission struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid;" json:"id"` - Name string `json:"name"` - - IsAllowed *bool `gorm:"type:boolean;" json:"is_allowed,omitempty"` - RoleID *string `json:"role_id,omitempty"` - Role *Role `gorm:"foreignKey:RoleID;references:ID;" json:"role,omitempty"` - Endpoints []*Endpoint `gorm:"many2many:permission_endpoints;" json:"endpoints,omitempty"` - // omit empty - - ParentID *uuid.UUID `json:"parent_id,omitempty"` - Parent *Permission `gorm:"foreignKey:ParentID;references:ID;" json:"parent,omitempty"` - Children []*Permission `gorm:"foreignKey:ParentID;references:ID;" json:"children,omitempty"` -} - -func (p *Permission) BeforeCreate(tx *gorm.DB) (err error) { - if p.ID == uuid.Nil { - p.ID = uuid.New() - } - return nil -} diff --git a/pkg/domain/role.go b/pkg/domain/role.go index 204baa5d..4e19e39d 100644 --- a/pkg/domain/role.go +++ b/pkg/domain/role.go @@ -58,6 +58,5 @@ type ListTksRoleResponse struct { } type UpdateTksRoleRequest struct { - Name string `json:"name" validate:"required"` Description string `json:"description" validate:"omitempty,min=0,max=100"` } From 599f58d129f7aa775164e741690ed2477d06b7e0 Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 4 Mar 2024 15:57:12 +0900 Subject: [PATCH 15/15] solve merge confict --- internal/repository/organization.go | 7 ------- internal/repository/user.go | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/internal/repository/organization.go b/internal/repository/organization.go index 2c23b54b..a7839166 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -77,13 +77,6 @@ func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Org pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("organizations", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&domain.Organization{})) - db.Count(&pg.TotalRows) - - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&organizations) _, res := pg.Fetch(r.db, &organizations) if res.Error != nil { return nil, res.Error diff --git a/internal/repository/user.go b/internal/repository/user.go index 9de16cd2..baf14464 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -164,7 +164,7 @@ func (r *UserRepository) ListWithPagination(pg *pagination.Pagination, organizat pg = pagination.NewPagination(nil) } - _, res := pg.Fetch(r.db.Preload("Organization").Preload("Role").Model(&User{}).Where("organization_id = ?", organizationId), &users) + _, res := pg.Fetch(r.db.Preload("Organization").Preload("Role").Model(&domain.User{}).Where("organization_id = ?", organizationId), &users) if res.Error != nil { log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return nil, res.Error