From 0035251959c5ea2a83384c19b909c55eff6cd7dc Mon Sep 17 00:00:00 2001 From: sangkenlee Date: Fri, 22 Mar 2024 00:35:00 +0900 Subject: [PATCH 01/18] =?UTF-8?q?=EC=A0=95=EC=B1=85=20api=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/delivery/http/policy.go | 534 ++++++++++++++++++++++++++++++ internal/kubernetes/kubernetes.go | 20 +- internal/model/policy.go | 75 +++++ internal/repository/policy.go | 213 ++++++++++++ internal/repository/repository.go | 1 + internal/route/route.go | 13 + internal/usecase/policy.go | 258 +++++++++++++++ internal/usecase/usecase.go | 1 + pkg/domain/policy.go | 120 +++++++ pkg/httpErrors/errorCode.go | 6 + 10 files changed, 1240 insertions(+), 1 deletion(-) create mode 100644 internal/delivery/http/policy.go create mode 100644 internal/model/policy.go create mode 100644 internal/repository/policy.go create mode 100644 internal/usecase/policy.go create mode 100644 pkg/domain/policy.go diff --git a/internal/delivery/http/policy.go b/internal/delivery/http/policy.go new file mode 100644 index 00000000..93433286 --- /dev/null +++ b/internal/delivery/http/policy.go @@ -0,0 +1,534 @@ +package http + +import ( + "fmt" + "net/http" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" + "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" +) + +type PolicyHandler struct { + usecase usecase.IPolicyUsecase +} + +type IPolicyHandler interface { + CreatePolicy(w http.ResponseWriter, r *http.Request) + UpdatePolicy(w http.ResponseWriter, r *http.Request) + DeletePolicy(w http.ResponseWriter, r *http.Request) + GetPolicy(w http.ResponseWriter, r *http.Request) + ListPolicy(w http.ResponseWriter, r *http.Request) + UpdatePolicyTargetClusters(w http.ResponseWriter, r *http.Request) + GetMandatoryPolicies(w http.ResponseWriter, r *http.Request) + SetMandatoryPolicies(w http.ResponseWriter, r *http.Request) + ExistsPolicyName(w http.ResponseWriter, r *http.Request) +} + +func NewPolicyHandler(u usecase.Usecase) IPolicyHandler { + return &PolicyHandler{ + usecase: u.Policy, + } +} + +// CreatePolicy godoc +// +// @Tags Policy +// @Summary [CreatePolicy] 정책 생성 +// @Description 새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param body body domain.CreatePolicyRequest true "create policy request" +// @Success 200 {object} domain.CreatePolicyResponse +// @Router /organizations/{organizationId}/policies [post] +// @Security JWT +func (h *PolicyHandler) CreatePolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.CreatePolicyRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.Policy + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + + policyId, err := h.usecase.Create(r.Context(), organizationId, dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CreatePolicyResponse + out.ID = policyId.String() + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdatePolicy godoc +// +// @Tags Policy +// @Summary [UpdatePolicy] 정책을 업데이트 +// @Description 정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Param body body domain.UpdatePolicyRequest true "update policy set request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policies/{policyId} [patch] +// @Security JWT +func (h *PolicyHandler) UpdatePolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + input := domain.UpdatePolicyRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + var templateId *uuid.UUID = nil + + if input.TemplateId != nil { + tuuid, err := uuid.Parse(*input.TemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + templateId = &tuuid + + } + + err = h.usecase.Update(r.Context(), organizationId, id, + input.Mandatory, input.PolicyName, &input.Description, templateId, input.EnforcementAction, + input.Parameters, input.Match, input.TargetClusterIds) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeletePolicy godoc +// +// @Tags Policy +// @Summary [DeletePolicy] 정책 삭제 +// @Description 정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policies/{policyId} [delete] +// @Security JWT +func (h *PolicyHandler) DeletePolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + err = h.usecase.Delete(r.Context(), organizationId, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, "") +} + +// GetPolicy godoc +// +// @Tags Policy +// @Summary [GetPolicy] 정책 조회 +// @Description 정책 정보를 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Success 200 {object} domain.GetPolicyResponse +// @Router /organizations/{organizationId}/policies/{policyId} [get] +// @Security JWT +func (h *PolicyHandler) GetPolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + policy, err := h.usecase.Get(r.Context(), organizationId, id) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "P_NOT_FOUND_POLICY", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + if policy == nil { + ResponseJSON(w, r, http.StatusNotFound, nil) + return + } + + var out domain.GetPolicyResponse + if err = serializer.Map(r.Context(), *policy, &out.Policy); err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListPolicy godoc +// +// @Tags Policy +// @Summary [ListPolicy] 정책 목록 조회 +// @Description 정책 목록을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param limit query string false "pageSize" +// @Param page query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.ListPolicyResponse +// @Router /organizations/{organizationId}/policies [get] +// @Security JWT +func (h *PolicyHandler) ListPolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + + pg := pagination.NewPagination(&urlParams) + + policies, err := h.usecase.Fetch(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ListPolicyResponse + out.Policies = make([]domain.PolicyResponse, len(*policies)) + for i, policy := range *policies { + if err := serializer.Map(r.Context(), policy, &out.Policies[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdatePolicyTargetClusters godoc +// +// @Tags Policy +// @Summary [UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정 +// @Description 정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Param body body domain.UpdatePolicyClustersRequest true "update policy set request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policies/{policyId}/clusters [patch] +// @Security JWT +func (h *PolicyHandler) UpdatePolicyTargetClusters(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + input := domain.UpdatePolicyClustersRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + err = h.usecase.UpdatePolicyTargetClusters(r.Context(), organizationId, id, + input.CurrentTargetClusterIds, input.NewTargetClusterIds) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetMandatoryPolicies godoc +// +// @Tags Policy +// @Summary [GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회 +// @Description 템플릿, 정책이 필수 인지 여부를 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Success 200 {object} domain.GetMandatoryPoliciesResponse +// @Router /organizations/{organizationId}/mandatory-policies [get] +// @Security JWT +func (h *PolicyHandler) GetMandatoryPolicies(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + out, err := h.usecase.GetMandatoryPolicies(r.Context(), organizationId) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// SetMandatoryPolicies godoc +// +// @Tags Policy +// @Summary [SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정 +// @Description 템플릿, 정책이 필수 인지 여부를 설정한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param body body domain.SetMandatoryPoliciesRequest true "update mandatory policy/policy template request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/mandatory-policies [patch] +// @Security JWT +func (h *PolicyHandler) SetMandatoryPolicies(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + input := domain.SetMandatoryPoliciesRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + mandatoryPolicyIds := []uuid.UUID{} + nonMandatoryPolicyIds := []uuid.UUID{} + for _, policy := range input.Policies { + policyId, err := uuid.Parse(policy.PolicyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + if policy.Mandatory { + mandatoryPolicyIds = append(mandatoryPolicyIds, policyId) + } else { + nonMandatoryPolicyIds = append(nonMandatoryPolicyIds, policyId) + } + } + + err = h.usecase.SetMandatoryPolicies(r.Context(), organizationId, mandatoryPolicyIds, nonMandatoryPolicyIds) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } +} + +// ExistsPolicyName godoc +// +// @Tags Policy +// @Summary [ExistsPolicyName] 정책 아름 존재 여부 확인 +// @Description 해당 이름을 가진 정책이 이미 존재하는지 확인한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyName path string true "정책 이름" +// @Success 200 {object} domain.ExistenceResponse +// @Router /organizations/{organizationId}/policies/name/{policyName}/existence [get] +// @Security JWT +func (h *PolicyHandler) ExistsPolicyName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyName, ok := vars["policyName"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyTemplateName not found in path"), + "P_INVALID_POLICY_NAME", "")) + return + } + + exist, err := h.usecase.IsPolicyNameExist(r.Context(), organizationId, policyName) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CheckExistedResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/kubernetes/kubernetes.go b/internal/kubernetes/kubernetes.go index 7559ea26..00555cad 100644 --- a/internal/kubernetes/kubernetes.go +++ b/internal/kubernetes/kubernetes.go @@ -4,16 +4,18 @@ import ( "bytes" "context" "fmt" - "gopkg.in/yaml.v3" "os" "strings" + "gopkg.in/yaml.v3" + "github.com/spf13/viper" rbacV1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -57,6 +59,22 @@ func GetClientAdminCluster(ctx context.Context) (*kubernetes.Clientset, error) { return clientset, nil } +// 쿠버네티스 기본 타입 이외의 타입(예: 정책 템플릿, 정책 등)을 처리하기 위한 dynamic client 생성 +func GetDynamicClientAdminCluster(ctx context.Context) (*dynamic.DynamicClient, error) { + config, err := getAdminConfig(ctx) + if err != nil { + log.Error(ctx, "Failed to load kubeconfig") + return nil, err + } + + // create the dynamic client + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + return nil, err + } + return dynamicClient, nil +} + func GetAwsSecret(ctx context.Context) (awsAccessKeyId string, awsSecretAccessKey string, err error) { clientset, err := GetClientAdminCluster(ctx) if err != nil { diff --git a/internal/model/policy.go b/internal/model/policy.go new file mode 100644 index 00000000..2514c860 --- /dev/null +++ b/internal/model/policy.go @@ -0,0 +1,75 @@ +package model + +import ( + "encoding/json" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" +) + +type Policy struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:varchar(36);not null"` + OrganizationId string + + PolicyName string + Mandatory bool + Description string + + TargetClusterIds []string `gorm:"-:all"` + TargetClusters []Cluster `gorm:"many2many:policy_target_clusters"` + + EnforcementAction string + + Parameters string `gorm:"type:text"` + PolicyMatch string `gorm:"type:text"` + Match *domain.Match `gorm:"-:all"` + + TemplateName string `gorm:"-:all"` + TemplateId uuid.UUID `gorm:"type:uuid"` + PolicyTemplate PolicyTemplate `gorm:"foreignKey:TemplateId"` + + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator User `gorm:"foreignKey:UpdatorId"` +} + +func (p *Policy) BeforeCreate(tx *gorm.DB) (err error) { + p.ID = uuid.New() + + if p.Match != nil { + jsonBytes, err := json.Marshal(p.Match) + + if err != nil { + return err + } + + p.PolicyMatch = string(jsonBytes) + } + + return nil +} + +func (p *Policy) AfterFind(tx *gorm.DB) (err error) { + p.TemplateName = p.PolicyTemplate.TemplateName + + if len(p.PolicyMatch) > 0 { + // 목록 조회 시 에러가 발생해서 전체 조회가 실패하는 것을 방지하기 위해서 에러는 무시 + _ = json.Unmarshal([]byte(p.PolicyMatch), p.Match) + } + + p.TargetClusterIds = make([]string, len(p.TargetClusters)) + for i, cluster := range p.TargetClusters { + p.TargetClusterIds[i] = cluster.ID.String() + } + + return +} + +type PolicyTargetCluster struct { + PolicyId uuid.UUID `gorm:"primarykey"` + ClusterId domain.ClusterId `gorm:"primarykey"` +} diff --git a/internal/repository/policy.go b/internal/repository/policy.go new file mode 100644 index 00000000..4a59b2c6 --- /dev/null +++ b/internal/repository/policy.go @@ -0,0 +1,213 @@ +package repository + +import ( + "context" + "fmt" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type IPolicyRepository interface { + Create(ctx context.Context, dto model.Policy) (policyId uuid.UUID, err error) + Update(ctx context.Context, organizationId string, policyId uuid.UUID, + updateMap map[string]interface{}, TargetClusters *[]model.Cluster) (err error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out *[]model.Policy, err error) + ExistByName(ctx context.Context, organizationId string, policyName string) (exist bool, err error) + ExistByID(ctx context.Context, organizationId string, policyId uuid.UUID) (exist bool, err error) + GetByName(ctx context.Context, organizationId string, policyName string) (out *model.Policy, err error) + GetByID(ctx context.Context, organizationId string, policyId uuid.UUID) (out *model.Policy, err error) + Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) + UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusters []model.Cluster) (err error) + SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) +} + +type PolicyRepository struct { + db *gorm.DB +} + +func NewPolicyRepository(db *gorm.DB) IPolicyRepository { + return &PolicyRepository{ + db: db, + } +} + +func (r *PolicyRepository) Create(ctx context.Context, dto model.Policy) (policyId uuid.UUID, err error) { + err = r.db.WithContext(ctx).Create(&dto).Error + + if err != nil { + return uuid.Nil, err + } + + return dto.ID, nil +} + +func (r *PolicyRepository) Update(ctx context.Context, organizationId string, policyId uuid.UUID, + updateMap map[string]interface{}, targetClusters *[]model.Cluster) (err error) { + + var policy model.Policy + policy.ID = policyId + + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if targetClusters != nil { + err = tx.WithContext(ctx).Model(&policy).Limit(1). + Association("TargetClusters").Replace(targetClusters) + + if err != nil { + return err + } + } + + if len(updateMap) > 0 { + err = tx.WithContext(ctx).Model(&policy).Limit(1). + Where("id = ?", policyId). + Updates(updateMap).Error + + if err != nil { + return err + } + } + + // return nil will commit the whole transaction + return nil + }) +} + +func (r *PolicyRepository) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out *[]model.Policy, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(r.db.WithContext(ctx).Preload(clause.Associations). + Where("organization_id = ?", organizationId), &out) + + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *PolicyRepository) ExistBy(ctx context.Context, organizationId string, key string, value interface{}) (exists bool, err error) { + query := fmt.Sprintf("organization_id = ? and %s = ?", value) + + var policy model.Policy + res := r.db.WithContext(ctx).Where(query, organizationId, value). + First(&policy) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Infof(ctx, "Not found policy %s='%v'", key, value) + return false, nil + } else { + log.Error(ctx, res.Error) + return false, res.Error + } + } + + return true, nil +} + +func (r *PolicyRepository) ExistByName(ctx context.Context, organizationId string, policyName string) (exist bool, err error) { + return r.ExistBy(ctx, "policy_name", organizationId, policyName) +} + +func (r *PolicyRepository) ExistByID(ctx context.Context, organizationId string, policyId uuid.UUID) (exist bool, err error) { + return r.ExistBy(ctx, "id", organizationId, policyId) +} + +func (r *PolicyRepository) GetBy(ctx context.Context, organizationId string, key string, value interface{}) (out *model.Policy, err error) { + query := fmt.Sprintf("organization_id = ? and %s = ?", value) + + var policy model.Policy + res := r.db.WithContext(ctx).Preload(clause.Associations). + Where(query, organizationId, value).First(&policy) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Infof(ctx, "Not found policy %s='%v'", key, value) + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return &policy, nil +} + +func (r *PolicyRepository) GetByName(ctx context.Context, organizationId string, policyName string) (out *model.Policy, err error) { + return r.GetBy(ctx, organizationId, "policy_name", policyName) +} + +func (r *PolicyRepository) GetByID(ctx context.Context, organizationId string, policyId uuid.UUID) (out *model.Policy, err error) { + return r.GetBy(ctx, organizationId, "id", policyId) +} + +func (r *PolicyRepository) Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&model.Policy{ID: policyId}).Association("TargetClusters").Clear(); err != nil { + return err + } + + if err := tx.Where("organization_id = ? and id = ?", organizationId, policyId).Delete(&model.Policy{}).Error; err != nil { + return err + } + + return nil + }) +} + +func (r *PolicyRepository) UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusters []model.Cluster) (err error) { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var policy model.Policy + res := tx.Preload("TargetClusters").Where("organization_id = ? and id = ?", organizationId, policyId).First(&policy) + + if res.Error != nil { + return res.Error + } + + if len(policy.TargetClusterIds) == 0 && len(currentClusterIds) != 0 { + return errors.New("concurrent modification of target clusters") + } + + actualCurrentClusterIdSet := mapset.NewSet(policy.TargetClusterIds...) + knownCurrentClusterIdSet := mapset.NewSet(currentClusterIds...) + + if !actualCurrentClusterIdSet.Equal(knownCurrentClusterIdSet) { + return errors.New("concurrent modification of target clusters") + } + + err = tx.Model(&policy).Limit(1). + Association("TargetClusters").Replace(targetClusters) + + if err != nil { + return err + } + + // return nil will commit the whole transaction + return nil + }) +} + +func (r *PolicyRepository) SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err = tx.Model(&model.Policy{}). + Where("organization_id = ? id in ?", organizationId, mandatoryPolicyIds). + Update("mandatory", true).Error; err != nil { + return err + } + + if err = tx.Model(&model.Policy{}). + Where("organization_id = ? id in ?", organizationId, nonMandatoryPolicyIds). + Update("mandatory", false).Error; err != nil { + return err + } + + return nil + }) +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 0114cf59..bee291a5 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -21,6 +21,7 @@ type Repository struct { Project IProjectRepository Audit IAuditRepository PolicyTemplate IPolicyTemplateRepository + Policy IPolicyRepository SystemNotification ISystemNotificationRepository SystemNotificationTemplate ISystemNotificationTemplateRepository SystemNotificationRule ISystemNotificationRuleRepository diff --git a/internal/route/route.go b/internal/route/route.go index bb00ba3b..8c73bb75 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -58,6 +58,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa Endpoint: repository.NewEndpointRepository(db), Audit: repository.NewAuditRepository(db), PolicyTemplate: repository.NewPolicyTemplateRepository(db), + Policy: repository.NewPolicyRepository(db), Dashboard: repository.NewDashboardRepository(db), } @@ -80,6 +81,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa Role: usecase.NewRoleUsecase(repoFactory), Permission: usecase.NewPermissionUsecase(repoFactory), PolicyTemplate: usecase.NewPolicyTemplateUsecase(repoFactory), + Policy: usecase.NewPolicyUsecase(repoFactory), } customMiddleware := internalMiddleware.NewMiddleware( @@ -308,6 +310,17 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/name/{policyTemplateName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyTemplateName, http.HandlerFunc(policyTemplateHandler.ExistsPolicyTemplateName))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/policy-templates/rego-compile", customMiddleware.Handle(internalApi.CompileRego, http.HandlerFunc(policyTemplateHandler.RegoCompile))).Methods(http.MethodPost) + policyHandler := delivery.NewPolicyHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/mandatory-policies", customMiddleware.Handle(internalApi.GetMandatoryPolicies, http.HandlerFunc(policyHandler.GetMandatoryPolicies))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/mandatory-policies", customMiddleware.Handle(internalApi.SetMandatoryPolicies, http.HandlerFunc(policyHandler.SetMandatoryPolicies))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies", customMiddleware.Handle(internalApi.ListPolicy, http.HandlerFunc(policyHandler.ListPolicy))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies", customMiddleware.Handle(internalApi.CreatePolicy, http.HandlerFunc(policyHandler.CreatePolicy))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}", customMiddleware.Handle(internalApi.GetPolicy, http.HandlerFunc(policyHandler.GetPolicy))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}", customMiddleware.Handle(internalApi.DeletePolicy, http.HandlerFunc(policyHandler.DeletePolicy))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}", customMiddleware.Handle(internalApi.UpdatePolicy, http.HandlerFunc(policyHandler.UpdatePolicy))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}/clusters", customMiddleware.Handle(internalApi.UpdatePolicyTargetClusters, http.HandlerFunc(policyHandler.UpdatePolicyTargetClusters))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyName, http.HandlerFunc(policyHandler.ExistsPolicyName))).Methods(http.MethodGet) + // assets r.PathPrefix("/api/").HandlerFunc(http.NotFound) r.PathPrefix("/").Handler(httpSwagger.WrapHandler).Methods(http.MethodGet) diff --git a/internal/usecase/policy.go b/internal/usecase/policy.go new file mode 100644 index 00000000..4f7da889 --- /dev/null +++ b/internal/usecase/policy.go @@ -0,0 +1,258 @@ +package usecase + +import ( + "context" + "fmt" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" +) + +type IPolicyUsecase interface { + Create(ctx context.Context, organizationId string, dto model.Policy) (policyId uuid.UUID, err error) + Update(ctx context.Context, organizationId string, policyId uuid.UUID, + mandatory *bool, policyName *string, description *string, templateId *uuid.UUID, enforcementAction *string, + parameters *string, match *domain.Match, targetClusterIds *[]string) (err error) + Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) + Get(ctx context.Context, organizationId string, policyId uuid.UUID) (policy *model.Policy, err error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (*[]model.Policy, error) + IsPolicyIdExist(ctx context.Context, organizationId string, policyId uuid.UUID) (exists bool, err error) + IsPolicyNameExist(ctx context.Context, organizationId string, policyName string) (exists bool, err error) + UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusterIds []string) (err error) + SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) + GetMandatoryPolicies(ctx context.Context, organizationId string) (response *domain.GetMandatoryPoliciesResponse, err error) +} + +type PolicyUsecase struct { + organizationRepo repository.IOrganizationRepository + clusterRepo repository.IClusterRepository + templateRepo repository.IPolicyTemplateRepository + repo repository.IPolicyRepository +} + +func NewPolicyUsecase(r repository.Repository) IPolicyUsecase { + return &PolicyUsecase{ + repo: r.Policy, + templateRepo: r.PolicyTemplate, + organizationRepo: r.Organization, + clusterRepo: r.Cluster, + } +} + +func (u *PolicyUsecase) Create(ctx context.Context, organizationId string, dto model.Policy) (policyId uuid.UUID, err error) { + dto.OrganizationId = organizationId + + user, ok := request.UserFrom(ctx) + if !ok { + return uuid.Nil, httpErrors.NewUnauthorizedError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "") + } + + exists, err := u.repo.ExistByName(ctx, dto.OrganizationId, dto.TemplateName) + if err != nil { + return uuid.Nil, err + } + + if exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_NAME", "policy template name already exists") + } + + dto.TargetClusters = make([]model.Cluster, len(dto.TargetClusterIds)) + for i, clusterId := range dto.TargetClusterIds { + + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + dto.TargetClusters[i] = cluster + } + + userId := user.GetUserId() + dto.CreatorId = &userId + + id, err := u.repo.Create(ctx, dto) + + if err != nil { + return uuid.Nil, err + } + + return id, nil +} + +func (u *PolicyUsecase) Update(ctx context.Context, organizationId string, policyId uuid.UUID, + mandatory *bool, policyName *string, description *string, templateId *uuid.UUID, enforcementAction *string, + parameters *string, match *domain.Match, targetClusterIds *[]string) (err error) { + + user, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "") + } + + _, err = u.repo.GetByID(ctx, organizationId, policyId) + if err != nil { + return httpErrors.NewNotFoundError(err, "P_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + updateMap := make(map[string]interface{}) + + if mandatory != nil { + updateMap["mandatory"] = mandatory + } + + if policyName != nil { + exists, err := u.repo.ExistByName(ctx, organizationId, *policyName) + if err == nil && exists { + return httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY__NAME", "policy template name already exists") + } + updateMap["policy_name"] = policyName + } + + if description != nil { + updateMap["description"] = description + } + + if templateId != nil { + updateMap["template_id"] = templateId + } + + if enforcementAction != nil { + updateMap["enforcement_action"] = enforcementAction + } + + if parameters != nil { + updateMap["parameters"] = parameters + } + + if parameters != nil { + updateMap["policy_match"] = match.JSON() + } + + var newTargetClusters *[]model.Cluster = nil + + if targetClusterIds != nil { + targetClusters := make([]model.Cluster, len(*targetClusterIds)) + + for i, clusterId := range *targetClusterIds { + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), "C_INVALID_CLUSTER_ID", "") + } + + targetClusters[i] = cluster + } + newTargetClusters = &targetClusters + } else if len(updateMap) == 0 { + // 허용된 조직도 필드 속성도 업데이트되지 않았으므로 아무것도 업데이트할 것이 없음 + return nil + } + + updatorId := user.GetUserId() + updateMap["updator_id"] = updatorId + + err = u.repo.Update(ctx, organizationId, policyId, updateMap, newTargetClusters) + if err != nil { + return err + } + + return nil +} + +func (u *PolicyUsecase) Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) { + return u.repo.Delete(ctx, organizationId, policyId) +} + +func (u *PolicyUsecase) Get(ctx context.Context, organizationId string, policyId uuid.UUID) (policy *model.Policy, err error) { + return u.repo.GetByID(ctx, organizationId, policyId) +} + +func (u *PolicyUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (*[]model.Policy, error) { + return u.repo.Fetch(ctx, organizationId, pg) +} + +func (u *PolicyUsecase) IsPolicyNameExist(ctx context.Context, organizationId string, policyName string) (exists bool, err error) { + return u.repo.ExistByName(ctx, organizationId, policyName) +} + +func (u *PolicyUsecase) IsPolicyIdExist(ctx context.Context, organizationId string, policyId uuid.UUID) (exists bool, err error) { + return u.repo.ExistByID(ctx, organizationId, policyId) +} + +func (u *PolicyUsecase) UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusterIds []string) (err error) { + targetClusters := make([]model.Cluster, len(targetClusterIds)) + + for i, clusterId := range targetClusterIds { + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), "C_INVALID_CLUSTER_ID", "") + } + + targetClusters[i] = cluster + } + + return u.repo.UpdatePolicyTargetClusters(ctx, organizationId, policyId, currentClusterIds, targetClusters) +} + +func (u *PolicyUsecase) SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) { + return u.repo.SetMandatoryPolicies(ctx, organizationId, mandatoryPolicyIds, nonMandatoryPolicyIds) +} + +func (u *PolicyUsecase) GetMandatoryPolicies(ctx context.Context, organizationId string) (response *domain.GetMandatoryPoliciesResponse, err error) { + + var out domain.GetMandatoryPoliciesResponse + + policyTemplates, err := u.templateRepo.Fetch(ctx, nil) + + if err != nil { + return nil, err + } + + templateMaps := map[string]*domain.MandatoryTemplateInfo{} + + for _, policyTemplate := range policyTemplates { + templateId := policyTemplate.ID.String() + + if len(policyTemplate.PermittedOrganizationIds) == 0 || + mapset.NewSet(policyTemplate.PermittedOrganizationIds...).Contains(organizationId) { + templateMaps[templateId] = &domain.MandatoryTemplateInfo{ + TemplateName: policyTemplate.TemplateName, + TemplateId: templateId, + Description: policyTemplate.Description, + Policies: []domain.MandatoryPolicyInfo{}, + } + } + } + + policies, err := u.repo.Fetch(ctx, organizationId, nil) + + if err != nil { + return nil, err + } + + for _, policy := range *policies { + template, ok := templateMaps[policy.TemplateId.String()] + + if ok { + template.Policies = append(template.Policies, domain.MandatoryPolicyInfo{ + PolicyName: policy.PolicyName, + PolicyId: policy.ID.String(), + Description: policy.Description, + Mandatory: policy.Mandatory, + }) + + if policy.Mandatory { + template.Mandatory = true + } + } + } + + for _, template := range templateMaps { + out.Templates = append(out.Templates, *template) + } + + return &out, nil +} diff --git a/internal/usecase/usecase.go b/internal/usecase/usecase.go index 181d1f31..46302b69 100644 --- a/internal/usecase/usecase.go +++ b/internal/usecase/usecase.go @@ -19,4 +19,5 @@ type Usecase struct { Permission IPermissionUsecase Audit IAuditUsecase PolicyTemplate IPolicyTemplateUsecase + Policy IPolicyUsecase } diff --git a/pkg/domain/policy.go b/pkg/domain/policy.go new file mode 100644 index 00000000..e2ccf0e0 --- /dev/null +++ b/pkg/domain/policy.go @@ -0,0 +1,120 @@ +package domain + +import ( + "encoding/json" + "time" +) + +type Kinds struct { + APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"` + Kinds []string `json:"kinds,omitempty"` +} + +type Match struct { + Namespaces []string `json:"namespaces,omitempty"` + ExcludedNamespaces []string `json:"excludedNamespaces,omitempty"` + Kinds []Kinds `json:"kinds,omitempty"` +} + +func (m *Match) JSON() string { + jsonBytes, err := json.Marshal(m) + + if err != nil { + return "" + } + + return string(jsonBytes) +} + +type PolicyResponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + Creator SimpleUserResponse `json:"creator,omitempty"` + Updator SimpleUserResponse `json:"updator,omitempty"` + CreatedAt time.Time `json:"createdAt" format:"date-time"` + UpdatedAt time.Time `json:"updatedAt" format:"date-time"` + + TargetClusterIds []string `json:"targetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` + Mandatory bool `json:"mandatory"` + + PolicyName string `json:"policyName" example:"label 정책"` + Description string `json:"description"` + TemplateId string `json:"templateId" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + TemplateName string `json:"templateName" example:"필수 Label 검사"` + EnforcementAction string `json:"enforcementAction" enum:"warn,deny,dryrun"` + Parameters string `json:"parameters" example:"\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\""` + Match *Match `json:"match,omitempty" swaggertype:"object,string" example:"refer:match.Match"` + //Tags []string `json:"tags,omitempty" example:"k8s,label"` +} + +type CreatePolicyRequest struct { + TargetClusterIds []string `json:"targetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` + Mandatory bool `json:"mandatory"` + + PolicyName string `json:"policyName" example:"label 정책"` + Description string `json:"description"` + TemplateId string `json:"templateId" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + TemplateName string `json:"templateName" example:"필수 Label 검사"` + EnforcementAction string `json:"enforcementAction" enum:"warn,deny,dryrun"` + Parameters string `json:"parameters" example:"\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\""` + Match *Match `json:"match,omitempty" swaggertype:"object,string" example:"refer:match.Match"` + //Tags []string `json:"tags,omitempty" example:"k8s,label"` +} + +type CreatePolicyResponse struct { + ID string `json:"id"` +} + +type UpdatePolicyRequest struct { + TargetClusterIds *[]string `json:"targetClusterIds,omitempty" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` + Mandatory *bool `json:"mandatory,omitempty"` + + PolicyName *string `json:"policyName,omitempty" example:"label 정책"` + Description string `json:"description"` + TemplateId *string `json:"templateId,omitempty" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + EnforcementAction *string `json:"enforcementAction,omitempty" enum:"warn,deny,dryrun"` + Parameters *string `json:"parameters,omitempty" example:"\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\""` + Match *Match `json:"match,omitempty" swaggertype:"object,string" example:"refer:match.Match"` + //Tags []string `json:"tags,omitempty" example:"k8s,label"` +} + +type UpdatePolicyClustersRequest struct { + CurrentTargetClusterIds []string `json:"currentTargetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90"` + NewTargetClusterIds []string `json:"newTargetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` +} + +type GetPolicyResponse struct { + Policy PolicyResponse `json:"policy"` +} + +type ListPolicyResponse struct { + Policies []PolicyResponse `json:"policies"` + Pagination PaginationResponse `json:"pagination"` +} + +type MandatoryPolicyInfo struct { + PolicyName string `json:"policyName" example:"org 레이블 요구"` + PolicyId string `json:"policyId" example:"0091fe9b-e44b-423d-9562-ac2b73089593"` + Description string `json:"description" example:"org 레이블 설정 여부 검사"` + Mandatory bool `json:"mandatory"` +} + +type MandatoryTemplateInfo struct { + TemplateName string `json:"templateName" example:"레이블 요구"` + TemplateId string `json:"templateId" example:"708d1e5b-4e6f-40e9-87a3-329e2fd051a5"` + Description string `json:"description" example:"파라미터로 설정된 레이블 검사"` + Mandatory bool `json:"mandatory"` + Policies []MandatoryPolicyInfo `json:"policies"` +} + +type GetMandatoryPoliciesResponse struct { + Templates []MandatoryTemplateInfo `json:"templates"` +} + +type MandatoryPolicyPatchInfo struct { + PolicyId string `json:"policyId" example:"0091fe9b-e44b-423d-9562-ac2b73089593"` + Mandatory bool `json:"mandatory"` +} + +type SetMandatoryPoliciesRequest struct { + Policies []MandatoryPolicyPatchInfo `json:"policies"` +} diff --git a/pkg/httpErrors/errorCode.go b/pkg/httpErrors/errorCode.go index 5dff7467..f8e1b961 100644 --- a/pkg/httpErrors/errorCode.go +++ b/pkg/httpErrors/errorCode.go @@ -19,6 +19,7 @@ var errorMap = map[ErrorCode]string{ "C_INVALID_CLOUD_SERVICE": "유효하지 않은 클라우드서비스입니다.", "C_INVALID_AUDIT_ID": "유효하지 않은 로그 아이디입니다. 로그 아이디를 확인하세요.", "C_INVALID_POLICY_TEMPLATE_ID": "유효하지 않은 정책 템플릿 아이디입니다. 정책 템플릿 아이디를 확인하세요.", + "C_INVALID_POLICY_ID": "유효하지 않은 정책 아이디입니다. 정책 아이디를 확인하세요.", "C_FAILED_TO_CALL_WORKFLOW": "워크플로우 호출에 실패했습니다.", // Auth @@ -122,6 +123,11 @@ var errorMap = map[ErrorCode]string{ "PT_INVALID_POLICY_TEMPLATE_NAME": "유효하지 않은 정책 템플릿 이름입니다. 정책 템플릿 이름을 확인하세요.", "PT_INVALID_POLICY_TEMPLATE_KIND": "유효하지 않은 정책 템플릿 유형입니다. 정책 템플릿 유형을 확인하세요.", "PT_INVALID_REGO_PARSEPARAMETER": "유효하지 않은 Rego 파싱 설정입니다. Rego 파싱 설정을 확인하세요.", + + // Policy + "P_CREATE_ALREADY_EXISTED_NAME": "정첵에 이미 존재하는 이름입니다.", + "P_NOT_FOUND_POLICY": "정책이 존재하지 않습니다.", + "P_INVALID_POLICY_NAME": "유효하지 않은 정책 이름입니다. 정책 이름을 확인하세요.", } func (m ErrorCode) GetText() string { From 8aa80744c3db9b6d7de90cf970310745cc9f6c53 Mon Sep 17 00:00:00 2001 From: sangkenlee Date: Fri, 22 Mar 2024 01:20:07 +0900 Subject: [PATCH 02/18] =?UTF-8?q?Policy=20=ED=85=8C=EC=9D=B4=EB=B8=94=20mi?= =?UTF-8?q?grate=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/database/database.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/database/database.go b/internal/database/database.go index a6d9cb85..d70511cd 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -176,6 +176,10 @@ func migrateSchema(db *gorm.DB) error { return err } + if err := db.AutoMigrate(&model.Policy{}); err != nil { + return err + } + if err := db.AutoMigrate(&model.Dashboard{}); err != nil { return err } From bad071cb8b9dc769dc3ddda14edeaac147a32552 Mon Sep 17 00:00:00 2001 From: sangkenlee Date: Fri, 22 Mar 2024 02:03:29 +0900 Subject: [PATCH 03/18] =?UTF-8?q?parameterdef=20<->=20json=20schema=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=84=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/policy-template/paramdef-util.go | 85 +++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 internal/policy-template/paramdef-util.go diff --git a/internal/policy-template/paramdef-util.go b/internal/policy-template/paramdef-util.go new file mode 100644 index 00000000..04ca850c --- /dev/null +++ b/internal/policy-template/paramdef-util.go @@ -0,0 +1,85 @@ +package policytemplate + +import ( + "strings" + + "github.com/openinfradev/tks-api/pkg/domain" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +func ParamDefsToJSONSchemaProeprties(paramdefs []*domain.ParameterDef) *apiextensionsv1.JSONSchemaProps { + if paramdefs == nil { + return nil + } + + result := apiextensionsv1.JSONSchemaProps{Type: "object", Properties: convert(paramdefs)} + + return &result +} + +func convert(paramdefs []*domain.ParameterDef) map[string]apiextensionsv1.JSONSchemaProps { + result := map[string]apiextensionsv1.JSONSchemaProps{} + + for _, paramdef := range paramdefs { + isArary := paramdef.IsArray + isObject := len(paramdef.Children) > 0 + + switch { + case isArary && isObject: + result[paramdef.Key] = apiextensionsv1.JSONSchemaProps{ + Type: "array", + Items: &apiextensionsv1.JSONSchemaPropsOrArray{ + Schema: ParamDefsToJSONSchemaProeprties(paramdef.Children), + }, + } + case isArary: + result[paramdef.Key] = apiextensionsv1.JSONSchemaProps{ + Type: "array", + Items: &apiextensionsv1.JSONSchemaPropsOrArray{ + Schema: &apiextensionsv1.JSONSchemaProps{Type: strings.TrimSuffix(paramdef.Type, "[]")}, + }, + } + case isObject: + result[paramdef.Key] = *ParamDefsToJSONSchemaProeprties(paramdef.Children) + _: + result[paramdef.Key] = apiextensionsv1.JSONSchemaProps{Type: paramdef.Type} + } + + } + + return result +} + +func JSONSchemaProeprtiesToParamDefs(jsschema *apiextensionsv1.JSONSchemaProps) []*domain.ParameterDef { + return convertToParameterDef(jsschema).Children +} + +func convertToParameterDef(jsschema *apiextensionsv1.JSONSchemaProps) *domain.ParameterDef { + // result := []ParameterDef{} + // fmt.Println(jsschema.Properties) + switch jsschema.Type { + case "array": + itemDef := convertToParameterDef(jsschema.Items.Schema) + itemDef.Type = jsschema.Items.Schema.Type + "[]" + itemDef.IsArray = true + + return itemDef + case "object": + children := []*domain.ParameterDef{} + for kc, vc := range jsschema.Properties { + converted := convertToParameterDef(&vc) + converted.Key = kc + children = append(children, converted) + } + return &domain.ParameterDef{Key: jsschema.ID, Type: jsschema.Type, DefaultValue: "", + Children: children} + default: + defaultValue := "" + + if jsschema.Default != nil { + defaultValue = string(jsschema.Default.Raw) + } + + return &domain.ParameterDef{Key: jsschema.ID, Type: jsschema.Type, DefaultValue: defaultValue, Children: []*domain.ParameterDef{}} + } +} From ea6dde400d242d3fb34a8f4c46f76f1c2f7525b8 Mon Sep 17 00:00:00 2001 From: sangkenlee Date: Fri, 22 Mar 2024 08:54:47 +0900 Subject: [PATCH 04/18] =?UTF-8?q?swagger=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/delivery/http/policy-template.go | 2 +- internal/delivery/http/policy.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/delivery/http/policy-template.go b/internal/delivery/http/policy-template.go index 5b0dfa3e..27bb93ce 100644 --- a/internal/delivery/http/policy-template.go +++ b/internal/delivery/http/policy-template.go @@ -615,7 +615,7 @@ func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, // @Accept json // @Produce json // @Param policyTemplateKind path string true "정책 템플릿 이름" -// @Success 200 {object} domain.ExistsPolicyTemplateKindResponse +// @Success 200 {object} domain.CheckExistedResponse // @Router /admin/policy-templates/kind/{policyTemplateKind}/existence [get] // @Security JWT func (h *PolicyTemplateHandler) ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) { diff --git a/internal/delivery/http/policy.go b/internal/delivery/http/policy.go index 93433286..972648c8 100644 --- a/internal/delivery/http/policy.go +++ b/internal/delivery/http/policy.go @@ -502,7 +502,7 @@ func (h *PolicyHandler) SetMandatoryPolicies(w http.ResponseWriter, r *http.Requ // @Produce json // @Param organizationId path string true "조직 식별자(o로 시작)" // @Param policyName path string true "정책 이름" -// @Success 200 {object} domain.ExistenceResponse +// @Success 200 {object} domain.CheckExistedResponse // @Router /organizations/{organizationId}/policies/name/{policyName}/existence [get] // @Security JWT func (h *PolicyHandler) ExistsPolicyName(w http.ResponseWriter, r *http.Request) { From 5454e10e031eeffa1dd7331ad3f637a38b59fbb0 Mon Sep 17 00:00:00 2001 From: sangkenlee Date: Fri, 22 Mar 2024 10:43:21 +0900 Subject: [PATCH 05/18] =?UTF-8?q?=EC=A0=95=EC=B1=85=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EB=93=B1=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/model/policy.go | 6 +++++- internal/repository/policy.go | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/model/policy.go b/internal/model/policy.go index 2514c860..9eb86264 100644 --- a/internal/model/policy.go +++ b/internal/model/policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "fmt" "github.com/google/uuid" "github.com/openinfradev/tks-api/pkg/domain" @@ -58,7 +59,10 @@ func (p *Policy) AfterFind(tx *gorm.DB) (err error) { if len(p.PolicyMatch) > 0 { // 목록 조회 시 에러가 발생해서 전체 조회가 실패하는 것을 방지하기 위해서 에러는 무시 - _ = json.Unmarshal([]byte(p.PolicyMatch), p.Match) + var match domain.Match + err = json.Unmarshal([]byte(p.PolicyMatch), &match) + p.Match = &match + fmt.Printf("!!!!!!!!!!!! err=%+v, p.Match=%+v p.PolicyMatch=%+v\n", err, p.Match, p.PolicyMatch) } p.TargetClusterIds = make([]string, len(p.TargetClusters)) diff --git a/internal/repository/policy.go b/internal/repository/policy.go index 4a59b2c6..9ceda1aa 100644 --- a/internal/repository/policy.go +++ b/internal/repository/policy.go @@ -94,7 +94,7 @@ func (r *PolicyRepository) Fetch(ctx context.Context, organizationId string, pg } func (r *PolicyRepository) ExistBy(ctx context.Context, organizationId string, key string, value interface{}) (exists bool, err error) { - query := fmt.Sprintf("organization_id = ? and %s = ?", value) + query := fmt.Sprintf("organization_id = ? and %s = ?", key) var policy model.Policy res := r.db.WithContext(ctx).Where(query, organizationId, value). @@ -114,15 +114,15 @@ func (r *PolicyRepository) ExistBy(ctx context.Context, organizationId string, k } func (r *PolicyRepository) ExistByName(ctx context.Context, organizationId string, policyName string) (exist bool, err error) { - return r.ExistBy(ctx, "policy_name", organizationId, policyName) + return r.ExistBy(ctx, organizationId, "policy_name", policyName) } func (r *PolicyRepository) ExistByID(ctx context.Context, organizationId string, policyId uuid.UUID) (exist bool, err error) { - return r.ExistBy(ctx, "id", organizationId, policyId) + return r.ExistBy(ctx, organizationId, "id", policyId) } func (r *PolicyRepository) GetBy(ctx context.Context, organizationId string, key string, value interface{}) (out *model.Policy, err error) { - query := fmt.Sprintf("organization_id = ? and %s = ?", value) + query := fmt.Sprintf("organization_id = ? and %s = ?", key) var policy model.Policy res := r.db.WithContext(ctx).Preload(clause.Associations). From 3f9f286a2a1a5d88ed6aac9ac924c1b90900aa07 Mon Sep 17 00:00:00 2001 From: sangkenlee Date: Sun, 24 Mar 2024 15:21:41 +0900 Subject: [PATCH 06/18] =?UTF-8?q?=EC=BF=A0=EB=B2=84=EB=84=A4=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=95=EC=B1=85=20=EA=B4=80=EB=A6=AC=20develop?= =?UTF-8?q?=20=EB=B8=8C=EB=9E=9C=EC=B9=98=20=EB=B3=91=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/model/policy.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/model/policy.go b/internal/model/policy.go index 9eb86264..1a05c5ef 100644 --- a/internal/model/policy.go +++ b/internal/model/policy.go @@ -2,7 +2,6 @@ package model import ( "encoding/json" - "fmt" "github.com/google/uuid" "github.com/openinfradev/tks-api/pkg/domain" @@ -62,7 +61,6 @@ func (p *Policy) AfterFind(tx *gorm.DB) (err error) { var match domain.Match err = json.Unmarshal([]byte(p.PolicyMatch), &match) p.Match = &match - fmt.Printf("!!!!!!!!!!!! err=%+v, p.Match=%+v p.PolicyMatch=%+v\n", err, p.Match, p.PolicyMatch) } p.TargetClusterIds = make([]string, len(p.TargetClusters)) From 9f6ec65188bf20620c3bbe3d943038717bee60c3 Mon Sep 17 00:00:00 2001 From: sangkenlee Date: Mon, 25 Mar 2024 13:44:37 +0900 Subject: [PATCH 07/18] =?UTF-8?q?Admin=20API=EB=93=A4Admin=5F=20Prefix=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/swagger/docs.go | 772 +++++++++++++++++- api/swagger/swagger.json | 772 +++++++++++++++++- api/swagger/swagger.yaml | 492 ++++++++++- internal/delivery/api/endpoint.go | 26 +- .../delivery/api/generated_endpoints.go.go | 156 ++-- internal/delivery/http/policy-template.go | 78 +- internal/middleware/audit/audit-map.go | 2 +- internal/route/route.go | 26 +- 8 files changed, 2106 insertions(+), 218 deletions(-) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 0b44751f..e3c7dc34 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -394,7 +394,7 @@ const docTemplate = `{ } } }, - "/admin/policytemplates": { + "/admin/policy-templates": { "get": { "security": [ { @@ -495,7 +495,7 @@ const docTemplate = `{ } } }, - "/admin/policytemplates/kind/{policyTemplateKind}/existence": { + "/admin/policy-templates/kind/{policyTemplateKind}/existence": { "get": { "security": [ { @@ -526,13 +526,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" } } } } }, - "/admin/policytemplates/name/{policyTemplateName}/existence": { + "/admin/policy-templates/name/{policyTemplateName}/existence": { "get": { "security": [ { @@ -569,7 +569,7 @@ const docTemplate = `{ } } }, - "/admin/policytemplates/{policyTemplateId}": { + "/admin/policy-templates/{policyTemplateId}": { "get": { "security": [ { @@ -679,7 +679,7 @@ const docTemplate = `{ } } }, - "/admin/policytemplates/{policyTemplateId}/deploy": { + "/admin/policy-templates/{policyTemplateId}/deploy": { "get": { "security": [ { @@ -716,7 +716,7 @@ const docTemplate = `{ } } }, - "/admin/policytemplates/{policyTemplateId}/statistics": { + "/admin/policy-templates/{policyTemplateId}/statistics": { "get": { "security": [ { @@ -753,7 +753,7 @@ const docTemplate = `{ } } }, - "/admin/policytemplates/{policyTemplateId}/versions": { + "/admin/policy-templates/{policyTemplateId}/versions": { "get": { "security": [ { @@ -834,7 +834,7 @@ const docTemplate = `{ } } }, - "/admin/policytemplates/{policyTemplateId}/versions/{version}": { + "/admin/policy-templates/{policyTemplateId}/versions/{version}": { "get": { "security": [ { @@ -3224,6 +3224,84 @@ const docTemplate = `{ } } }, + "/organizations/{organizationId}/mandatory-policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse" + } + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update mandatory policy/policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/organizations/{organizationId}/my-profile": { "get": { "security": [ @@ -3347,7 +3425,330 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Update user's password expired date to current date", + "description": "Update user's password expired date to current date", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user's password expired date to current date", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" + } + } + } + } + }, + "/organizations/{organizationId}/my-profile/password": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user password detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user password detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update user password request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ListPolicy] 정책 목록 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[CreatePolicy] 정책 생성", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create policy request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/name/{policyName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 이름을 가진 정책이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ExistsPolicyName] 정책 아름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 이름", + "name": "policyName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 정보를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicy] 정책 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[DeletePolicy] 정책 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다.", "consumes": [ "application/json" ], @@ -3355,39 +3756,49 @@ const docTemplate = `{ "application/json" ], "tags": [ - "My-profile" + "Policy" ], - "summary": "Update user's password expired date to current date", + "summary": "[UpdatePolicy] 정책을 업데이트", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "조직 식별자(o로 시작)", "name": "organizationId", "in": "path", "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest" + } } ], "responses": { "200": { "description": "OK" - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" - } } } } }, - "/organizations/{organizationId}/my-profile/password": { - "put": { + "/organizations/{organizationId}/policies/{policyId}/clusters": { + "patch": { "security": [ { "JWT": [] } ], - "description": "Update user password detail", + "description": "정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다.", "consumes": [ "application/json" ], @@ -3395,24 +3806,31 @@ const docTemplate = `{ "application/json" ], "tags": [ - "My-profile" + "Policy" ], - "summary": "Update user password detail", + "summary": "[UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "조직 식별자(o로 시작)", "name": "organizationId", "in": "path", "required": true }, { - "description": "update user password request", + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest" } } ], @@ -7385,7 +7803,7 @@ const docTemplate = `{ } } }, - "/policytemplates/rego-compile": { + "/policy-templates/rego-compile": { "post": { "security": [ { @@ -8693,6 +9111,63 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse": { "type": "object", "properties": { @@ -9260,9 +9735,6 @@ const docTemplate = `{ } } }, - "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse": { - "type": "object" - }, "github_com_openinfradev_tks-api_pkg_domain.FilterResponse": { "type": "object", "properties": { @@ -9559,6 +10031,17 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse": { + "type": "object", + "properties": { + "templates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse": { "type": "object", "properties": { @@ -9598,6 +10081,14 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse": { + "type": "object", + "properties": { + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse": { "type": "object", "properties": { @@ -10016,6 +10507,20 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse": { "type": "object", "properties": { @@ -10173,6 +10678,64 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "org 레이블 설정 여부 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + }, + "policyName": { + "type": "string", + "example": "org 레이블 요구" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo": { + "type": "object", + "properties": { + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo" + } + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse": { "type": "object", "required": [ @@ -10383,6 +10946,73 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyResponse": { + "type": "object", + "properties": { + "createdAt": { + "type": "string", + "format": "date-time" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse": { "type": "object", "properties": { @@ -10889,6 +11519,17 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse": { "type": "object", "properties": { @@ -11676,6 +12317,75 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest": { + "type": "object", + "properties": { + "currentTargetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "newTargetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest": { "type": "object", "properties": { diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 58f58a56..4011501e 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -388,7 +388,7 @@ } } }, - "/admin/policytemplates": { + "/admin/policy-templates": { "get": { "security": [ { @@ -489,7 +489,7 @@ } } }, - "/admin/policytemplates/kind/{policyTemplateKind}/existence": { + "/admin/policy-templates/kind/{policyTemplateKind}/existence": { "get": { "security": [ { @@ -520,13 +520,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" } } } } }, - "/admin/policytemplates/name/{policyTemplateName}/existence": { + "/admin/policy-templates/name/{policyTemplateName}/existence": { "get": { "security": [ { @@ -563,7 +563,7 @@ } } }, - "/admin/policytemplates/{policyTemplateId}": { + "/admin/policy-templates/{policyTemplateId}": { "get": { "security": [ { @@ -673,7 +673,7 @@ } } }, - "/admin/policytemplates/{policyTemplateId}/deploy": { + "/admin/policy-templates/{policyTemplateId}/deploy": { "get": { "security": [ { @@ -710,7 +710,7 @@ } } }, - "/admin/policytemplates/{policyTemplateId}/statistics": { + "/admin/policy-templates/{policyTemplateId}/statistics": { "get": { "security": [ { @@ -747,7 +747,7 @@ } } }, - "/admin/policytemplates/{policyTemplateId}/versions": { + "/admin/policy-templates/{policyTemplateId}/versions": { "get": { "security": [ { @@ -828,7 +828,7 @@ } } }, - "/admin/policytemplates/{policyTemplateId}/versions/{version}": { + "/admin/policy-templates/{policyTemplateId}/versions/{version}": { "get": { "security": [ { @@ -3218,6 +3218,84 @@ } } }, + "/organizations/{organizationId}/mandatory-policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse" + } + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update mandatory policy/policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/organizations/{organizationId}/my-profile": { "get": { "security": [ @@ -3341,7 +3419,330 @@ "JWT": [] } ], - "description": "Update user's password expired date to current date", + "description": "Update user's password expired date to current date", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user's password expired date to current date", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" + } + } + } + } + }, + "/organizations/{organizationId}/my-profile/password": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user password detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user password detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update user password request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ListPolicy] 정책 목록 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[CreatePolicy] 정책 생성", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create policy request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/name/{policyName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 이름을 가진 정책이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ExistsPolicyName] 정책 아름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 이름", + "name": "policyName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 정보를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicy] 정책 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[DeletePolicy] 정책 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다.", "consumes": [ "application/json" ], @@ -3349,39 +3750,49 @@ "application/json" ], "tags": [ - "My-profile" + "Policy" ], - "summary": "Update user's password expired date to current date", + "summary": "[UpdatePolicy] 정책을 업데이트", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "조직 식별자(o로 시작)", "name": "organizationId", "in": "path", "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest" + } } ], "responses": { "200": { "description": "OK" - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" - } } } } }, - "/organizations/{organizationId}/my-profile/password": { - "put": { + "/organizations/{organizationId}/policies/{policyId}/clusters": { + "patch": { "security": [ { "JWT": [] } ], - "description": "Update user password detail", + "description": "정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다.", "consumes": [ "application/json" ], @@ -3389,24 +3800,31 @@ "application/json" ], "tags": [ - "My-profile" + "Policy" ], - "summary": "Update user password detail", + "summary": "[UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "조직 식별자(o로 시작)", "name": "organizationId", "in": "path", "required": true }, { - "description": "update user password request", + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest" } } ], @@ -7379,7 +7797,7 @@ } } }, - "/policytemplates/rego-compile": { + "/policy-templates/rego-compile": { "post": { "security": [ { @@ -8687,6 +9105,63 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse": { "type": "object", "properties": { @@ -9254,9 +9729,6 @@ } } }, - "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse": { - "type": "object" - }, "github_com_openinfradev_tks-api_pkg_domain.FilterResponse": { "type": "object", "properties": { @@ -9553,6 +10025,17 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse": { + "type": "object", + "properties": { + "templates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse": { "type": "object", "properties": { @@ -9592,6 +10075,14 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse": { + "type": "object", + "properties": { + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse": { "type": "object", "properties": { @@ -10010,6 +10501,20 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse": { "type": "object", "properties": { @@ -10167,6 +10672,64 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "org 레이블 설정 여부 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + }, + "policyName": { + "type": "string", + "example": "org 레이블 요구" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo": { + "type": "object", + "properties": { + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo" + } + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse": { "type": "object", "required": [ @@ -10377,6 +10940,73 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyResponse": { + "type": "object", + "properties": { + "createdAt": { + "type": "string", + "format": "date-time" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse": { "type": "object", "properties": { @@ -10883,6 +11513,17 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse": { "type": "object", "properties": { @@ -11670,6 +12311,75 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest": { + "type": "object", + "properties": { + "currentTargetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "newTargetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest": { "type": "object", "properties": { diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index 71ac2318..3950feae 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -846,6 +846,45 @@ definitions: - adminEmail - name type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest: + properties: + description: + type: string + enforcementAction: + type: string + mandatory: + type: boolean + match: + additionalProperties: + type: string + example: + refer: match.Match + type: object + parameters: + example: '"labels":{"key":"owner","allowedRegex:^[a-zA-Z]+.agilebank.demo$}"' + type: string + policyName: + example: label 정책 + type: string + targetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + templateId: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + templateName: + example: 필수 Label 검사 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse: + properties: + id: + type: string + type: object github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse: properties: id: @@ -1232,8 +1271,6 @@ definitions: updatedAt: type: string type: object - github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse: - type: object github_com_openinfradev_tks-api_pkg_domain.FilterResponse: properties: column: @@ -1426,6 +1463,13 @@ definitions: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse' type: array type: object + github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse: + properties: + templates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo' + type: array + type: object github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse: properties: user: @@ -1451,6 +1495,11 @@ definitions: organization: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse: + properties: + policy: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse' + type: object github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse: properties: deployVersion: @@ -1719,6 +1768,15 @@ definitions: pagination: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' type: object + github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + policies: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse' + type: array + type: object github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse: properties: pagination: @@ -1822,6 +1880,46 @@ definitions: type: array type: object type: object + github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo: + properties: + description: + example: org 레이블 설정 여부 검사 + type: string + mandatory: + type: boolean + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 + type: string + policyName: + example: org 레이블 요구 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo: + properties: + mandatory: + type: boolean + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo: + properties: + description: + example: 파라미터로 설정된 레이블 검사 + type: string + mandatory: + type: boolean + policies: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo' + type: array + templateId: + example: 708d1e5b-4e6f-40e9-87a3-329e2fd051a5 + type: string + templateName: + example: 레이블 요구 + type: string + type: object github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse: properties: key: @@ -1960,6 +2058,53 @@ definitions: value: type: integer type: object + github_com_openinfradev_tks-api_pkg_domain.PolicyResponse: + properties: + createdAt: + format: date-time + type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + description: + type: string + enforcementAction: + type: string + id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + mandatory: + type: boolean + match: + additionalProperties: + type: string + example: + refer: match.Match + type: object + parameters: + example: '"labels":{"key":"owner","allowedRegex:^[a-zA-Z]+.agilebank.demo$}"' + type: string + policyName: + example: label 정책 + type: string + targetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + templateId: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + templateName: + example: 필수 Label 검사 + type: string + updatedAt: + format: date-time + type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + type: object github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse: properties: createdAt: @@ -2300,6 +2445,13 @@ definitions: taskId: type: string type: object + github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest: + properties: + policies: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo' + type: array + type: object github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse: properties: awsAccountId: @@ -2822,6 +2974,53 @@ definitions: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' type: array type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest: + properties: + currentTargetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + newTargetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest: + properties: + description: + type: string + enforcementAction: + type: string + mandatory: + type: boolean + match: + additionalProperties: + type: string + example: + refer: match.Match + type: object + parameters: + example: '"labels":{"key":"owner","allowedRegex:^[a-zA-Z]+.agilebank.demo$}"' + type: string + policyName: + example: label 정책 + type: string + targetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + templateId: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + type: object github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest: properties: deprecated: @@ -3397,7 +3596,7 @@ paths: summary: Update user by admin tags: - Admin - /admin/policytemplates: + /admin/policy-templates: get: consumes: - application/json @@ -3461,7 +3660,7 @@ paths: summary: '[CreatePolicyTemplate] 정책 템플릿 신규 생성' tags: - PolicyTemplate - /admin/policytemplates/{policyTemplateId}: + /admin/policy-templates/{policyTemplateId}: delete: consumes: - application/json @@ -3530,7 +3729,7 @@ paths: summary: '[UpdatePolicyTemplate] 정책 템플릿 업데이트' tags: - PolicyTemplate - /admin/policytemplates/{policyTemplateId}/deploy: + /admin/policy-templates/{policyTemplateId}/deploy: get: consumes: - application/json @@ -3553,7 +3752,7 @@ paths: summary: '[GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회' tags: - PolicyTemplate - /admin/policytemplates/{policyTemplateId}/statistics: + /admin/policy-templates/{policyTemplateId}/statistics: get: consumes: - application/json @@ -3577,7 +3776,7 @@ paths: summary: '[ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회' tags: - PolicyTemplate - /admin/policytemplates/{policyTemplateId}/versions: + /admin/policy-templates/{policyTemplateId}/versions: get: consumes: - application/json @@ -3628,7 +3827,7 @@ paths: summary: '[CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장' tags: - PolicyTemplate - /admin/policytemplates/{policyTemplateId}/versions/{version}: + /admin/policy-templates/{policyTemplateId}/versions/{version}: delete: consumes: - application/json @@ -3681,7 +3880,7 @@ paths: summary: '[GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회' tags: - PolicyTemplate - /admin/policytemplates/kind/{policyTemplateKind}/existence: + /admin/policy-templates/kind/{policyTemplateKind}/existence: get: consumes: - application/json @@ -3698,13 +3897,13 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' security: - JWT: [] summary: '[ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인' tags: - PolicyTemplate - /admin/policytemplates/name/{policyTemplateName}/existence: + /admin/policy-templates/name/{policyTemplateName}/existence: get: consumes: - application/json @@ -5171,6 +5370,55 @@ paths: summary: Update dashboard tags: - Dashboards + /organizations/{organizationId}/mandatory-policies: + get: + consumes: + - application/json + description: 템플릿, 정책이 필수 인지 여부를 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse' + security: + - JWT: [] + summary: '[GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회' + tags: + - Policy + patch: + consumes: + - application/json + description: 템플릿, 정책이 필수 인지 여부를 설정한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: update mandatory policy/policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정' + tags: + - Policy /organizations/{organizationId}/my-profile: delete: consumes: @@ -5296,6 +5544,226 @@ paths: summary: Update user password detail tags: - My-profile + /organizations/{organizationId}/policies: + get: + consumes: + - application/json + description: 정책 목록을 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: pageSize + in: query + name: limit + type: string + - description: pageNumber + in: query + name: page + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse' + security: + - JWT: [] + summary: '[ListPolicy] 정책 목록 조회' + tags: + - Policy + post: + consumes: + - application/json + description: 새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. + 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: create policy request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse' + security: + - JWT: [] + summary: '[CreatePolicy] 정책 생성' + tags: + - Policy + /organizations/{organizationId}/policies/{policyId}: + delete: + consumes: + - application/json + description: 정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[DeletePolicy] 정책 삭제' + tags: + - Policy + get: + consumes: + - application/json + description: 정책 정보를 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse' + security: + - JWT: [] + summary: '[GetPolicy] 정책 조회' + tags: + - Policy + patch: + consumes: + - application/json + description: 정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + - description: update policy set request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[UpdatePolicy] 정책을 업데이트' + tags: + - Policy + /organizations/{organizationId}/policies/{policyId}/clusters: + patch: + consumes: + - application/json + description: 정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 + 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + - description: update policy set request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정' + tags: + - Policy + /organizations/{organizationId}/policies/name/{policyName}/existence: + get: + consumes: + - application/json + description: 해당 이름을 가진 정책이 이미 존재하는지 확인한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 이름 + in: path + name: policyName + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' + security: + - JWT: [] + summary: '[ExistsPolicyName] 정책 아름 존재 여부 확인' + tags: + - Policy /organizations/{organizationId}/primary-cluster: patch: consumes: @@ -7860,7 +8328,7 @@ paths: summary: Get Permission Templates tags: - Permission - /policytemplates/rego-compile: + /policy-templates/rego-compile: post: consumes: - application/json diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 2364a2ea..e07171e6 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -211,19 +211,19 @@ const ( Admin_GetProjects // PolicyTemplate - ListPolicyTemplate - CreatePolicyTemplate - DeletePolicyTemplate - GetPolicyTemplate - UpdatePolicyTemplate - GetPolicyTemplateDeploy - ListPolicyTemplateStatistics - ListPolicyTemplateVersions - CreatePolicyTemplateVersion - DeletePolicyTemplateVersion - GetPolicyTemplateVersion - ExistsPolicyTemplateKind - ExistsPolicyTemplateName + Admin_ListPolicyTemplate + Admin_CreatePolicyTemplate + Admin_DeletePolicyTemplate + Admin_GetPolicyTemplate + Admin_UpdatePolicyTemplate + Admin_GetPolicyTemplateDeploy + Admin_ListPolicyTemplateStatistics + Admin_ListPolicyTemplateVersions + Admin_CreatePolicyTemplateVersion + Admin_DeletePolicyTemplateVersion + Admin_GetPolicyTemplateVersion + Admin_ExistsPolicyTemplateKind + Admin_ExistsPolicyTemplateName // ClusterPolicyStatus ListClusterPolicyStatus diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go index 891ac188..ee43f07f 100644 --- a/internal/delivery/api/generated_endpoints.go.go +++ b/internal/delivery/api/generated_endpoints.go.go @@ -635,56 +635,56 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "Admin_GetProjects", Group: "Admin Project", }, - ListPolicyTemplate: { - Name: "ListPolicyTemplate", + Admin_ListPolicyTemplate: { + Name: "Admin_ListPolicyTemplate", Group: "PolicyTemplate", }, - CreatePolicyTemplate: { - Name: "CreatePolicyTemplate", + Admin_CreatePolicyTemplate: { + Name: "Admin_CreatePolicyTemplate", Group: "PolicyTemplate", }, - DeletePolicyTemplate: { - Name: "DeletePolicyTemplate", + Admin_DeletePolicyTemplate: { + Name: "Admin_DeletePolicyTemplate", Group: "PolicyTemplate", }, - GetPolicyTemplate: { - Name: "GetPolicyTemplate", + Admin_GetPolicyTemplate: { + Name: "Admin_GetPolicyTemplate", Group: "PolicyTemplate", }, - UpdatePolicyTemplate: { - Name: "UpdatePolicyTemplate", + Admin_UpdatePolicyTemplate: { + Name: "Admin_UpdatePolicyTemplate", Group: "PolicyTemplate", }, - GetPolicyTemplateDeploy: { - Name: "GetPolicyTemplateDeploy", + Admin_GetPolicyTemplateDeploy: { + Name: "Admin_GetPolicyTemplateDeploy", Group: "PolicyTemplate", }, - ListPolicyTemplateStatistics: { - Name: "ListPolicyTemplateStatistics", + Admin_ListPolicyTemplateStatistics: { + Name: "Admin_ListPolicyTemplateStatistics", Group: "PolicyTemplate", }, - ListPolicyTemplateVersions: { - Name: "ListPolicyTemplateVersions", + Admin_ListPolicyTemplateVersions: { + Name: "Admin_ListPolicyTemplateVersions", Group: "PolicyTemplate", }, - CreatePolicyTemplateVersion: { - Name: "CreatePolicyTemplateVersion", + Admin_CreatePolicyTemplateVersion: { + Name: "Admin_CreatePolicyTemplateVersion", Group: "PolicyTemplate", }, - DeletePolicyTemplateVersion: { - Name: "DeletePolicyTemplateVersion", + Admin_DeletePolicyTemplateVersion: { + Name: "Admin_DeletePolicyTemplateVersion", Group: "PolicyTemplate", }, - GetPolicyTemplateVersion: { - Name: "GetPolicyTemplateVersion", + Admin_GetPolicyTemplateVersion: { + Name: "Admin_GetPolicyTemplateVersion", Group: "PolicyTemplate", }, - ExistsPolicyTemplateKind: { - Name: "ExistsPolicyTemplateKind", + Admin_ExistsPolicyTemplateKind: { + Name: "Admin_ExistsPolicyTemplateKind", Group: "PolicyTemplate", }, - ExistsPolicyTemplateName: { - Name: "ExistsPolicyTemplateName", + Admin_ExistsPolicyTemplateName: { + Name: "Admin_ExistsPolicyTemplateName", Group: "PolicyTemplate", }, ListClusterPolicyStatus: { @@ -1126,32 +1126,32 @@ func (e Endpoint) String() string { return "Admin_GetTksRole" case Admin_GetProjects: return "Admin_GetProjects" - case ListPolicyTemplate: - return "ListPolicyTemplate" - case CreatePolicyTemplate: - return "CreatePolicyTemplate" - case DeletePolicyTemplate: - return "DeletePolicyTemplate" - case GetPolicyTemplate: - return "GetPolicyTemplate" - case UpdatePolicyTemplate: - return "UpdatePolicyTemplate" - case GetPolicyTemplateDeploy: - return "GetPolicyTemplateDeploy" - case ListPolicyTemplateStatistics: - return "ListPolicyTemplateStatistics" - case ListPolicyTemplateVersions: - return "ListPolicyTemplateVersions" - case CreatePolicyTemplateVersion: - return "CreatePolicyTemplateVersion" - case DeletePolicyTemplateVersion: - return "DeletePolicyTemplateVersion" - case GetPolicyTemplateVersion: - return "GetPolicyTemplateVersion" - case ExistsPolicyTemplateKind: - return "ExistsPolicyTemplateKind" - case ExistsPolicyTemplateName: - return "ExistsPolicyTemplateName" + case Admin_ListPolicyTemplate: + return "Admin_ListPolicyTemplate" + case Admin_CreatePolicyTemplate: + return "Admin_CreatePolicyTemplate" + case Admin_DeletePolicyTemplate: + return "Admin_DeletePolicyTemplate" + case Admin_GetPolicyTemplate: + return "Admin_GetPolicyTemplate" + case Admin_UpdatePolicyTemplate: + return "Admin_UpdatePolicyTemplate" + case Admin_GetPolicyTemplateDeploy: + return "Admin_GetPolicyTemplateDeploy" + case Admin_ListPolicyTemplateStatistics: + return "Admin_ListPolicyTemplateStatistics" + case Admin_ListPolicyTemplateVersions: + return "Admin_ListPolicyTemplateVersions" + case Admin_CreatePolicyTemplateVersion: + return "Admin_CreatePolicyTemplateVersion" + case Admin_DeletePolicyTemplateVersion: + return "Admin_DeletePolicyTemplateVersion" + case Admin_GetPolicyTemplateVersion: + return "Admin_GetPolicyTemplateVersion" + case Admin_ExistsPolicyTemplateKind: + return "Admin_ExistsPolicyTemplateKind" + case Admin_ExistsPolicyTemplateName: + return "Admin_ExistsPolicyTemplateName" case ListClusterPolicyStatus: return "ListClusterPolicyStatus" case GetClusterPolicyTemplateStatus: @@ -1534,32 +1534,32 @@ func GetEndpoint(name string) Endpoint { return Admin_GetTksRole case "Admin_GetProjects": return Admin_GetProjects - case "ListPolicyTemplate": - return ListPolicyTemplate - case "CreatePolicyTemplate": - return CreatePolicyTemplate - case "DeletePolicyTemplate": - return DeletePolicyTemplate - case "GetPolicyTemplate": - return GetPolicyTemplate - case "UpdatePolicyTemplate": - return UpdatePolicyTemplate - case "GetPolicyTemplateDeploy": - return GetPolicyTemplateDeploy - case "ListPolicyTemplateStatistics": - return ListPolicyTemplateStatistics - case "ListPolicyTemplateVersions": - return ListPolicyTemplateVersions - case "CreatePolicyTemplateVersion": - return CreatePolicyTemplateVersion - case "DeletePolicyTemplateVersion": - return DeletePolicyTemplateVersion - case "GetPolicyTemplateVersion": - return GetPolicyTemplateVersion - case "ExistsPolicyTemplateKind": - return ExistsPolicyTemplateKind - case "ExistsPolicyTemplateName": - return ExistsPolicyTemplateName + case "Admin_ListPolicyTemplate": + return Admin_ListPolicyTemplate + case "Admin_CreatePolicyTemplate": + return Admin_CreatePolicyTemplate + case "Admin_DeletePolicyTemplate": + return Admin_DeletePolicyTemplate + case "Admin_GetPolicyTemplate": + return Admin_GetPolicyTemplate + case "Admin_UpdatePolicyTemplate": + return Admin_UpdatePolicyTemplate + case "Admin_GetPolicyTemplateDeploy": + return Admin_GetPolicyTemplateDeploy + case "Admin_ListPolicyTemplateStatistics": + return Admin_ListPolicyTemplateStatistics + case "Admin_ListPolicyTemplateVersions": + return Admin_ListPolicyTemplateVersions + case "Admin_CreatePolicyTemplateVersion": + return Admin_CreatePolicyTemplateVersion + case "Admin_DeletePolicyTemplateVersion": + return Admin_DeletePolicyTemplateVersion + case "Admin_GetPolicyTemplateVersion": + return Admin_GetPolicyTemplateVersion + case "Admin_ExistsPolicyTemplateKind": + return Admin_ExistsPolicyTemplateKind + case "Admin_ExistsPolicyTemplateName": + return Admin_ExistsPolicyTemplateName case "ListClusterPolicyStatus": return ListClusterPolicyStatus case "GetClusterPolicyTemplateStatus": diff --git a/internal/delivery/http/policy-template.go b/internal/delivery/http/policy-template.go index 27bb93ce..bc66d83b 100644 --- a/internal/delivery/http/policy-template.go +++ b/internal/delivery/http/policy-template.go @@ -24,19 +24,19 @@ type PolicyTemplateHandler struct { } type IPolicyTemplateHandler interface { - CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) - UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) - DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) - GetPolicyTemplate(w http.ResponseWriter, r *http.Request) - ListPolicyTemplate(w http.ResponseWriter, r *http.Request) - ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) - ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) - ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) - GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) - CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) - GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) - DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) - ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) + Admin_CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_GetPolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_ListPolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) + Admin_ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) + Admin_ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) + Admin_GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) + Admin_CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + Admin_GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + Admin_DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + Admin_ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) RegoCompile(w http.ResponseWriter, r *http.Request) } @@ -46,7 +46,7 @@ func NewPolicyTemplateHandler(u usecase.Usecase) IPolicyTemplateHandler { } } -// CreatePolicyTemplate godoc +// Admin_CreatePolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [CreatePolicyTemplate] 정책 템플릿 신규 생성 @@ -57,7 +57,7 @@ func NewPolicyTemplateHandler(u usecase.Usecase) IPolicyTemplateHandler { // @Success 200 {object} domain.CreatePolicyTemplateReponse // @Router /admin/policy-templates [post] // @Security JWT -func (h *PolicyTemplateHandler) CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) { input := domain.CreatePolicyTemplateRequest{} err := UnmarshalRequestInput(r, &input) @@ -84,7 +84,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplate(w http.ResponseWriter, r *h ResponseJSON(w, r, http.StatusOK, out) } -// UpdatePolicyTemplate godoc +// Admin_UpdatePolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [UpdatePolicyTemplate] 정책 템플릿 업데이트 @@ -96,7 +96,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplate(w http.ResponseWriter, r *h // @Success 200 {object} nil // @Router /admin/policy-templates/{policyTemplateId} [patch] // @Security JWT -func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -137,7 +137,7 @@ func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *h ResponseJSON(w, r, http.StatusOK, nil) } -// DeletePolicyTemplate godoc +// Admin_DeletePolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [DeletePolicyTemplate] 정책 템플릿 삭제 @@ -148,7 +148,7 @@ func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *h // @Success 200 {object} nil // @Router /admin/policy-templates/{policyTemplateId} [delete] // @Security JWT -func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -184,7 +184,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *h ResponseJSON(w, r, http.StatusOK, "") } -// GetPolicyTemplate godoc +// Admin_GetPolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [GetPolicyTemplate] 정책 템플릿 조회(최신 버전) @@ -195,7 +195,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *h // @Success 200 {object} domain.GetPolicyTemplateResponse // @Router /admin/policy-templates/{policyTemplateId} [get] // @Security JWT -func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -244,7 +244,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http ResponseJSON(w, r, http.StatusOK, out) } -// ListPolicyTemplate godoc +// Admin_ListPolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [ListPolicyTemplate] 정책 템플릿 목록 조회 @@ -259,7 +259,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http // @Success 200 {object} domain.ListPolicyTemplateResponse // @Router /admin/policy-templates [get] // @Security JWT -func (h *PolicyTemplateHandler) ListPolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplate(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() pg := pagination.NewPagination(&urlParams) @@ -290,7 +290,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplate(w http.ResponseWriter, r *htt ResponseJSON(w, r, http.StatusOK, out) } -// ListPolicyTemplateVersions godoc +// Admin_ListPolicyTemplateVersions godoc // // @Tags PolicyTemplate // @Summary [ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회 @@ -301,7 +301,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplate(w http.ResponseWriter, r *htt // @Success 200 {object} domain.ListPolicyTemplateVersionsResponse // @Router /admin/policy-templates/{policyTemplateId}/versions [get] // @Security JWT -func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] @@ -338,7 +338,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter ResponseJSON(w, r, http.StatusOK, out) } -// ListPolicyTemplateStatistics godoc +// Admin_ListPolicyTemplateStatistics godoc // // @Tags PolicyTemplate // @Summary [ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회 @@ -349,7 +349,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter // @Success 200 {object} domain.ListPolicyTemplateStatisticsResponse // @Router /admin/policy-templates/{policyTemplateId}/statistics [get] // @Security JWT -func (h *PolicyTemplateHandler) ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) { // result := domain.ListPolicyTemplateStatisticsResponse{ // PolicyTemplateStatistics: []domain.PolicyTemplateStatistics{ // { @@ -367,7 +367,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateStatistics(w http.ResponseWrit // util.JsonResponse(w, result) } -// GetPolicyTemplateDeploy godoc +// Admin_GetPolicyTemplateDeploy godoc // // @Tags PolicyTemplate // @Summary [GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회 @@ -378,7 +378,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateStatistics(w http.ResponseWrit // @Success 200 {object} domain.GetPolicyTemplateDeployResponse // @Router /admin/policy-templates/{policyTemplateId}/deploy [get] // @Security JWT -func (h *PolicyTemplateHandler) GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) { // c1 := util.UUIDGen() // c2 := util.UUIDGen() // c3 := util.UUIDGen() @@ -393,7 +393,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateDeploy(w http.ResponseWriter, r // util.JsonResponse(w, result) } -// GetPolicyTemplateVersion godoc +// Admin_GetPolicyTemplateVersion godoc // // @Tags PolicyTemplate // @Summary [GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회 @@ -405,7 +405,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateDeploy(w http.ResponseWriter, r // @Success 200 {object} domain.GetPolicyTemplateVersionResponse // @Router /admin/policy-templates/{policyTemplateId}/versions/{version} [get] // @Security JWT -func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -450,7 +450,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter, ResponseJSON(w, r, http.StatusOK, out) } -// CreatePolicyTemplateVersion godoc +// Admin_CreatePolicyTemplateVersion godoc // // @Tags PolicyTemplate // @Summary [CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장 @@ -462,7 +462,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter, // @Success 200 {object} domain.CreatePolicyTemplateVersionResponse // @Router /admin/policy-templates/{policyTemplateId}/versions [post] // @Security JWT -func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -526,7 +526,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWrite ResponseJSON(w, r, http.StatusOK, out) } -// DeletePolicyTemplateVersion godoc +// Admin_DeletePolicyTemplateVersion godoc // // @Tags PolicyTemplate // @Summary [DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제 @@ -538,7 +538,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWrite // @Success 200 {object} nil // @Router /admin/policy-templates/{policyTemplateId}/versions/{version} [delete] // @Security JWT -func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -575,7 +575,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWrite ResponseJSON(w, r, http.StatusOK, "") } -// ExistsPolicyTemplateName godoc +// Admin_ExistsPolicyTemplateName godoc // // @Tags PolicyTemplate // @Summary [ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인 @@ -586,7 +586,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWrite // @Success 200 {object} domain.CheckExistedResponse // @Router /admin/policy-templates/name/{policyTemplateName}/existence [get] // @Security JWT -func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateName, ok := vars["policyTemplateName"] if !ok { @@ -607,7 +607,7 @@ func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, ResponseJSON(w, r, http.StatusOK, out) } -// ExistsPolicyTemplateKind godoc +// Admin_ExistsPolicyTemplateKind godoc // // @Tags PolicyTemplate // @Summary [ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인 @@ -618,7 +618,7 @@ func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, // @Success 200 {object} domain.CheckExistedResponse // @Router /admin/policy-templates/kind/{policyTemplateKind}/existence [get] // @Security JWT -func (h *PolicyTemplateHandler) ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateKind, ok := vars["policyTemplateKind"] if !ok { diff --git a/internal/middleware/audit/audit-map.go b/internal/middleware/audit/audit-map.go index d7c70439..9029b11b 100644 --- a/internal/middleware/audit/audit-map.go +++ b/internal/middleware/audit/audit-map.go @@ -116,7 +116,7 @@ var auditMap = map[internalApi.Endpoint]fnAudit{ } else { return fmt.Sprintf("어드민 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) } - }, internalApi.CreatePolicyTemplate: func(ctx context.Context, out *bytes.Buffer, in []byte, statusCode int) (message string, description string) { + }, internalApi.Admin_CreatePolicyTemplate: func(ctx context.Context, out *bytes.Buffer, in []byte, statusCode int) (message string, description string) { input := domain.CreatePolicyTemplateRequest{} if err := json.Unmarshal(in, &input); err != nil { log.Error(ctx, err) diff --git a/internal/route/route.go b/internal/route/route.go index 8c73bb75..1bd7beb8 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -295,19 +295,19 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/permissions", customMiddleware.Handle(internalApi.UpdatePermissionsByRoleId, http.HandlerFunc(permissionHandler.UpdatePermissionsByRoleId))).Methods(http.MethodPut) policyTemplateHandler := delivery.NewPolicyTemplateHandler(usecaseFactory) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.ListPolicyTemplate, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplate))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.CreatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.CreatePolicyTemplate))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.DeletePolicyTemplate, http.HandlerFunc(policyTemplateHandler.DeletePolicyTemplate))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.GetPolicyTemplate, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplate))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.UpdatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.UpdatePolicyTemplate))).Methods(http.MethodPatch) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/deploy ", customMiddleware.Handle(internalApi.GetPolicyTemplateDeploy, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplateDeploy))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/statistics", customMiddleware.Handle(internalApi.ListPolicyTemplateStatistics, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplateStatistics))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.ListPolicyTemplateVersions, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplateVersions))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.CreatePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.CreatePolicyTemplateVersion))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.DeletePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.DeletePolicyTemplateVersion))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.GetPolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplateVersion))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/kind/{policyTemplateKind}/existence", customMiddleware.Handle(internalApi.ExistsPolicyTemplateKind, http.HandlerFunc(policyTemplateHandler.ExistsPolicyTemplateKind))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/name/{policyTemplateName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyTemplateName, http.HandlerFunc(policyTemplateHandler.ExistsPolicyTemplateName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.Admin_CreatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_CreatePolicyTemplate))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.Admin_DeletePolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_DeletePolicyTemplate))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.Admin_GetPolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_GetPolicyTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.Admin_UpdatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_UpdatePolicyTemplate))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/deploy ", customMiddleware.Handle(internalApi.Admin_GetPolicyTemplateDeploy, http.HandlerFunc(policyTemplateHandler.Admin_GetPolicyTemplateDeploy))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/statistics", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplateStatistics, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplateStatistics))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplateVersions, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplateVersions))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.Admin_CreatePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.Admin_CreatePolicyTemplateVersion))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.Admin_DeletePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.Admin_DeletePolicyTemplateVersion))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.Admin_GetPolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.Admin_GetPolicyTemplateVersion))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/kind/{policyTemplateKind}/existence", customMiddleware.Handle(internalApi.Admin_ExistsPolicyTemplateKind, http.HandlerFunc(policyTemplateHandler.Admin_ExistsPolicyTemplateKind))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/name/{policyTemplateName}/existence", customMiddleware.Handle(internalApi.Admin_ExistsPolicyTemplateName, http.HandlerFunc(policyTemplateHandler.Admin_ExistsPolicyTemplateName))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/policy-templates/rego-compile", customMiddleware.Handle(internalApi.CompileRego, http.HandlerFunc(policyTemplateHandler.RegoCompile))).Methods(http.MethodPost) policyHandler := delivery.NewPolicyHandler(usecaseFactory) From 82d1466b583cf8f204e84db654d3eac1a5775172 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 20 Mar 2024 17:33:47 +0900 Subject: [PATCH 08/18] re mapping permissions --- .../delivery/api/endpoints_permission_test.go | 70 +++ .../delivery/api/generated_endpoints.go.go | 8 - internal/model/permission.go | 585 +++++++++++++----- pkg/domain/endpoint.go | 8 +- pkg/domain/permission.go | 4 - 5 files changed, 513 insertions(+), 162 deletions(-) create mode 100644 internal/delivery/api/endpoints_permission_test.go diff --git a/internal/delivery/api/endpoints_permission_test.go b/internal/delivery/api/endpoints_permission_test.go new file mode 100644 index 00000000..e6033894 --- /dev/null +++ b/internal/delivery/api/endpoints_permission_test.go @@ -0,0 +1,70 @@ +package api_test + +import ( + "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/internal/model" + "testing" +) + +func TestEndpointsUsage(t *testing.T) { + var allEndpoints []string + for _, v := range api.ApiMap { + allEndpoints = append(allEndpoints, v.Name) + } + //allEndpoints := []Endpoint{ + // Login, Logout, RefreshToken, FindId, // 계속해서 모든 Endpoint 추가 + // // 나머지 Endpoint 상수들을 여기에 추가 + //} + usageCount := make(map[string]int) + ps := model.NewAdminPermissionSet() + + permissions := []*model.Permission{ + ps.Dashboard, + ps.Notification, + ps.Configuration, + ps.ProjectManagement, + ps.Stack, + ps.SecurityPolicy, + ps.Common, + ps.Admin, + } + + leafPermissions := make([]*model.Permission, 0) + + for _, perm := range permissions { + leafPermissions = model.GetEdgePermission(perm, leafPermissions, nil) + } + + // Permission 설정에서 Endpoint 사용 횟수 카운트 + for _, perm := range leafPermissions { + countEndpoints(perm, usageCount) + } + + var unusedEndpoints, duplicatedEndpoints []string + + // 미사용 또는 중복 사용된 Endpoint 확인 및 출력 + for _, endpoint := range allEndpoints { + count, exists := usageCount[endpoint] + if !exists { + unusedEndpoints = append(unusedEndpoints, endpoint) + } else if count > 1 { + duplicatedEndpoints = append(duplicatedEndpoints, endpoint) + } + } + + for _, endpoint := range unusedEndpoints { + t.Logf("Unused Endpoint: %s", endpoint) + } + + t.Logf("\n") + for _, endpoint := range duplicatedEndpoints { + t.Logf("Duplicated Endpoint: %s", endpoint) + } + +} + +func countEndpoints(perm *model.Permission, usageCount map[string]int) { + for _, endpoint := range perm.Endpoints { + usageCount[endpoint.Name]++ + } +} diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go index ee43f07f..16484548 100644 --- a/internal/delivery/api/generated_endpoints.go.go +++ b/internal/delivery/api/generated_endpoints.go.go @@ -35,10 +35,6 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "VerifyToken", Group: "Auth", }, - DeleteToken: { - Name: "DeleteToken", - Group: "Auth", - }, CreateUser: { Name: "CreateUser", Group: "User", @@ -826,8 +822,6 @@ func (e Endpoint) String() string { return "VerifyIdentityForLostPassword" case VerifyToken: return "VerifyToken" - case DeleteToken: - return "DeleteToken" case CreateUser: return "CreateUser" case ListUser: @@ -1234,8 +1228,6 @@ 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/model/permission.go b/internal/model/permission.go index 5025d766..746b1257 100644 --- a/internal/model/permission.go +++ b/internal/model/permission.go @@ -11,9 +11,9 @@ type PermissionKind string const ( DashBoardPermission PermissionKind = "대시보드" - StackPermission PermissionKind = "스택 관리" - SecurityPolicyPermission PermissionKind = "보안/정책 관리" - ProjectManagementPermission PermissionKind = "프로젝트 관리" + StackPermission PermissionKind = "스택" + SecurityPolicyPermission PermissionKind = "정책" + ProjectManagementPermission PermissionKind = "프로젝트" NotificationPermission PermissionKind = "알림" ConfigurationPermission PermissionKind = "설정" ) @@ -42,6 +42,8 @@ type PermissionSet struct { ProjectManagement *Permission `gorm:"-:all" json:"project_management,omitempty"` Notification *Permission `gorm:"-:all" json:"notification,omitempty"` Configuration *Permission `gorm:"-:all" json:"configuration,omitempty"` + Common *Permission `gorm:"-:all" json:"common,omitempty"` + Admin *Permission `gorm:"-:all" json:"admin,omitempty"` } func NewDefaultPermissionSet() *PermissionSet { @@ -52,6 +54,20 @@ func NewDefaultPermissionSet() *PermissionSet { ProjectManagement: newProjectManagement(), Notification: newNotification(), Configuration: newConfiguration(), + Common: newCommon(), + } +} + +func NewAdminPermissionSet() *PermissionSet { + return &PermissionSet{ + Admin: newAdmin(), + Dashboard: newDashboard(), + Stack: newStack(), + SecurityPolicy: newSecurityPolicy(), + ProjectManagement: newProjectManagement(), + Notification: newNotification(), + Configuration: newConfiguration(), + Common: newCommon(), } } @@ -101,84 +117,98 @@ func newDashboard() *Permission { api.GetResourcesDashboard, ), }, + { + ID: uuid.New(), + Name: "수정", + IsAllowed: helper.BoolP(false), + }, }, }, + }, + } + + return dashboard +} + +func newStack() *Permission { + stack := &Permission{ + ID: uuid.New(), + Name: string(StackPermission), + Children: []*Permission{ { ID: uuid.New(), - Name: "대시보드 설정", + Name: "스택", Children: []*Permission{ { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetStacks, + api.GetStack, + api.CheckStackName, + api.GetStackStatus, + api.GetStackKubeConfig, + + api.SetFavoriteStack, + api.DeleteFavoriteStack, + + // Cluster + api.GetCluster, + api.GetClusters, + api.GetClusterSiteValues, + api.GetBootstrapKubeconfig, + api.GetNodes, + + // AppGroup + api.GetAppgroups, + api.GetAppgroup, + api.GetApplications, + ), }, { ID: uuid.New(), Name: "생성", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateStack, + api.InstallStack, + api.CreateAppgroup, + + // Cluster + api.CreateCluster, + api.ImportCluster, + api.InstallCluster, + api.CreateBootstrapKubeconfig, + + // AppGroup + api.CreateAppgroup, + api.CreateApplication, + ), }, { ID: uuid.New(), Name: "수정", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateStack, + ), }, { ID: uuid.New(), Name: "삭제", IsAllowed: helper.BoolP(false), - }, - }, - }, - }, - } + Endpoints: endpointObjects( + api.DeleteStack, - return dashboard -} + // Cluster + api.DeleteCluster, -func newStack() *Permission { - stack := &Permission{ - ID: uuid.New(), - Name: string(StackPermission), - Children: []*Permission{ - { - ID: uuid.New(), - Name: "조회", - IsAllowed: helper.BoolP(false), - Endpoints: endpointObjects( - api.GetStacks, - api.GetStack, - api.CheckStackName, - api.GetStackStatus, - api.GetStackKubeConfig, - - api.SetFavoriteStack, - api.DeleteFavoriteStack, - ), - }, - { - ID: uuid.New(), - Name: "생성", - IsAllowed: helper.BoolP(false), - Endpoints: endpointObjects( - api.CreateStack, - api.InstallStack, - ), - }, - { - ID: uuid.New(), - Name: "수정", - IsAllowed: helper.BoolP(false), - Endpoints: endpointObjects( - api.UpdateStack, - ), - }, - { - ID: uuid.New(), - Name: "삭제", - IsAllowed: helper.BoolP(false), - Endpoints: endpointObjects( - api.DeleteStack, - ), + // AppGroup + api.DeleteAppgroup, + ), + }, + }, }, }, } @@ -193,27 +223,107 @@ func newSecurityPolicy() *Permission { Children: []*Permission{ { ID: uuid.New(), - Name: "보안/정책", + Name: "정책", Children: []*Permission{ { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.ListPolicyTemplate, + api.GetPolicyTemplate, + api.GetPolicyTemplateDeploy, + api.ListPolicyTemplateStatistics, + api.ListPolicyTemplateVersions, + api.GetPolicyTemplateVersion, + api.ExistsPolicyTemplateName, + api.ExistsPolicyTemplateKind, + + // ClusterPolicyStatus + api.ListClusterPolicyStatus, + api.GetClusterPolicyTemplateStatus, + + // Policy + api.GetMandatoryPolicies, + api.ListPolicy, + api.GetPolicy, + api.ExistsPolicyName, + + // OrganizationPolicyTemplate + api.ListOrganizationPolicyTemplate, + api.GetOrganizationPolicyTemplate, + api.GetOrganizationPolicyTemplateDeploy, + api.ListOrganizationPolicyTemplateStatistics, + api.ListOrganizationPolicyTemplateVersions, + api.GetOrganizationPolicyTemplateVersion, + api.ExistsOrganizationPolicyTemplateKind, + api.ExistsOrganizationPolicyTemplateName, + + // PolicyTemplateExample + api.ListPolicyTemplateExample, + api.GetPolicyTemplateExample, + ), }, { ID: uuid.New(), Name: "생성", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.CreatePolicyTemplate, + api.CreatePolicyTemplateVersion, + + // Policy + api.SetMandatoryPolicies, + api.CreatePolicy, + + // OrganizationPolicyTemplate + api.CreateOrganizationPolicyTemplate, + api.CreateOrganizationPolicyTemplateVersion, + ), }, { ID: uuid.New(), Name: "수정", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.UpdatePolicyTemplate, + + // ClusterPolicyStatus + api.UpdateClusterPolicyTemplateStatus, + + // Policy + api.UpdatePolicy, + api.UpdatePolicyTargetClusters, + + // OrganizationPolicyTemplate + api.UpdateOrganizationPolicyTemplate, + + // PolicyTemplateExample + api.UpdatePolicyTemplateExample, + ), }, { ID: uuid.New(), Name: "삭제", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.DeletePolicyTemplate, + api.DeletePolicyTemplateVersion, + + // Policy + api.DeletePolicy, + + // OrganizationPolicyTemplate + api.DeleteOrganizationPolicyTemplate, + api.DeleteOrganizationPolicyTemplateVersion, + + // PolicyTemplateExample + api.DeletePolicyTemplateExample, + ), }, }, }, @@ -223,77 +333,98 @@ func newSecurityPolicy() *Permission { return security_policy } -func newProjectManagement() *Permission { - projectManagement := &Permission{ +func newNotification() *Permission { + notification := &Permission{ ID: uuid.New(), - Name: string(ProjectManagementPermission), + Name: string(NotificationPermission), Children: []*Permission{ { ID: uuid.New(), - Name: "프로젝트", + Name: "시스템 알림", Children: []*Permission{ { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( - api.GetProjects, - api.GetProject, + api.GetSystemNotification, + api.GetSystemNotifications, ), }, { ID: uuid.New(), - Name: "생성", + Name: "수정", IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( - api.CreateProject, + api.UpdateSystemNotification, + api.CreateSystemNotificationAction, ), }, + { + ID: uuid.New(), + Name: "다운로드", + IsAllowed: helper.BoolP(false), + Children: []*Permission{}, + }, }, }, { ID: uuid.New(), - Name: "앱 서빙", + Name: "정책 알림", + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + IsAllowed: helper.BoolP(false), + Children: []*Permission{}, + }, + { + ID: uuid.New(), + Name: "다운로드", + IsAllowed: helper.BoolP(false), + Children: []*Permission{}, + }, + }, + }, + }, + } + + return notification +} + +func newProjectManagement() *Permission { + projectManagement := &Permission{ + ID: uuid.New(), + Name: string(ProjectManagementPermission), + Children: []*Permission{ + { + ID: uuid.New(), + Name: "프로젝트", Children: []*Permission{ { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( - api.GetAppServeApps, - api.GetAppServeApp, - api.GetNumOfAppsOnStack, - api.GetAppServeAppLatestTask, - api.IsAppServeAppExist, - api.IsAppServeAppNameExist, + api.GetProjects, + api.GetProject, + api.GetProjectKubeconfig, ), }, { ID: uuid.New(), - Name: "빌드", + Name: "생성", IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( - api.CreateAppServeApp, - api.IsAppServeAppExist, - api.IsAppServeAppNameExist, - api.UpdateAppServeApp, - api.UpdateAppServeAppEndpoint, - api.UpdateAppServeAppStatus, - api.RollbackAppServeApp, + api.CreateProject, ), }, { ID: uuid.New(), - Name: "배포", + Name: "수정", IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( - api.CreateAppServeApp, - api.IsAppServeAppExist, - api.IsAppServeAppNameExist, - api.UpdateAppServeApp, - api.UpdateAppServeAppEndpoint, - api.UpdateAppServeAppStatus, - api.RollbackAppServeApp, + api.UpdateProject, ), }, { @@ -301,14 +432,14 @@ func newProjectManagement() *Permission { Name: "삭제", IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( - api.DeleteAppServeApp, + api.DeleteProject, ), }, }, }, { ID: uuid.New(), - Name: "설정-일반", + Name: "일반 설정", Children: []*Permission{ { ID: uuid.New(), @@ -330,19 +461,11 @@ func newProjectManagement() *Permission { api.UpdateProject, ), }, - { - ID: uuid.New(), - Name: "삭제", - IsAllowed: helper.BoolP(false), - Endpoints: endpointObjects( - api.DeleteProject, - ), - }, }, }, { ID: uuid.New(), - Name: "설정-멤버", + Name: "구성원 설정", Children: []*Permission{ { ID: uuid.New(), @@ -383,7 +506,7 @@ func newProjectManagement() *Permission { }, { ID: uuid.New(), - Name: "설정-네임스페이스", + Name: "네임스페이스", Children: []*Permission{ { ID: uuid.New(), @@ -392,6 +515,7 @@ func newProjectManagement() *Permission { Endpoints: endpointObjects( api.GetProjectNamespaces, api.GetProjectNamespace, + api.GetProjectNamespaceK8sResources, ), }, { @@ -406,7 +530,9 @@ func newProjectManagement() *Permission { ID: uuid.New(), Name: "수정", IsAllowed: helper.BoolP(false), - Endpoints: endpointObjects(), + Endpoints: endpointObjects( + api.UpdateProjectNamespace, + ), }, { ID: uuid.New(), @@ -418,43 +544,67 @@ func newProjectManagement() *Permission { }, }, }, - }, - } - - return projectManagement -} - -func newNotification() *Permission { - notification := &Permission{ - ID: uuid.New(), - Name: string(NotificationPermission), - Children: []*Permission{ { ID: uuid.New(), - Name: "시스템 경고", + Name: "앱 서빙", Children: []*Permission{ { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetAppServeApps, + api.GetAppServeApp, + api.GetNumOfAppsOnStack, + api.GetAppServeAppLatestTask, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.GetAppServeAppTaskDetail, + api.GetAppServeAppTasksByAppId, + ), }, - }, - }, - { - ID: uuid.New(), - Name: "보안/정책 감사로그", - Children: []*Permission{ { ID: uuid.New(), - Name: "조회", + Name: "생성", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateAppServeApp, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.UpdateAppServeApp, + api.UpdateAppServeAppEndpoint, + api.UpdateAppServeAppStatus, + api.RollbackAppServeApp, + ), + }, + { + ID: uuid.New(), + Name: "수정", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateAppServeApp, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.UpdateAppServeApp, + api.UpdateAppServeAppEndpoint, + api.UpdateAppServeAppStatus, + api.RollbackAppServeApp, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteAppServeApp, + ), }, }, }, }, } - return notification + return projectManagement } func newConfiguration() *Permission { @@ -486,38 +636,44 @@ func newConfiguration() *Permission { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetCloudAccounts, + api.GetCloudAccount, + api.CheckCloudAccountName, + api.CheckAwsAccountId, + api.GetResourceQuota, + ), }, { ID: uuid.New(), Name: "생성", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateCloudAccount, + ), }, { ID: uuid.New(), Name: "수정", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateCloudAccount, + ), }, { ID: uuid.New(), Name: "삭제", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteCloudAccount, + api.DeleteForceCloudAccount, + ), }, }, }, { ID: uuid.New(), - Name: "스택 템플릿", - Children: []*Permission{ - { - ID: uuid.New(), - Name: "조회", - IsAllowed: helper.BoolP(false), - }, - }, - }, - { - ID: uuid.New(), - Name: "프로젝트 관리", + Name: "프로젝트", Children: []*Permission{ { ID: uuid.New(), @@ -529,16 +685,6 @@ func newConfiguration() *Permission { Name: "생성", IsAllowed: helper.BoolP(false), }, - { - ID: uuid.New(), - Name: "수정", - IsAllowed: helper.BoolP(false), - }, - { - ID: uuid.New(), - Name: "삭제", - IsAllowed: helper.BoolP(false), - }, }, }, { @@ -549,73 +695,120 @@ func newConfiguration() *Permission { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.ListUser, + api.GetUser, + api.CheckId, + api.CheckEmail, + ), }, { ID: uuid.New(), Name: "생성", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateUser, + api.CheckId, + api.CheckEmail, + ), }, { ID: uuid.New(), Name: "수정", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateUser, + api.ResetPassword, + ), }, { ID: uuid.New(), Name: "삭제", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteUser, + ), }, }, }, { ID: uuid.New(), - Name: "사용자 권한 관리", + Name: "역할 및 권한", Children: []*Permission{ { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.ListTksRoles, + api.GetTksRole, + api.GetPermissionsByRoleId, + api.GetPermissionTemplates, + ), }, { ID: uuid.New(), Name: "생성", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateTksRole, + ), }, { ID: uuid.New(), Name: "수정", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateTksRole, + api.UpdatePermissionsByRoleId, + ), }, { ID: uuid.New(), Name: "삭제", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteTksRole, + ), }, }, }, { ID: uuid.New(), - Name: "알림 설정", + Name: "시스템 알림", Children: []*Permission{ { ID: uuid.New(), Name: "조회", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetSystemNotificationRules, + api.GetSystemNotificationRule, + ), }, { ID: uuid.New(), Name: "생성", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateSystemNotificationRule, + ), }, { ID: uuid.New(), Name: "수정", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateSystemNotificationRule, + ), }, { ID: uuid.New(), Name: "삭제", IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteSystemNotificationRule, + ), }, }, }, @@ -625,6 +818,110 @@ func newConfiguration() *Permission { return configuration } +func newCommon() *Permission { + common := &Permission{ + ID: uuid.New(), + Name: "공통", + IsAllowed: helper.BoolP(true), + Endpoints: endpointObjects( + // Auth + api.Login, + api.Logout, + api.RefreshToken, + api.FindId, + api.FindPassword, + api.VerifyIdentityForLostId, + api.VerifyIdentityForLostPassword, + api.VerifyToken, + + // Stack + api.SetFavoriteStack, + api.DeleteFavoriteStack, + + // Project + api.SetFavoriteProject, + api.SetFavoriteProjectNamespace, + api.UnSetFavoriteProject, + api.UnSetFavoriteProjectNamespace, + + // MyProfile + api.GetMyProfile, + api.UpdateMyProfile, + api.UpdateMyPassword, + api.RenewPasswordExpiredDate, + api.DeleteMyProfile, + + // StackTemplate + api.GetOrganizationStackTemplates, + api.GetOrganizationStackTemplate, + + // Utiliy + api.CompileRego, + ), + } + + return common + +} + +func newAdmin() *Permission { + admin := &Permission{ + ID: uuid.New(), + Name: "관리자", + IsAllowed: helper.BoolP(true), + Endpoints: endpointObjects( + // Organization + api.Admin_CreateOrganization, + api.Admin_DeleteOrganization, + api.UpdateOrganization, + api.GetOrganization, + api.GetOrganizations, + api.UpdatePrimaryCluster, + api.CheckOrganizationName, + + // User + api.ResetPassword, + api.CheckId, + api.CheckEmail, + + // StackTemplate + api.Admin_GetStackTemplates, + api.Admin_GetStackTemplate, + api.Admin_GetStackTemplateServices, + api.Admin_CreateStackTemplate, + api.Admin_UpdateStackTemplate, + api.Admin_DeleteStackTemplate, + api.Admin_UpdateStackTemplateOrganizations, + api.Admin_CheckStackTemplateName, + + // Admin + api.Admin_GetUser, + api.Admin_ListUser, + api.Admin_CreateUser, + api.Admin_UpdateUser, + api.Admin_DeleteUser, + api.Admin_GetSystemNotificationTemplate, + api.Admin_CreateSystemNotificationTemplate, + api.Admin_ListUser, + api.Admin_GetTksRole, + api.Admin_GetProjects, + api.Admin_UpdateSystemNotificationTemplate, + api.Admin_ListTksRoles, + api.Admin_GetSystemNotificationTemplates, + + // Audit + api.GetAudits, + api.GetAudit, + api.DeleteAudit, + + api.CreateSystemNotification, + api.DeleteSystemNotification, + ), + } + + return admin +} + func (p *PermissionSet) SetAllowedPermissionSet() { edgePermissions := make([]*Permission, 0) edgePermissions = append(edgePermissions, GetEdgePermission(p.Dashboard, edgePermissions, nil)...) diff --git a/pkg/domain/endpoint.go b/pkg/domain/endpoint.go index e2a2127d..33acdc92 100644 --- a/pkg/domain/endpoint.go +++ b/pkg/domain/endpoint.go @@ -1,10 +1,6 @@ package domain -import "time" - type EndpointResponse struct { - Name string `json:"name"` - Group string `json:"group"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + Name string `json:"name"` + Group string `json:"group"` } diff --git a/pkg/domain/permission.go b/pkg/domain/permission.go index f7b69f40..8be227a2 100644 --- a/pkg/domain/permission.go +++ b/pkg/domain/permission.go @@ -8,11 +8,7 @@ type PermissionResponse struct { ID uuid.UUID `json:"ID"` Name string `json:"name"` IsAllowed *bool `json:"is_allowed,omitempty"` - RoleID *string `json:"role_id,omitempty"` - Role *RoleResponse `json:"role,omitempty"` Endpoints []*EndpointResponse `json:"endpoints,omitempty"` - ParentID *uuid.UUID `json:"parent_id,omitempty"` - Parent *PermissionResponse `json:"parent,omitempty"` Children []*PermissionResponse `json:"children,omitempty"` } From a2c09a6e0df1e5fc4e18709833064f20b62e4442 Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 22 Mar 2024 17:41:55 +0900 Subject: [PATCH 09/18] change permission API --- internal/delivery/api/endpoint.go | 1 + .../delivery/api/endpoints_permission_test.go | 2 +- .../delivery/api/generated_endpoints.go.go | 16 ++ internal/delivery/http/permission.go | 168 +++++++++++++++--- internal/model/permission.go | 153 +++++++++++++--- internal/route/route.go | 1 + internal/usecase/permission.go | 44 ++++- pkg/domain/permission.go | 60 +++++-- 8 files changed, 379 insertions(+), 66 deletions(-) diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index e07171e6..c5874faf 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -195,6 +195,7 @@ const ( GetPermissionTemplates GetPermissionsByRoleId UpdatePermissionsByRoleId + GetPermissionsByAccountId // Admin_User Admin_CreateUser diff --git a/internal/delivery/api/endpoints_permission_test.go b/internal/delivery/api/endpoints_permission_test.go index e6033894..b012f456 100644 --- a/internal/delivery/api/endpoints_permission_test.go +++ b/internal/delivery/api/endpoints_permission_test.go @@ -24,7 +24,7 @@ func TestEndpointsUsage(t *testing.T) { ps.Configuration, ps.ProjectManagement, ps.Stack, - ps.SecurityPolicy, + ps.Policy, ps.Common, ps.Admin, } diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go index 16484548..b4b8549e 100644 --- a/internal/delivery/api/generated_endpoints.go.go +++ b/internal/delivery/api/generated_endpoints.go.go @@ -35,6 +35,10 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "VerifyToken", Group: "Auth", }, + DeleteToken: { + Name: "DeleteToken", + Group: "Auth", + }, CreateUser: { Name: "CreateUser", Group: "User", @@ -599,6 +603,10 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "UpdatePermissionsByRoleId", Group: "Permission", }, + GetPermissionsByAccountId: { + Name: "GetPermissionsByAccountId", + Group: "Permission", + }, Admin_CreateUser: { Name: "Admin_CreateUser", Group: "Admin_User", @@ -822,6 +830,8 @@ func (e Endpoint) String() string { return "VerifyIdentityForLostPassword" case VerifyToken: return "VerifyToken" + case DeleteToken: + return "DeleteToken" case CreateUser: return "CreateUser" case ListUser: @@ -1104,6 +1114,8 @@ func (e Endpoint) String() string { return "GetPermissionsByRoleId" case UpdatePermissionsByRoleId: return "UpdatePermissionsByRoleId" + case GetPermissionsByAccountId: + return "GetPermissionsByAccountId" case Admin_CreateUser: return "Admin_CreateUser" case Admin_ListUser: @@ -1228,6 +1240,8 @@ func GetEndpoint(name string) Endpoint { return VerifyIdentityForLostPassword case "VerifyToken": return VerifyToken + case "DeleteToken": + return DeleteToken case "CreateUser": return CreateUser case "ListUser": @@ -1510,6 +1524,8 @@ func GetEndpoint(name string) Endpoint { return GetPermissionsByRoleId case "UpdatePermissionsByRoleId": return UpdatePermissionsByRoleId + case "GetPermissionsByAccountId": + return GetPermissionsByAccountId case "Admin_CreateUser": return Admin_CreateUser case "Admin_ListUser": diff --git a/internal/delivery/http/permission.go b/internal/delivery/http/permission.go index 9e660850..8ec9b7e9 100644 --- a/internal/delivery/http/permission.go +++ b/internal/delivery/http/permission.go @@ -1,15 +1,14 @@ package http import ( + "context" "net/http" "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/model" - "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" ) type IPermissionHandler interface { @@ -20,11 +19,13 @@ type IPermissionHandler interface { type PermissionHandler struct { permissionUsecase usecase.IPermissionUsecase + userUsecase usecase.IUserUsecase } func NewPermissionHandler(usecase usecase.Usecase) *PermissionHandler { return &PermissionHandler{ permissionUsecase: usecase.Permission, + userUsecase: usecase.User, } } @@ -41,20 +42,110 @@ func NewPermissionHandler(usecase usecase.Usecase) *PermissionHandler { func (h PermissionHandler) GetPermissionTemplates(w http.ResponseWriter, r *http.Request) { permissionSet := model.NewDefaultPermissionSet() - var premissionSetResponse domain.PermissionSetResponse - if err := serializer.Map(r.Context(), permissionSet, &premissionSetResponse); err != nil { - log.Info(r.Context(), err) + var out domain.GetPermissionTemplatesResponse + out.Permissions = new(domain.PermissionTemplateResponse) + + out.Permissions.Dashboard = convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Dashboard) + out.Permissions.Stack = convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Stack) + out.Permissions.Policy = convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Policy) + out.Permissions.ProjectManagement = convertModelToPermissionTemplateResponse(r.Context(), permissionSet.ProjectManagement) + out.Permissions.Notification = convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Notification) + out.Permissions.Configuration = convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Configuration) + + ResponseJSON(w, r, http.StatusOK, out) +} + +func convertModelToPermissionTemplateResponse(ctx context.Context, permission *model.Permission) *domain.TemplateResponse { + var permissionResponse domain.TemplateResponse + + permissionResponse.Key = permission.Key + permissionResponse.Name = permission.Name + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed } - var out domain.GetPermissionTemplatesResponse - out.Permissions = append(out.Permissions, premissionSetResponse.Dashboard) - out.Permissions = append(out.Permissions, premissionSetResponse.Stack) - out.Permissions = append(out.Permissions, premissionSetResponse.SecurityPolicy) - out.Permissions = append(out.Permissions, premissionSetResponse.ProjectManagement) - out.Permissions = append(out.Permissions, premissionSetResponse.Notification) - out.Permissions = append(out.Permissions, premissionSetResponse.Configuration) + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToPermissionTemplateResponse(ctx, child)) + } + + return &permissionResponse +} + +// GetPermissionsByAccountId godoc +// +// @Tags Permission +// @Summary Get Permissions By Account ID +// @Description Get Permissions By Account ID +// @Accept json +// @Produce json +// @Success 200 {object} domain.GetUsersPermissionsResponse +// @Router /organizations/{organizationId}/users/{accountId}/permissions [get] +// @Security JWT +func (h PermissionHandler) GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) { + var organizationId, accountId string + + vars := mux.Vars(r) + if v, ok := vars["accountId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + accountId = v + } + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + user, err := h.userUsecase.GetByAccountId(r.Context(), accountId, organizationId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + var roles []*model.Role + roles = append(roles, &user.Role) + + var permissionSets []*model.PermissionSet + for _, role := range roles { + permissionSet, err := h.permissionUsecase.GetPermissionSetByRoleId(r.Context(), role.ID) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + permissionSets = append(permissionSets, permissionSet) + } + + mergedPermissionSet := h.permissionUsecase.MergePermissionWithOrOperator(r.Context(), permissionSets...) + + var permissions domain.MergedPermissionSetResponse + permissions.Dashboard = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Dashboard) + permissions.Stack = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Stack) + permissions.Policy = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Policy) + permissions.ProjectManagement = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.ProjectManagement) + permissions.Notification = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Notification) + permissions.Configuration = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Configuration) + + var out domain.GetUsersPermissionsResponse + out.Permissions = &permissions ResponseJSON(w, r, http.StatusOK, out) + +} + +func convertModelToMergedPermissionSetResponse(ctx context.Context, permission *model.Permission) *domain.MergePermissionResponse { + var permissionResponse domain.MergePermissionResponse + + permissionResponse.Key = permission.Key + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed + } + + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToMergedPermissionSetResponse(ctx, child)) + } + + return &permissionResponse } // GetPermissionsByRoleId godoc @@ -85,22 +176,50 @@ func (h PermissionHandler) GetPermissionsByRoleId(w http.ResponseWriter, r *http return } - var premissionSetResponse domain.PermissionSetResponse - if err := serializer.Map(r.Context(), permissionSet, &premissionSetResponse); err != nil { - log.Info(r.Context(), err) - } + var permissionSetResponse domain.PermissionSetResponse + permissionSetResponse.Dashboard = convertModelToPermissionResponse(r.Context(), permissionSet.Dashboard) + permissionSetResponse.Stack = convertModelToPermissionResponse(r.Context(), permissionSet.Stack) + permissionSetResponse.Policy = convertModelToPermissionResponse(r.Context(), permissionSet.Policy) + permissionSetResponse.ProjectManagement = convertModelToPermissionResponse(r.Context(), permissionSet.ProjectManagement) + permissionSetResponse.Notification = convertModelToPermissionResponse(r.Context(), permissionSet.Notification) + permissionSetResponse.Configuration = convertModelToPermissionResponse(r.Context(), permissionSet.Configuration) var out domain.GetPermissionsByRoleIdResponse - out.Permissions = append(out.Permissions, premissionSetResponse.Dashboard) - out.Permissions = append(out.Permissions, premissionSetResponse.Stack) - out.Permissions = append(out.Permissions, premissionSetResponse.SecurityPolicy) - out.Permissions = append(out.Permissions, premissionSetResponse.ProjectManagement) - out.Permissions = append(out.Permissions, premissionSetResponse.Notification) - out.Permissions = append(out.Permissions, premissionSetResponse.Configuration) + out.Permissions = &permissionSetResponse ResponseJSON(w, r, http.StatusOK, out) } +func convertModelToPermissionResponse(ctx context.Context, permission *model.Permission) *domain.PermissionResponse { + var permissionResponse domain.PermissionResponse + + permissionResponse.ID = permission.ID + permissionResponse.Key = permission.Key + permissionResponse.Name = permission.Name + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed + } + + for _, endpoint := range permission.Endpoints { + permissionResponse.Endpoints = append(permissionResponse.Endpoints, convertModelToEndpointResponse(ctx, endpoint)) + } + + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToPermissionResponse(ctx, child)) + } + + return &permissionResponse +} + +func convertModelToEndpointResponse(ctx context.Context, endpoint *model.Endpoint) *domain.EndpointResponse { + var endpointResponse domain.EndpointResponse + + endpointResponse.Name = endpoint.Name + endpointResponse.Group = endpoint.Group + + return &endpointResponse +} + // UpdatePermissionsByRoleId godoc // // @Tags Permission @@ -124,9 +243,8 @@ func (h PermissionHandler) UpdatePermissionsByRoleId(w http.ResponseWriter, r *h for _, permissionResponse := range input.Permissions { var permission model.Permission - if err := serializer.Map(r.Context(), permissionResponse, &permission); err != nil { - log.Info(r.Context(), err) - } + permission.ID = permissionResponse.ID + permission.IsAllowed = permissionResponse.IsAllowed if err := h.permissionUsecase.UpdatePermission(r.Context(), &permission); err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) diff --git a/internal/model/permission.go b/internal/model/permission.go index 746b1257..87e06b6e 100644 --- a/internal/model/permission.go +++ b/internal/model/permission.go @@ -10,12 +10,42 @@ import ( type PermissionKind string const ( - DashBoardPermission PermissionKind = "대시보드" - StackPermission PermissionKind = "스택" - SecurityPolicyPermission PermissionKind = "정책" - ProjectManagementPermission PermissionKind = "프로젝트" - NotificationPermission PermissionKind = "알림" - ConfigurationPermission PermissionKind = "설정" + DashBoardPermission PermissionKind = "대시보드" + StackPermission PermissionKind = "스택" + PolicyPermission PermissionKind = "정책" + ProjectPermission PermissionKind = "프로젝트" + NotificationPermission PermissionKind = "알림" + ConfigurationPermission PermissionKind = "설정" + + OperationRead = "READ" + OperationCreate = "CREATE" + OperationUpdate = "UPDATE" + OperationDelete = "DELETE" + OperationDownload = "DOWNLOAD" + + // Key + TopDashboardKey = "DASHBOARD" + MiddleDashboardKey = "DASHBOARD-DASHBOARD" + TopStackKey = "STACK" + MiddleStackKey = "STACK-STACK" + TopPolicyKey = "POLICY" + MiddlePolicyKey = "POLICY-POLICY" + TopNotificationKey = "NOTIFICATION" + MiddleNotificationKey = "NOTIFICATION-SYSTEM_NOTIFICATION" + MiddlePolicyNotificationKey = "NOTIFICATION-POLICY_NOTIFICATION" + TopProjectKey = "PROJECT" + MiddleProjectKey = "PROJECT-PROJECT_LIST" + MiddleProjectCommonConfigurationKey = "PROJECT-PROJECT_COMMON_CONFIGURATION" + MiddleProjectMemberConfigurationKey = "PROJECT-PROJECT_MEMBER_CONFIGURATION" + MiddleProjectNamespaceKey = "PROJECT-PROJECT_NAMESPACE" + MiddleProjectAppServeKey = "PROJECT-PROJECT_APP_SERVE" + TopConfigurationKey = "CONFIGURATION" + MiddleConfigurationKey = "CONFIGURATION-CONFIGURATION" + MiddleConfigurationCloudAccountKey = "CONFIGURATION-CLOUD_ACCOUNT" + MiddleConfigurationProjectKey = "CONFIGURATION-PROJECT" + MiddleConfigurationUserKey = "CONFIGURATION-USER" + MiddleConfigurationRoleKey = "CONFIGURATION-ROLE" + MiddleConfigurationSystemNotificationKey = "CONFIGURATION-SYSTEM_NOTIFICATION" ) type Permission struct { @@ -23,6 +53,7 @@ type Permission struct { ID uuid.UUID `gorm:"primarykey;type:uuid;" json:"ID"` Name string `json:"name"` + Key string `gorm:"type:text;" json:"key,omitempty"` IsAllowed *bool `gorm:"type:boolean;" json:"is_allowed,omitempty"` RoleID *string `json:"role_id,omitempty"` @@ -38,7 +69,7 @@ type Permission struct { type PermissionSet struct { Dashboard *Permission `gorm:"-:all" json:"dashboard,omitempty"` Stack *Permission `gorm:"-:all" json:"stack,omitempty"` - SecurityPolicy *Permission `gorm:"-:all" json:"security_policy,omitempty"` + Policy *Permission `gorm:"-:all" json:"policy,omitempty"` ProjectManagement *Permission `gorm:"-:all" json:"project_management,omitempty"` Notification *Permission `gorm:"-:all" json:"notification,omitempty"` Configuration *Permission `gorm:"-:all" json:"configuration,omitempty"` @@ -50,11 +81,12 @@ func NewDefaultPermissionSet() *PermissionSet { return &PermissionSet{ Dashboard: newDashboard(), Stack: newStack(), - SecurityPolicy: newSecurityPolicy(), - ProjectManagement: newProjectManagement(), + Policy: newPolicy(), + ProjectManagement: newProject(), Notification: newNotification(), Configuration: newConfiguration(), Common: newCommon(), + Admin: nil, } } @@ -63,8 +95,8 @@ func NewAdminPermissionSet() *PermissionSet { Admin: newAdmin(), Dashboard: newDashboard(), Stack: newStack(), - SecurityPolicy: newSecurityPolicy(), - ProjectManagement: newProjectManagement(), + Policy: newPolicy(), + ProjectManagement: newProject(), Notification: newNotification(), Configuration: newConfiguration(), Common: newCommon(), @@ -101,14 +133,17 @@ func newDashboard() *Permission { dashboard := &Permission{ ID: uuid.New(), Name: string(DashBoardPermission), + Key: TopDashboardKey, Children: []*Permission{ { ID: uuid.New(), Name: "대시보드", + Key: MiddleDashboardKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetChartsDashboard, @@ -120,6 +155,7 @@ func newDashboard() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), }, }, @@ -134,14 +170,17 @@ func newStack() *Permission { stack := &Permission{ ID: uuid.New(), Name: string(StackPermission), + Key: TopStackKey, Children: []*Permission{ { ID: uuid.New(), Name: "스택", + Key: MiddleStackKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetStacks, @@ -169,6 +208,7 @@ func newStack() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateStack, @@ -189,6 +229,7 @@ func newStack() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateStack, @@ -197,6 +238,7 @@ func newStack() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteStack, @@ -216,18 +258,21 @@ func newStack() *Permission { return stack } -func newSecurityPolicy() *Permission { - security_policy := &Permission{ +func newPolicy() *Permission { + policy := &Permission{ ID: uuid.New(), - Name: string(SecurityPolicyPermission), + Name: string(PolicyPermission), + Key: TopPolicyKey, Children: []*Permission{ { ID: uuid.New(), Name: "정책", + Key: MiddlePolicyKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate @@ -268,6 +313,7 @@ func newSecurityPolicy() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate @@ -286,6 +332,7 @@ func newSecurityPolicy() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate @@ -308,6 +355,7 @@ func newSecurityPolicy() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate @@ -330,21 +378,24 @@ func newSecurityPolicy() *Permission { }, } - return security_policy + return policy } func newNotification() *Permission { notification := &Permission{ ID: uuid.New(), Name: string(NotificationPermission), + Key: TopNotificationKey, Children: []*Permission{ { ID: uuid.New(), Name: "시스템 알림", + Key: MiddleNotificationKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetSystemNotification, @@ -354,6 +405,7 @@ func newNotification() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateSystemNotification, @@ -363,6 +415,7 @@ func newNotification() *Permission { { ID: uuid.New(), Name: "다운로드", + Key: OperationDownload, IsAllowed: helper.BoolP(false), Children: []*Permission{}, }, @@ -371,16 +424,19 @@ func newNotification() *Permission { { ID: uuid.New(), Name: "정책 알림", + Key: MiddlePolicyNotificationKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Children: []*Permission{}, }, { ID: uuid.New(), Name: "다운로드", + Key: OperationDownload, IsAllowed: helper.BoolP(false), Children: []*Permission{}, }, @@ -392,18 +448,21 @@ func newNotification() *Permission { return notification } -func newProjectManagement() *Permission { - projectManagement := &Permission{ +func newProject() *Permission { + project := &Permission{ ID: uuid.New(), - Name: string(ProjectManagementPermission), + Name: string(ProjectPermission), + Key: TopProjectKey, Children: []*Permission{ { ID: uuid.New(), - Name: "프로젝트", + Name: "프로젝트 목록", + Key: MiddleProjectKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetProjects, @@ -414,6 +473,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateProject, @@ -422,6 +482,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateProject, @@ -430,6 +491,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteProject, @@ -440,10 +502,12 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "일반 설정", + Key: MiddleProjectCommonConfigurationKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetProjects, @@ -456,6 +520,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateProject, @@ -466,10 +531,12 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "구성원 설정", + Key: MiddleProjectMemberConfigurationKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetProjectMembers, @@ -481,6 +548,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.AddProjectMember, @@ -489,6 +557,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateProjectMemberRole, @@ -497,6 +566,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.RemoveProjectMember, @@ -507,10 +577,12 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "네임스페이스", + Key: MiddleProjectNamespaceKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetProjectNamespaces, @@ -521,6 +593,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateProjectNamespace, @@ -529,6 +602,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateProjectNamespace, @@ -537,6 +611,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteProjectNamespace, @@ -547,10 +622,12 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "앱 서빙", + Key: MiddleProjectAppServeKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetAppServeApps, @@ -566,6 +643,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateAppServeApp, @@ -580,6 +658,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateAppServeApp, @@ -594,6 +673,7 @@ func newProjectManagement() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteAppServeApp, @@ -604,26 +684,30 @@ func newProjectManagement() *Permission { }, } - return projectManagement + return project } func newConfiguration() *Permission { configuration := &Permission{ ID: uuid.New(), Name: string(ConfigurationPermission), + Key: TopConfigurationKey, Children: []*Permission{ { ID: uuid.New(), Name: "일반", + Key: MiddleConfigurationKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), }, { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), }, }, @@ -631,10 +715,12 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "클라우드 계정", + Key: MiddleConfigurationCloudAccountKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetCloudAccounts, @@ -647,6 +733,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateCloudAccount, @@ -655,6 +742,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateCloudAccount, @@ -663,6 +751,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteCloudAccount, @@ -674,15 +763,18 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "프로젝트", + Key: MiddleConfigurationProjectKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), }, { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), }, }, @@ -690,10 +782,12 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "사용자", + Key: MiddleConfigurationUserKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.ListUser, @@ -705,6 +799,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateUser, @@ -715,6 +810,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateUser, @@ -724,6 +820,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteUser, @@ -734,10 +831,12 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "역할 및 권한", + Key: MiddleConfigurationRoleKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.ListTksRoles, @@ -749,6 +848,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateTksRole, @@ -757,6 +857,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateTksRole, @@ -766,6 +867,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteTksRole, @@ -776,10 +878,12 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "시스템 알림", + Key: MiddleConfigurationSystemNotificationKey, Children: []*Permission{ { ID: uuid.New(), Name: "조회", + Key: OperationRead, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.GetSystemNotificationRules, @@ -789,6 +893,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "생성", + Key: OperationCreate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.CreateSystemNotificationRule, @@ -797,6 +902,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "수정", + Key: OperationUpdate, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.UpdateSystemNotificationRule, @@ -805,6 +911,7 @@ func newConfiguration() *Permission { { ID: uuid.New(), Name: "삭제", + Key: OperationDelete, IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( api.DeleteSystemNotificationRule, @@ -926,7 +1033,7 @@ 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.Policy, 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)...) @@ -943,7 +1050,7 @@ func (p *PermissionSet) SetUserPermissionSet() { 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.Policy, 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)...) @@ -956,7 +1063,7 @@ func (p *PermissionSet) SetUserPermissionSet() { func (p *PermissionSet) SetRoleId(roleId string) { setRoleIdToPermission(p.Dashboard, roleId) setRoleIdToPermission(p.Stack, roleId) - setRoleIdToPermission(p.SecurityPolicy, roleId) + setRoleIdToPermission(p.Policy, roleId) setRoleIdToPermission(p.ProjectManagement, roleId) setRoleIdToPermission(p.Notification, roleId) setRoleIdToPermission(p.Configuration, roleId) diff --git a/internal/route/route.go b/internal/route/route.go index 1bd7beb8..04d93110 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -293,6 +293,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa 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.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/permissions", customMiddleware.Handle(internalApi.GetPermissionsByAccountId, http.HandlerFunc(permissionHandler.GetPermissionsByAccountId))).Methods(http.MethodGet) policyTemplateHandler := delivery.NewPolicyTemplateHandler(usecaseFactory) r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplate))).Methods(http.MethodGet) diff --git a/internal/usecase/permission.go b/internal/usecase/permission.go index 32b6bf0d..7db6be5d 100644 --- a/internal/usecase/permission.go +++ b/internal/usecase/permission.go @@ -15,6 +15,7 @@ type IPermissionUsecase interface { GetAllowedPermissionSet(ctx context.Context) *model.PermissionSet GetUserPermissionSet(ctx context.Context) *model.PermissionSet UpdatePermission(ctx context.Context, permission *model.Permission) error + MergePermissionWithOrOperator(ctx context.Context, permissionSet ...*model.PermissionSet) *model.PermissionSet } type PermissionUsecase struct { @@ -35,7 +36,7 @@ func (p PermissionUsecase) CreatePermissionSet(ctx context.Context, permissionSe if err = p.repo.Create(ctx, permissionSet.Stack); err != nil { return err } - if err = p.repo.Create(ctx, permissionSet.SecurityPolicy); err != nil { + if err = p.repo.Create(ctx, permissionSet.Policy); err != nil { return err } if err = p.repo.Create(ctx, permissionSet.ProjectManagement); err != nil { @@ -54,7 +55,7 @@ func (p PermissionUsecase) GetPermissionSetByRoleId(ctx context.Context, roleId permissionSet := &model.PermissionSet{ Dashboard: nil, Stack: nil, - SecurityPolicy: nil, + Policy: nil, ProjectManagement: nil, Notification: nil, Configuration: nil, @@ -70,9 +71,9 @@ func (p PermissionUsecase) GetPermissionSetByRoleId(ctx context.Context, roleId permissionSet.Dashboard = permission case string(model.StackPermission): permissionSet.Stack = permission - case string(model.SecurityPolicyPermission): - permissionSet.SecurityPolicy = permission - case string(model.ProjectManagementPermission): + case string(model.PolicyPermission): + permissionSet.Policy = permission + case string(model.ProjectPermission): permissionSet.ProjectManagement = permission case string(model.NotificationPermission): permissionSet.Notification = permission @@ -115,3 +116,36 @@ func (p PermissionUsecase) GetUserPermissionSet(ctx context.Context) *model.Perm permissionSet.SetUserPermissionSet() return permissionSet } + +func (p PermissionUsecase) MergePermissionWithOrOperator(ctx context.Context, permissionSet ...*model.PermissionSet) *model.PermissionSet { + var out *model.PermissionSet + for i, ps := range permissionSet { + if i == 0 { + out = ps + continue + } + + out.Dashboard = p.mergePermission(out.Dashboard, ps.Dashboard) + out.Stack = p.mergePermission(out.Stack, ps.Stack) + out.Policy = p.mergePermission(out.Policy, ps.Policy) + out.ProjectManagement = p.mergePermission(out.ProjectManagement, ps.ProjectManagement) + out.Notification = p.mergePermission(out.Notification, ps.Notification) + out.Configuration = p.mergePermission(out.Configuration, ps.Configuration) + } + + return out +} + +func (p PermissionUsecase) mergePermission(mergedPermission, permission *model.Permission) *model.Permission { + var mergedEdgePermissions []*model.Permission + mergedEdgePermissions = model.GetEdgePermission(mergedPermission, mergedEdgePermissions, nil) + + var rightEdgePermissions []*model.Permission + rightEdgePermissions = model.GetEdgePermission(permission, rightEdgePermissions, nil) + + for i, rightEdgePermission := range rightEdgePermissions { + *(mergedEdgePermissions[i].IsAllowed) = *(mergedEdgePermissions[i].IsAllowed) || *(rightEdgePermission.IsAllowed) + } + + return mergedPermission +} diff --git a/pkg/domain/permission.go b/pkg/domain/permission.go index 8be227a2..d9e83240 100644 --- a/pkg/domain/permission.go +++ b/pkg/domain/permission.go @@ -4,31 +4,67 @@ import ( "github.com/google/uuid" ) -type PermissionResponse struct { - ID uuid.UUID `json:"ID"` - Name string `json:"name"` - IsAllowed *bool `json:"is_allowed,omitempty"` - Endpoints []*EndpointResponse `json:"endpoints,omitempty"` - Children []*PermissionResponse `json:"children,omitempty"` +type GetPermissionTemplatesResponse struct { + Permissions *PermissionTemplateResponse `json:"permissions"` +} + +type PermissionTemplateResponse struct { + Dashboard *TemplateResponse `json:"dashboard,omitempty"` + Stack *TemplateResponse `json:"stack,omitempty"` + Policy *TemplateResponse `json:"policy,omitempty"` + ProjectManagement *TemplateResponse `json:"project_management,omitempty"` + Notification *TemplateResponse `json:"notification,omitempty"` + Configuration *TemplateResponse `json:"configuration,omitempty"` +} + +type TemplateResponse struct { + Name string `json:"name"` + Key string `json:"key"` + IsAllowed *bool `json:"is_allowed,omitempty"` + Children []*TemplateResponse `json:"children,omitempty"` +} + +type GetPermissionsByRoleIdResponse struct { + Permissions *PermissionSetResponse `json:"permissions"` } type PermissionSetResponse struct { Dashboard *PermissionResponse `json:"dashboard,omitempty"` Stack *PermissionResponse `json:"stack,omitempty"` - SecurityPolicy *PermissionResponse `json:"security_policy,omitempty"` + Policy *PermissionResponse `json:"policy,omitempty"` ProjectManagement *PermissionResponse `json:"project_management,omitempty"` Notification *PermissionResponse `json:"notification,omitempty"` Configuration *PermissionResponse `json:"configuration,omitempty"` } -type GetPermissionTemplatesResponse struct { - Permissions []*PermissionResponse `json:"permissions"` +type PermissionResponse struct { + ID uuid.UUID `json:"ID"` + Name string `json:"name"` + Key string `json:"key"` + IsAllowed *bool `json:"is_allowed,omitempty"` + Endpoints []*EndpointResponse `json:"endpoints,omitempty"` + Children []*PermissionResponse `json:"children,omitempty"` } -type GetPermissionsByRoleIdResponse struct { +type UpdatePermissionsByRoleIdRequest struct { Permissions []*PermissionResponse `json:"permissions"` } -type UpdatePermissionsByRoleIdRequest struct { - Permissions []*PermissionResponse `json:"permissions"` +type GetUsersPermissionsResponse struct { + Permissions *MergedPermissionSetResponse `json:"permissions"` +} + +type MergedPermissionSetResponse struct { + Dashboard *MergePermissionResponse `json:"dashboard,omitempty"` + Stack *MergePermissionResponse `json:"stack,omitempty"` + Policy *MergePermissionResponse `json:"policy,omitempty"` + ProjectManagement *MergePermissionResponse `json:"project_management,omitempty"` + Notification *MergePermissionResponse `json:"notification,omitempty"` + Configuration *MergePermissionResponse `json:"configuration,omitempty"` +} + +type MergePermissionResponse struct { + Key string `json:"key"` + IsAllowed *bool `json:"is_allowed,omitempty"` + Children []*MergePermissionResponse `json:"children,omitempty"` } From 6a2a4286a099371987c8267a4f5a67710290b071 Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 22 Mar 2024 17:43:27 +0900 Subject: [PATCH 10/18] lint fix --- internal/usecase/organization.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/usecase/organization.go b/internal/usecase/organization.go index ff7c59d0..9fa70a56 100644 --- a/internal/usecase/organization.go +++ b/internal/usecase/organization.go @@ -35,7 +35,6 @@ type OrganizationUsecase struct { roleRepo repository.IRoleRepository clusterRepo repository.IClusterRepository stackTemplateRepo repository.IStackTemplateRepository - policyTemplateRepo repository.IPolicyTemplateRepository systemNotificationTemplateRepo repository.ISystemNotificationTemplateRepository argo argowf.ArgoClient kc keycloak.IKeycloak From efb9b597f3936ad5d9a7b60f9db3f32727fe91b0 Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 22 Mar 2024 17:45:42 +0900 Subject: [PATCH 11/18] trimming --- internal/delivery/api/endpoint.go | 1 - internal/delivery/api/generated_endpoints.go.go | 8 -------- 2 files changed, 9 deletions(-) diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index c5874faf..1bd43ae1 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -19,7 +19,6 @@ const ( VerifyIdentityForLostId VerifyIdentityForLostPassword VerifyToken - DeleteToken // User CreateUser diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go index b4b8549e..8d1efb92 100644 --- a/internal/delivery/api/generated_endpoints.go.go +++ b/internal/delivery/api/generated_endpoints.go.go @@ -35,10 +35,6 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "VerifyToken", Group: "Auth", }, - DeleteToken: { - Name: "DeleteToken", - Group: "Auth", - }, CreateUser: { Name: "CreateUser", Group: "User", @@ -830,8 +826,6 @@ func (e Endpoint) String() string { return "VerifyIdentityForLostPassword" case VerifyToken: return "VerifyToken" - case DeleteToken: - return "DeleteToken" case CreateUser: return "CreateUser" case ListUser: @@ -1240,8 +1234,6 @@ func GetEndpoint(name string) Endpoint { return VerifyIdentityForLostPassword case "VerifyToken": return VerifyToken - case "DeleteToken": - return DeleteToken case "CreateUser": return CreateUser case "ListUser": From db0da2dcef1dc5c6a86fe5cfa362522db9ae45eb Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 22 Mar 2024 17:57:13 +0900 Subject: [PATCH 12/18] minor fix. --- internal/delivery/http/permission.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/delivery/http/permission.go b/internal/delivery/http/permission.go index 8ec9b7e9..04c8a947 100644 --- a/internal/delivery/http/permission.go +++ b/internal/delivery/http/permission.go @@ -15,6 +15,7 @@ type IPermissionHandler interface { GetPermissionTemplates(w http.ResponseWriter, r *http.Request) GetPermissionsByRoleId(w http.ResponseWriter, r *http.Request) UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) + GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) } type PermissionHandler struct { @@ -22,7 +23,7 @@ type PermissionHandler struct { userUsecase usecase.IUserUsecase } -func NewPermissionHandler(usecase usecase.Usecase) *PermissionHandler { +func NewPermissionHandler(usecase usecase.Usecase) IPermissionHandler { return &PermissionHandler{ permissionUsecase: usecase.Permission, userUsecase: usecase.User, From 7bb99f1d68cd85a0c2de1b242c63ad9347645b9e Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 22 Mar 2024 20:18:58 +0900 Subject: [PATCH 13/18] Add project role to the response of login endpoint --- internal/delivery/http/auth.go | 25 +++++++++++++++++++++---- pkg/domain/auth.go | 26 +++++++++++++++++++------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/internal/delivery/http/auth.go b/internal/delivery/http/auth.go index f817f8ba..ee87d823 100644 --- a/internal/delivery/http/auth.go +++ b/internal/delivery/http/auth.go @@ -28,14 +28,16 @@ type IAuthHandler interface { //Authenticate(next http.Handler) http.Handler } type AuthHandler struct { - usecase usecase.IAuthUsecase - auditUsecase usecase.IAuditUsecase + usecase usecase.IAuthUsecase + auditUsecase usecase.IAuditUsecase + projectUsecase usecase.IProjectUsecase } func NewAuthHandler(h usecase.Usecase) IAuthHandler { return &AuthHandler{ - usecase: h.Auth, - auditUsecase: h.Audit, + usecase: h.Auth, + auditUsecase: h.Audit, + projectUsecase: h.Project, } } @@ -95,11 +97,26 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { } } + projects, err := h.projectUsecase.GetProjects(r.Context(), input.OrganizationId, user.ID.String(), true, nil) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + var out domain.LoginResponse if err = serializer.Map(r.Context(), user, &out.User); err != nil { log.Error(r.Context(), err) } + for _, project := range projects { + var projectRole domain.ProjectIdProjectRoleResponse + projectRole.ID = project.ID + projectRole.ProjectRoleId = project.ProjectRoleId + projectRole.ProjectRoleName = project.ProjectRoleName + out.User.Projects = append(out.User.Projects, &projectRole) + } + ResponseJSON(w, r, http.StatusOK, out) } diff --git a/pkg/domain/auth.go b/pkg/domain/auth.go index d88da980..8d8983e3 100644 --- a/pkg/domain/auth.go +++ b/pkg/domain/auth.go @@ -8,16 +8,28 @@ type LoginRequest struct { type LoginResponse struct { User struct { - AccountId string `json:"accountId"` - Name string `json:"name"` - Token string `json:"token"` - Role RoleResponse `json:"role"` - Department string `json:"department"` - Organization OrganizationResponse `json:"organization"` - PasswordExpired bool `json:"passwordExpired"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Token string `json:"token"` + Role RoleIdRoleNameResponse `json:"role"` + Projects []*ProjectIdProjectRoleResponse `json:"projects"` + Department string `json:"department"` + Organization OrganizationResponse `json:"organization"` + PasswordExpired bool `json:"passwordExpired"` } `json:"user"` } +type RoleIdRoleNameResponse struct { + ID string `json:"roleId"` + Name string `json:"roleName"` +} + +type ProjectIdProjectRoleResponse struct { + ID string `json:"projectId"` + ProjectRoleId string `json:"projectRoleId"` + ProjectRoleName string `json:"projectRoleName"` +} + type LogoutResponse struct { SsoUrls map[string][]string `json:"ssoUrls"` } From 81a1bebf962d68601fbdcd7c36ba713b13dd571e Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 22 Mar 2024 19:38:34 +0900 Subject: [PATCH 14/18] Require users to obtain a new access token after changes to their TKS role or project role --- go.mod | 1 - internal/database/database.go | 5 +- internal/delivery/http/auth.go | 2 +- internal/delivery/http/project.go | 59 ++++++++++++-- internal/helper/jwt.go | 18 ++++- internal/keycloak/config.go | 6 +- internal/keycloak/keycloak.go | 6 +- .../auth/authenticator/authenticator.go | 24 +++++- .../authenticator/custom/authenticator.go | 80 +++++++++++++++++++ .../auth/authenticator/keycloak/keycloak.go | 21 +++-- internal/model/auth.go | 23 ++++++ internal/repository/auth.go | 47 +++++++---- internal/route/route.go | 3 +- internal/usecase/auth.go | 24 ++++-- internal/usecase/user.go | 13 ++- 15 files changed, 278 insertions(+), 54 deletions(-) create mode 100644 internal/middleware/auth/authenticator/custom/authenticator.go create mode 100644 internal/model/auth.go diff --git a/go.mod b/go.mod index a1014793..47e17ec0 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/ses v1.21.0 github.com/aws/aws-sdk-go-v2/service/sts v1.27.0 github.com/deckarep/golang-set/v2 v2.6.0 - github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.18.0 diff --git a/internal/database/database.go b/internal/database/database.go index d70511cd..fbc85aac 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -61,7 +61,10 @@ func InitDB() (*gorm.DB, error) { func migrateSchema(db *gorm.DB) error { // Auth - if err := db.AutoMigrate(&repository.CacheEmailCode{}); err != nil { + if err := db.AutoMigrate(&model.CacheEmailCode{}); err != nil { + return err + } + if err := db.AutoMigrate(&model.ExpiredTokenTime{}); err != nil { return err } if err := db.AutoMigrate(&model.User{}); err != nil { diff --git a/internal/delivery/http/auth.go b/internal/delivery/http/auth.go index ee87d823..eec9d560 100644 --- a/internal/delivery/http/auth.go +++ b/internal/delivery/http/auth.go @@ -327,7 +327,7 @@ func (h *AuthHandler) VerifyToken(w http.ResponseWriter, r *http.Request) { } if !isActive { - ErrorJSON(w, r, httpErrors.NewUnauthorizedError(fmt.Errorf("token is not active"), "A_UNUSABLE_TOKEN", "")) + ErrorJSON(w, r, httpErrors.NewUnauthorizedError(fmt.Errorf("token is not active"), "A_EXPIRED_TOKEN", "")) return } diff --git a/internal/delivery/http/project.go b/internal/delivery/http/project.go index ba66719a..e5712505 100644 --- a/internal/delivery/http/project.go +++ b/internal/delivery/http/project.go @@ -58,12 +58,14 @@ type IProjectHandler interface { } type ProjectHandler struct { - usecase usecase.IProjectUsecase + usecase usecase.IProjectUsecase + authUsecase usecase.IAuthUsecase } func NewProjectHandler(u usecase.Usecase) IProjectHandler { return &ProjectHandler{ - usecase: u.Project, + usecase: u.Project, + authUsecase: u.Auth, } } @@ -624,6 +626,13 @@ func (p ProjectHandler) AddProjectMember(w http.ResponseWriter, r *http.Request) return } } + + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pmr.ProjectUserId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } } out := domain.CommonProjectResponse{Result: "OK"} @@ -898,6 +907,20 @@ func (p ProjectHandler) RemoveProjectMember(w http.ResponseWriter, r *http.Reque } } + pm, err := p.usecase.GetProjectMember(r.Context(), projectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if err := p.usecase.RemoveProjectMember(r.Context(), organizationId, projectMemberId); err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return @@ -956,8 +979,8 @@ func (p ProjectHandler) RemoveProjectMembers(w http.ResponseWriter, r *http.Requ } // TODO: change multi row delete - for _, pm := range projectMemberReq.ProjectMember { - if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, pm.ProjectMemberId); err != nil { + for _, pmId := range projectMemberReq.ProjectMember { + if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, pmId.ProjectMemberId); err != nil { log.Error(r.Context(), err) ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return @@ -965,14 +988,22 @@ func (p ProjectHandler) RemoveProjectMembers(w http.ResponseWriter, r *http.Requ // tasks for keycloak & k8s for stackId := range stackIds { - if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", pm.ProjectMemberId); err != nil { + if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", pmId.ProjectMemberId); err != nil { log.Error(r.Context(), err) ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } } - if err := p.usecase.RemoveProjectMember(r.Context(), organizationId, pm.ProjectMemberId); err != nil { + pm, err := p.usecase.GetProjectMember(r.Context(), pmId.ProjectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + + if err := p.usecase.RemoveProjectMember(r.Context(), organizationId, pmId.ProjectMemberId); err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } @@ -1088,6 +1119,14 @@ func (p ProjectHandler) UpdateProjectMemberRole(w http.ResponseWriter, r *http.R } } + // update token expired time + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) } @@ -1190,6 +1229,14 @@ func (p ProjectHandler) UpdateProjectMembersRole(w http.ResponseWriter, r *http. return } } + + // update token expired time + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } } ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) diff --git a/internal/helper/jwt.go b/internal/helper/jwt.go index 68170a55..3db7184a 100644 --- a/internal/helper/jwt.go +++ b/internal/helper/jwt.go @@ -1,9 +1,10 @@ package helper import ( + "fmt" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" "github.com/spf13/viper" ) @@ -37,3 +38,18 @@ func VerifyToken(tokenString string) (*jwt.Token, error) { } return token, err } + +func StringToTokenWithoutVerification(tokenString string) (*jwt.Token, error) { + token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) + if err != nil { + return nil, fmt.Errorf("invalid token") + } + return token, nil +} +func RetrieveClaims(token *jwt.Token) (map[string]interface{}, error) { + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + return nil, fmt.Errorf("invalid token") + } + return claims, nil +} diff --git a/internal/keycloak/config.go b/internal/keycloak/config.go index 212748c1..eb26042c 100644 --- a/internal/keycloak/config.go +++ b/internal/keycloak/config.go @@ -12,7 +12,7 @@ const ( DefaultClientID = "tks" DefaultClientSecret = "secret" AdminCliClientID = "admin-cli" - accessTokenLifespan = 60 * 60 * 24 // 1 day - ssoSessionIdleTimeout = 60 * 60 * 24 // 1 day - ssoSessionMaxLifespan = 60 * 60 * 24 // 1 day + AccessTokenLifespan = 60 * 60 * 24 // 1 day + SsoSessionIdleTimeout = 60 * 60 * 24 // 1 day + SsoSessionMaxLifespan = 60 * 60 * 24 // 1 day ) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index ff69db1c..d7618fee 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -927,9 +927,9 @@ func defaultRealmSetting(realmId string) gocloak.RealmRepresentation { return gocloak.RealmRepresentation{ Realm: gocloak.StringP(realmId), Enabled: gocloak.BoolP(true), - AccessTokenLifespan: gocloak.IntP(accessTokenLifespan), - SsoSessionIdleTimeout: gocloak.IntP(ssoSessionIdleTimeout), - SsoSessionMaxLifespan: gocloak.IntP(ssoSessionMaxLifespan), + AccessTokenLifespan: gocloak.IntP(AccessTokenLifespan), + SsoSessionIdleTimeout: gocloak.IntP(SsoSessionIdleTimeout), + SsoSessionMaxLifespan: gocloak.IntP(SsoSessionMaxLifespan), } } diff --git a/internal/middleware/auth/authenticator/authenticator.go b/internal/middleware/auth/authenticator/authenticator.go index 80ea6bba..8f8ab1b3 100644 --- a/internal/middleware/auth/authenticator/authenticator.go +++ b/internal/middleware/auth/authenticator/authenticator.go @@ -2,6 +2,7 @@ package authenticator import ( "fmt" + "github.com/openinfradev/tks-api/internal/repository" "net/http" internalHttp "github.com/openinfradev/tks-api/internal/delivery/http" @@ -19,23 +20,38 @@ type Request interface { } type defaultAuthenticator struct { - auth Request + kcAuth Request + customAuth Request + repo repository.Repository } -func NewAuthenticator(kc Request) *defaultAuthenticator { +func NewAuthenticator(kc Request, repo repository.Repository, c Request) *defaultAuthenticator { return &defaultAuthenticator{ - auth: kc, + kcAuth: kc, + repo: repo, + customAuth: c, } } func (a *defaultAuthenticator) WithAuthentication(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - resp, ok, err := a.auth.AuthenticateRequest(r) + resp, ok, err := a.kcAuth.AuthenticateRequest(r) if !ok { log.Error(r.Context(), err) internalHttp.ErrorJSON(w, r, err) return } + if err != nil { + internalHttp.ErrorJSON(w, r, err) + return + } + + _, ok, err = a.customAuth.AuthenticateRequest(r) + if !ok { + internalHttp.ErrorJSON(w, r, err) + return + } + r = r.WithContext(request.WithUser(r.Context(), resp.User)) _, ok = request.UserFrom(r.Context()) diff --git a/internal/middleware/auth/authenticator/custom/authenticator.go b/internal/middleware/auth/authenticator/custom/authenticator.go new file mode 100644 index 00000000..f92f218a --- /dev/null +++ b/internal/middleware/auth/authenticator/custom/authenticator.go @@ -0,0 +1,80 @@ +package custom + +import ( + "fmt" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "net/http" + "strings" +) + +type CustomAuthenticator struct { + repo repository.Repository +} + +func NewCustomAuthenticator(repo repository.Repository) *CustomAuthenticator { + return &CustomAuthenticator{ + repo: repo, + } +} +func (a *CustomAuthenticator) AuthenticateRequest(r *http.Request) (*authenticator.Response, bool, error) { + authHeader := strings.TrimSpace(r.Header.Get("Authorization")) + if authHeader == "" { + return nil, false, fmt.Errorf("authorizer header is invalid") + } + parts := strings.SplitN(authHeader, " ", 3) + if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { + return nil, false, fmt.Errorf("authorizer header is invalid") + } + token := parts[1] + + if len(token) == 0 { + // The space before the token case + if len(parts) == 3 { + log.Warn(r.Context(), "the provided Authorization header contains extra space before the bearer token, and is ignored") + } + return nil, false, fmt.Errorf("token is empty") + } + + parsedToken, err := helper.StringToTokenWithoutVerification(token) + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + claims, err := helper.RetrieveClaims(parsedToken) + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + organizationId, ok := claims["organization"].(string) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("organizationId is not found"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + userId, ok := claims["sub"].(string) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("userId is not found"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + expiredTime, err := a.repo.Auth.GetExpiredTimeOnToken(r.Context(), organizationId, userId) + if expiredTime == nil { + return nil, true, nil + } + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + iat, ok := claims["iat"].(float64) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("iat is not found"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + if int64(iat) < expiredTime.ExpiredTime.Unix() { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("token is changed"), "A_UNUSABLE_TOKEN", "토큰이 변경되었습니다.") + } + + return nil, true, nil +} diff --git a/internal/middleware/auth/authenticator/keycloak/keycloak.go b/internal/middleware/auth/authenticator/keycloak/keycloak.go index 8f0ee136..dcc649a8 100644 --- a/internal/middleware/auth/authenticator/keycloak/keycloak.go +++ b/internal/middleware/auth/authenticator/keycloak/keycloak.go @@ -2,11 +2,11 @@ package keycloak import ( "fmt" + "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/pkg/httpErrors" "net/http" "strings" - jwtWithouKey "github.com/dgrijalva/jwt-go" "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" @@ -49,12 +49,17 @@ func (a *keycloakAuthenticator) AuthenticateRequest(r *http.Request) (*authentic } func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) (*authenticator.Response, bool, error) { - parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) + parsedToken, err := helper.StringToTokenWithoutVerification(token) if err != nil { return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } - organizationId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) + claims, err := helper.RetrieveClaims(parsedToken) + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + organizationId, ok := claims["organization"].(string) if !ok { return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("organization is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } @@ -70,7 +75,7 @@ func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) // tks role extraction roleOrganizationMapping := make(map[string]string) - if roles, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["tks-role"]; !ok { + if roles, ok := claims["tks-role"]; !ok { log.Errorf(r.Context(), "tks-role is not found in token") return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("tks-role is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") @@ -90,7 +95,7 @@ func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) // project role extraction projectIds := make([]string, 0) roleProjectMapping := make(map[string]string) - if roles, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["project-role"]; ok { + if roles, ok := claims["project-role"]; ok { for _, role := range roles.([]interface{}) { slice := strings.Split(role.(string), "@") if len(slice) != 2 { @@ -104,18 +109,18 @@ func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) } } - userId, err := uuid.Parse(parsedToken.Claims.(jwtWithouKey.MapClaims)["sub"].(string)) + userId, err := uuid.Parse(claims["sub"].(string)) if err != nil { log.Errorf(r.Context(), "failed to verify access token: %v", err) return nil, false, httpErrors.NewUnauthorizedError(err, "C_INTERNAL_ERROR", "") } - requestSessionId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["sid"].(string) + requestSessionId, ok := claims["sid"].(string) if !ok { return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("session id is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } - userAccountId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["preferred_username"].(string) + userAccountId, ok := claims["preferred_username"].(string) if !ok { return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("preferred_username is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } diff --git a/internal/model/auth.go b/internal/model/auth.go new file mode 100644 index 00000000..b2866816 --- /dev/null +++ b/internal/model/auth.go @@ -0,0 +1,23 @@ +package model + +import ( + "github.com/google/uuid" + "gorm.io/gorm" + "time" +) + +// Models +type ExpiredTokenTime struct { + gorm.Model + + OrganizationId string `gorm:"index:idx_org_id_subject_id,unique;not null"` + SubjectId string `gorm:"index:idx_org_id_subject_id,unique;not null"` + ExpiredTime time.Time +} + +type CacheEmailCode struct { + gorm.Model + + UserId uuid.UUID `gorm:"not null"` + Code string `gorm:"type:varchar(6);not null"` +} diff --git a/internal/repository/auth.go b/internal/repository/auth.go index d9c86a59..c59d4c7f 100644 --- a/internal/repository/auth.go +++ b/internal/repository/auth.go @@ -3,29 +3,25 @@ package repository import ( "context" "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" "gorm.io/gorm" + "gorm.io/gorm/clause" + "time" ) type IAuthRepository interface { CreateEmailCode(ctx context.Context, userId uuid.UUID, code string) error - GetEmailCode(ctx context.Context, userId uuid.UUID) (CacheEmailCode, error) + GetEmailCode(ctx context.Context, userId uuid.UUID) (model.CacheEmailCode, error) UpdateEmailCode(ctx context.Context, userId uuid.UUID, code string) error DeleteEmailCode(ctx context.Context, userId uuid.UUID) error + GetExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) (*model.ExpiredTokenTime, error) + UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error } type AuthRepository struct { db *gorm.DB } -// Models - -type CacheEmailCode struct { - gorm.Model - - UserId uuid.UUID `gorm:"not null"` - Code string `gorm:"type:varchar(6);not null"` -} - func NewAuthRepository(db *gorm.DB) IAuthRepository { return &AuthRepository{ db: db, @@ -33,25 +29,44 @@ func NewAuthRepository(db *gorm.DB) IAuthRepository { } func (r *AuthRepository) CreateEmailCode(ctx context.Context, userId uuid.UUID, code string) error { - cacheEmailCode := CacheEmailCode{ + cacheEmailCode := model.CacheEmailCode{ UserId: userId, Code: code, } return r.db.WithContext(ctx).Create(&cacheEmailCode).Error } -func (r *AuthRepository) GetEmailCode(ctx context.Context, userId uuid.UUID) (CacheEmailCode, error) { - var cacheEmailCode CacheEmailCode +func (r *AuthRepository) GetEmailCode(ctx context.Context, userId uuid.UUID) (model.CacheEmailCode, error) { + var cacheEmailCode model.CacheEmailCode if err := r.db.WithContext(ctx).Where("user_id = ?", userId).First(&cacheEmailCode).Error; err != nil { - return CacheEmailCode{}, err + return model.CacheEmailCode{}, err } return cacheEmailCode, nil } func (r *AuthRepository) UpdateEmailCode(ctx context.Context, userId uuid.UUID, code string) error { - return r.db.WithContext(ctx).Model(&CacheEmailCode{}).Where("user_id = ?", userId).Update("code", code).Error + return r.db.WithContext(ctx).Model(&model.CacheEmailCode{}).Where("user_id = ?", userId).Update("code", code).Error } func (r *AuthRepository) DeleteEmailCode(ctx context.Context, userId uuid.UUID) error { - return r.db.WithContext(ctx).Unscoped().Where("user_id = ?", userId).Delete(&CacheEmailCode{}).Error + return r.db.WithContext(ctx).Unscoped().Where("user_id = ?", userId).Delete(&model.CacheEmailCode{}).Error +} + +func (r *AuthRepository) GetExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) (*model.ExpiredTokenTime, error) { + var expiredTokenTime model.ExpiredTokenTime + if err := r.db.WithContext(ctx).Where("organization_id = ? AND subject_id = ?", organizationId, userId).First(&expiredTokenTime).Error; err != nil { + return nil, err + } + return &expiredTokenTime, nil +} +func (r *AuthRepository) UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error { + // set expired time to now + return r.db.WithContext(ctx).Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "subject_id"}, {Name: "organization_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"expired_time"}), + }).Create(&model.ExpiredTokenTime{ + SubjectId: userId, + ExpiredTime: time.Now(), + OrganizationId: organizationId, + }).Error } diff --git a/internal/route/route.go b/internal/route/route.go index 04d93110..3783f41f 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -16,6 +16,7 @@ import ( "github.com/openinfradev/tks-api/internal/keycloak" internalMiddleware "github.com/openinfradev/tks-api/internal/middleware" "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + authCustom "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/custom" authKeycloak "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/keycloak" "github.com/openinfradev/tks-api/internal/middleware/auth/authorizer" "github.com/openinfradev/tks-api/internal/repository" @@ -85,7 +86,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa } customMiddleware := internalMiddleware.NewMiddleware( - authenticator.NewAuthenticator(authKeycloak.NewKeycloakAuthenticator(kc)), + authenticator.NewAuthenticator(authKeycloak.NewKeycloakAuthenticator(kc), repoFactory, authCustom.NewCustomAuthenticator(repoFactory)), authorizer.NewDefaultAuthorization(repoFactory), requestRecoder.NewDefaultRequestRecoder(), audit.NewDefaultAudit(repoFactory)) diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index 485a60a6..f8edf598 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -12,8 +12,6 @@ import ( "strings" "time" - jwtWithouKey "github.com/dgrijalva/jwt-go" - "github.com/openinfradev/tks-api/pkg/log" "github.com/spf13/viper" "golang.org/x/net/html" @@ -32,13 +30,14 @@ import ( type IAuthUsecase interface { Login(ctx context.Context, accountId string, password string, organizationId string) (model.User, error) - Logout(ctx context.Context, accessToken string, organizationId string) error + Logout(ctx context.Context, sessionId string, organizationId string) error FindId(ctx context.Context, code string, email string, userName string, organizationId string) (string, error) FindPassword(ctx context.Context, code string, accountId string, email string, userName string, organizationId string) error VerifyIdentity(ctx context.Context, accountId string, email string, userName string, organizationId string) error SingleSignIn(ctx context.Context, organizationId, accountId, password string) ([]*http.Cookie, error) SingleSignOut(ctx context.Context, organizationId string) (string, []*http.Cookie, error) VerifyToken(ctx context.Context, token string) (bool, error) + UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error } const ( @@ -96,9 +95,9 @@ func (u *AuthUsecase) Login(ctx context.Context, accountId string, password stri return user, nil } -func (u *AuthUsecase) Logout(ctx context.Context, accessToken string, organizationName string) error { +func (u *AuthUsecase) Logout(ctx context.Context, sessionId string, organizationName string) error { // [TODO] refresh token 을 추가하고, session timeout 을 줄이는 방향으로 고려할 것 - err := u.kc.Logout(ctx, accessToken, organizationName) + err := u.kc.Logout(ctx, sessionId, organizationName) if err != nil { return err } @@ -305,11 +304,16 @@ func (u *AuthUsecase) SingleSignOut(ctx context.Context, organizationId string) } func (u *AuthUsecase) VerifyToken(ctx context.Context, token string) (bool, error) { - parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) + parsedToken, err := helper.StringToTokenWithoutVerification(token) + if err != nil { + return false, err + } + claims, err := helper.RetrieveClaims(parsedToken) if err != nil { return false, err } - org, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) + + org, ok := claims["organization"].(string) if !ok { return false, fmt.Errorf("organization is not found in token") } @@ -325,7 +329,11 @@ func (u *AuthUsecase) VerifyToken(ctx context.Context, token string) (bool, erro return true, nil } -func (u *AuthUsecase) isExpiredEmailCode(code repository.CacheEmailCode) bool { +func (u *AuthUsecase) UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error { + return u.authRepository.UpdateExpiredTimeOnToken(ctx, organizationId, userId) +} + +func (u *AuthUsecase) isExpiredEmailCode(code model.CacheEmailCode) bool { return !helper.IsDurationExpired(code.UpdatedAt, internal.EmailCodeExpireTime) } diff --git a/internal/usecase/user.go b/internal/usecase/user.go index 5730e87d..ad8308f3 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -29,7 +29,7 @@ type IUserUsecase interface { Update(ctx context.Context, userId uuid.UUID, user *model.User) (*model.User, error) ResetPassword(ctx context.Context, userId uuid.UUID) error ResetPasswordByAccountId(ctx context.Context, accountId string, organizationId string) error - GenerateRandomPassword(ctx context.Context, ) string + GenerateRandomPassword(ctx context.Context) string Delete(ctx context.Context, userId uuid.UUID, organizationId string) error GetByAccountId(ctx context.Context, accountId string, organizationId string) (*model.User, error) GetByEmail(ctx context.Context, email string, organizationId string) (*model.User, error) @@ -47,6 +47,7 @@ type IUserUsecase interface { } type UserUsecase struct { + authRepository repository.IAuthRepository userRepository repository.IUserRepository roleRepository repository.IRoleRepository organizationRepository repository.IOrganizationRepository @@ -510,6 +511,15 @@ func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId st log.Errorf(ctx, "join group in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err, "", "") } + + targetUser, err := u.userRepository.Get(ctx, newUser.AccountId, newUser.Organization.ID) + if err != nil { + return nil, err + } + err = u.authRepository.UpdateExpiredTimeOnToken(ctx, newUser.Organization.ID, targetUser.ID.String()) + if err != nil { + return nil, err + } } *user, err = u.userRepository.UpdateWithUuid(ctx, user.ID, user.AccountId, user.Name, newUser.Role.ID, user.Email, @@ -523,6 +533,7 @@ func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId st func NewUserUsecase(r repository.Repository, kc keycloak.IKeycloak) IUserUsecase { return &UserUsecase{ + authRepository: r.Auth, userRepository: r.User, roleRepository: r.Role, kc: kc, From e92b58f1a80705bf6a8f580c95055b2534bdbf13 Mon Sep 17 00:00:00 2001 From: donggyu Date: Fri, 22 Mar 2024 19:49:59 +0900 Subject: [PATCH 15/18] lint fix --- internal/delivery/http/project.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/delivery/http/project.go b/internal/delivery/http/project.go index e5712505..c5405eed 100644 --- a/internal/delivery/http/project.go +++ b/internal/delivery/http/project.go @@ -1002,6 +1002,11 @@ func (p ProjectHandler) RemoveProjectMembers(w http.ResponseWriter, r *http.Requ return } err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } if err := p.usecase.RemoveProjectMember(r.Context(), organizationId, pmId.ProjectMemberId); err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) From 14e63bcc6248dd1afcd4c339142acecff5305cbb Mon Sep 17 00:00:00 2001 From: "taekyu.kang" Date: Fri, 22 Mar 2024 15:19:32 +0900 Subject: [PATCH 16/18] feature. implemntation systemnotification conditions --- api/swagger/docs.go | 183 +++++++++++++++++- api/swagger/swagger.json | 183 +++++++++++++++++- api/swagger/swagger.yaml | 122 ++++++++++++ internal/database/database.go | 5 +- .../delivery/http/system-notification-rule.go | 71 ++++++- internal/model/system-notification-rule.go | 51 ++--- .../repository/system-notification-rule.go | 45 +++-- internal/route/route.go | 8 +- internal/usecase/system-notification-rule.go | 73 +++++-- pkg/domain/system-notification-rule.go | 71 ++++++- pkg/httpErrors/errorCode.go | 8 +- 11 files changed, 734 insertions(+), 86 deletions(-) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index e3c7dc34..45504123 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -9475,14 +9475,68 @@ const docTemplate = `{ "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest": { "type": "object", "required": [ - "name" + "messageCondition", + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" ], "properties": { "description": { "type": "string" }, + "messageActionProposal": { + "type": "string" + }, + "messageCondition": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, "name": { "type": "string" + }, + "systemNotificationConditions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "duration": { + "type": "integer" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "order": { + "type": "integer" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + } + }, + "systemNotificationTemplateId": { + "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -11946,6 +12000,46 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse": { + "type": "object", + "properties": { + "duration": { + "type": "integer" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "order": { + "type": "integer" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter": { + "type": "object", + "properties": { + "operator": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "value": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse": { "type": "object", "properties": { @@ -12032,9 +12126,36 @@ const docTemplate = `{ "id": { "type": "string" }, + "messageActionProposal": { + "type": "string" + }, + "messageCondition": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, "name": { "type": "string" }, + "systemNotificationConditions": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse" + } + }, + "systemNotificationTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse" + }, + "targetUsers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + }, "updatedAt": { "type": "string" }, @@ -12547,9 +12668,69 @@ const docTemplate = `{ }, "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest": { "type": "object", + "required": [ + "messageCondition", + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" + ], "properties": { "description": { "type": "string" + }, + "messageActionProposal": { + "type": "string" + }, + "messageCondition": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "systemNotificationConditions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "duration": { + "type": "integer" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "order": { + "type": "integer" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + } + }, + "systemNotificationTemplateId": { + "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 4011501e..d04a4c49 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -9469,14 +9469,68 @@ "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest": { "type": "object", "required": [ - "name" + "messageCondition", + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" ], "properties": { "description": { "type": "string" }, + "messageActionProposal": { + "type": "string" + }, + "messageCondition": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, "name": { "type": "string" + }, + "systemNotificationConditions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "duration": { + "type": "integer" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "order": { + "type": "integer" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + } + }, + "systemNotificationTemplateId": { + "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -11940,6 +11994,46 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse": { + "type": "object", + "properties": { + "duration": { + "type": "integer" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "order": { + "type": "integer" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter": { + "type": "object", + "properties": { + "operator": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "value": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse": { "type": "object", "properties": { @@ -12026,9 +12120,36 @@ "id": { "type": "string" }, + "messageActionProposal": { + "type": "string" + }, + "messageCondition": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, "name": { "type": "string" }, + "systemNotificationConditions": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse" + } + }, + "systemNotificationTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse" + }, + "targetUsers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + }, "updatedAt": { "type": "string" }, @@ -12541,9 +12662,69 @@ }, "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest": { "type": "object", + "required": [ + "messageCondition", + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" + ], "properties": { "description": { "type": "string" + }, + "messageActionProposal": { + "type": "string" + }, + "messageCondition": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "systemNotificationConditions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "duration": { + "type": "integer" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "order": { + "type": "integer" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + } + }, + "systemNotificationTemplateId": { + "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index 3950feae..865f2519 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -1099,10 +1099,47 @@ definitions: properties: description: type: string + messageActionProposal: + type: string + messageCondition: + type: string + messageContent: + type: string + messageTitle: + type: string name: type: string + systemNotificationConditions: + items: + properties: + duration: + type: integer + enableEmail: + type: boolean + enablePortal: + type: boolean + order: + type: integer + parameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter' + type: array + severity: + type: string + type: object + type: array + systemNotificationTemplateId: + type: string + targetUserIds: + items: + type: string + type: array required: + - messageCondition + - messageContent + - messageTitle - name + - systemNotificationTemplateId type: object github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleResponse: properties: @@ -2728,6 +2765,32 @@ definitions: updatedAt: type: string type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse: + properties: + duration: + type: integer + enableEmail: + type: boolean + enablePortal: + type: boolean + order: + type: integer + parameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter' + type: array + severity: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter: + properties: + operator: + type: string + order: + type: integer + value: + type: string + type: object github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse: properties: closedAt: @@ -2785,8 +2848,26 @@ definitions: type: string id: type: string + messageActionProposal: + type: string + messageCondition: + type: string + messageContent: + type: string + messageTitle: + type: string name: type: string + systemNotificationConditions: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse' + type: array + systemNotificationTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse' + targetUsers: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + type: array updatedAt: type: string updator: @@ -3129,6 +3210,47 @@ definitions: properties: description: type: string + messageActionProposal: + type: string + messageCondition: + type: string + messageContent: + type: string + messageTitle: + type: string + name: + type: string + systemNotificationConditions: + items: + properties: + duration: + type: integer + enableEmail: + type: boolean + enablePortal: + type: boolean + order: + type: integer + parameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter' + type: array + severity: + type: string + type: object + type: array + systemNotificationTemplateId: + type: string + targetUserIds: + items: + type: string + type: array + required: + - messageCondition + - messageContent + - messageTitle + - name + - systemNotificationTemplateId type: object github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationTemplateRequest: properties: diff --git a/internal/database/database.go b/internal/database/database.go index fbc85aac..4a480a4e 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -130,13 +130,10 @@ func migrateSchema(db *gorm.DB) error { } // SystemNotificationRule - if err := db.AutoMigrate(&model.SystemNotificationRule{}); err != nil { - return err - } if err := db.AutoMigrate(&model.SystemNotificationCondition{}); err != nil { return err } - if err := db.AutoMigrate(&model.SystemNotificationMessage{}); err != nil { + if err := db.AutoMigrate(&model.SystemNotificationRule{}); err != nil { return err } diff --git a/internal/delivery/http/system-notification-rule.go b/internal/delivery/http/system-notification-rule.go index 068966bc..2c27207c 100644 --- a/internal/delivery/http/system-notification-rule.go +++ b/internal/delivery/http/system-notification-rule.go @@ -1,6 +1,7 @@ package http import ( + "encoding/json" "fmt" "net/http" @@ -38,6 +39,13 @@ func NewSystemNotificationRuleHandler(h usecase.Usecase) *SystemNotificationRule // @Router /organizations/{organizationId}/system-notification-rules [post] // @Security JWT func (h *SystemNotificationRuleHandler) CreateSystemNotificationRule(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + input := domain.CreateSystemNotificationRuleRequest{} err := UnmarshalRequestInput(r, &input) if err != nil { @@ -49,6 +57,14 @@ func (h *SystemNotificationRuleHandler) CreateSystemNotificationRule(w http.Resp if err = serializer.Map(r.Context(), input, &dto); err != nil { log.Info(r.Context(), err) } + dto.OrganizationId = organizationId + + dto.SystemNotificationConditions = make([]model.SystemNotificationCondition, len(input.SystemNotificationConditions)) + for i, systemNotificationCondition := range input.SystemNotificationConditions { + if err := serializer.Map(r.Context(), systemNotificationCondition, &dto.SystemNotificationConditions[i]); err != nil { + log.Info(r.Context(), err) + } + } id, err := h.usecase.Create(r.Context(), dto) if err != nil { @@ -99,6 +115,25 @@ func (h *SystemNotificationRuleHandler) GetSystemNotificationRules(w http.Respon if err := serializer.Map(r.Context(), systemNotificationRule, &out.SystemNotificationRules[i]); err != nil { log.Info(r.Context(), err) } + + out.SystemNotificationRules[i].TargetUsers = make([]domain.SimpleUserResponse, len(systemNotificationRule.TargetUsers)) + for j, targetUser := range systemNotificationRule.TargetUsers { + if err := serializer.Map(r.Context(), targetUser, &out.SystemNotificationRules[i].TargetUsers[j]); err != nil { + log.Info(r.Context(), err) + } + } + + out.SystemNotificationRules[i].SystemNotificationConditions = make([]domain.SystemNotificationConditionResponse, len(systemNotificationRule.SystemNotificationConditions)) + for j, condition := range systemNotificationRule.SystemNotificationConditions { + if err := serializer.Map(r.Context(), condition, &out.SystemNotificationRules[i].SystemNotificationConditions[j]); err != nil { + log.Info(r.Context(), err) + } + log.Info(r.Context(), condition.Parameter) + err = json.Unmarshal(condition.Parameter, &out.SystemNotificationRules[i].SystemNotificationConditions[j].Parameters) + if err != nil { + log.Error(r.Context(), err) + } + } } if out.Pagination, err = pg.Response(r.Context()); err != nil { @@ -144,6 +179,18 @@ func (h *SystemNotificationRuleHandler) GetSystemNotificationRule(w http.Respons log.Info(r.Context(), err) } + out.SystemNotificationRule.SystemNotificationConditions = make([]domain.SystemNotificationConditionResponse, len(systemNotificationRule.SystemNotificationConditions)) + for i, condition := range systemNotificationRule.SystemNotificationConditions { + if err := serializer.Map(r.Context(), condition, &out.SystemNotificationRule.SystemNotificationConditions[i]); err != nil { + log.Info(r.Context(), err) + } + log.Info(r.Context(), condition.Parameter) + err = json.Unmarshal(condition.Parameter, &out.SystemNotificationRule.SystemNotificationConditions[i].Parameters) + if err != nil { + log.Error(r.Context(), err) + } + } + ResponseJSON(w, r, http.StatusOK, out) } @@ -160,24 +207,44 @@ func (h *SystemNotificationRuleHandler) GetSystemNotificationRule(w http.Respons // @Security JWT func (h *SystemNotificationRuleHandler) UpdateSystemNotificationRule(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + strId, ok := vars["systemNotificationRuleId"] if !ok { ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationRuleId"), "C_INVALID_STACK_TEMPLATE_ID", "")) return } - systemNotificationRuleId, err := uuid.Parse(strId) if err != nil { ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_STACK_TEMPLATE_ID", "")) return } + input := domain.UpdateSystemNotificationRuleRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + var dto model.SystemNotificationRule - if err := serializer.Map(r.Context(), r, &dto); err != nil { + if err = serializer.Map(r.Context(), input, &dto); err != nil { log.Info(r.Context(), err) } + dto.OrganizationId = organizationId dto.ID = systemNotificationRuleId + dto.SystemNotificationConditions = make([]model.SystemNotificationCondition, len(input.SystemNotificationConditions)) + for i, systemNotificationCondition := range input.SystemNotificationConditions { + if err := serializer.Map(r.Context(), systemNotificationCondition, &dto.SystemNotificationConditions[i]); err != nil { + log.Info(r.Context(), err) + } + } + err = h.usecase.Update(r.Context(), dto) if err != nil { ErrorJSON(w, r, err) diff --git a/internal/model/system-notification-rule.go b/internal/model/system-notification-rule.go index b1ddee30..9ab63c37 100644 --- a/internal/model/system-notification-rule.go +++ b/internal/model/system-notification-rule.go @@ -2,40 +2,43 @@ package model import ( "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" "gorm.io/datatypes" "gorm.io/gorm" ) type SystemNotificationCondition struct { - SystemNotificationRuleId uuid.UUID `gorm:"primarykey"` - Order int `gorm:"primarykey"` + gorm.Model + + SystemNotificationRuleId uuid.UUID + Order int Severity string Duration int - Condition datatypes.JSON - EnableEmail bool `gorm:"default:false"` - EnablePortal bool `gorm:"default:true"` -} - -type SystemNotificationMessage struct { - SystemNotificationRuleId uuid.UUID `gorm:"primarykey"` - Title string - Content int - Condition datatypes.JSON - ActionProposal string - TargetUsers []User `gorm:"many2many:system_notification_message_users"` + Parameter datatypes.JSON + Parameters []domain.SystemNotificationParameter `gorm:"-:all"` + EnableEmail bool `gorm:"default:false"` + EnablePortal bool `gorm:"default:true"` } type SystemNotificationRule struct { gorm.Model - ID uuid.UUID `gorm:"primarykey"` - Name string `gorm:"index,unique"` - Description string - Templates []SystemNotificationTemplate `gorm:"many2many:system_notification_rule_system_notification_templates"` - Messages []SystemNotificationMessage `gorm:"many2many:system_notification_rule_system_notification_messages"` - - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator *User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator *User `gorm:"foreignKey:UpdatorId"` + ID uuid.UUID `gorm:"primarykey"` + Name string `gorm:"index,unique"` + Description string + OrganizationId string + Organization Organization `gorm:"foreignKey:OrganizationId"` + SystemNotificationTemplate SystemNotificationTemplate `gorm:"foreignKey:SystemNotificationTemplateId"` + SystemNotificationTemplateId string + SystemNotificationConditions []SystemNotificationCondition `gorm:"foreignKey:SystemNotificationRuleId"` + TargetUsers []User `gorm:"many2many:system_notification_rule_users"` + TargetUserIds []string `gorm:"-:all"` + MessageTitle string + MessageContent string + MessageCondition datatypes.JSON + MessageActionProposal string + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator *User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator *User `gorm:"foreignKey:UpdatorId"` } diff --git a/internal/repository/system-notification-rule.go b/internal/repository/system-notification-rule.go index 3e07db21..84ed1063 100644 --- a/internal/repository/system-notification-rule.go +++ b/internal/repository/system-notification-rule.go @@ -20,7 +20,6 @@ type ISystemNotificationRuleRepository interface { Create(ctx context.Context, dto model.SystemNotificationRule) (systemNotificationRuleId uuid.UUID, err error) Update(ctx context.Context, dto model.SystemNotificationRule) (err error) Delete(ctx context.Context, dto model.SystemNotificationRule) (err error) - UpdateOrganizations(ctx context.Context, systemNotificationRuleId uuid.UUID, organizationIds []model.Organization) (err error) } type SystemNotificationRuleRepository struct { @@ -83,40 +82,48 @@ func (r *SystemNotificationRuleRepository) Create(ctx context.Context, dto model if res.Error != nil { return uuid.Nil, res.Error } + return dto.ID, nil } func (r *SystemNotificationRuleRepository) Update(ctx context.Context, dto model.SystemNotificationRule) (err error) { - res := r.db.WithContext(ctx).Model(&model.SystemNotificationRule{}). - Where("id = ?", dto.ID). - Updates(map[string]interface{}{ - "Name": dto.Name, - "Description": dto.Description, - "UpdatorId": dto.UpdatorId}) + var m model.SystemNotificationRule + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&m, "id = ?", dto.ID) if res.Error != nil { return res.Error } - return nil -} -func (r *SystemNotificationRuleRepository) Delete(ctx context.Context, dto model.SystemNotificationRule) (err error) { - res := r.db.WithContext(ctx).Delete(&model.SystemNotificationRule{}, "id = ?", dto.ID) + m.Name = dto.Name + m.Description = dto.Description + m.SystemNotificationTemplateId = dto.SystemNotificationTemplateId + //m.SystemNotificationConditions = dto.SystemNotificationConditions + m.MessageTitle = dto.MessageTitle + m.MessageContent = dto.MessageContent + m.MessageCondition = dto.MessageCondition + m.MessageActionProposal = dto.MessageActionProposal + m.UpdatorId = dto.UpdatorId + + res = r.db.WithContext(ctx).Session(&gorm.Session{FullSaveAssociations: true}).Updates(&m) if res.Error != nil { return res.Error } - return nil -} -func (r *SystemNotificationRuleRepository) UpdateOrganizations(ctx context.Context, systemNotificationRuleId uuid.UUID, organizations []model.Organization) (err error) { - var systemNotificationRule = model.SystemNotificationRule{} - res := r.db.WithContext(ctx).Preload("Organizations").First(&systemNotificationRule, "id = ?", systemNotificationRuleId) - if res.Error != nil { - return res.Error + err = r.db.WithContext(ctx).Model(&m).Association("TargetUsers").Replace(dto.TargetUsers) + if err != nil { + return err } - err = r.db.WithContext(ctx).Model(&systemNotificationRule).Association("Organizations").Replace(organizations) + err = r.db.WithContext(ctx).Model(&m).Association("SystemNotificationConditions").Replace(dto.SystemNotificationConditions) if err != nil { return err } return nil } + +func (r *SystemNotificationRuleRepository) Delete(ctx context.Context, dto model.SystemNotificationRule) (err error) { + res := r.db.WithContext(ctx).Delete(&model.SystemNotificationRule{}, "id = ?", dto.ID) + if res.Error != nil { + return res.Error + } + return nil +} diff --git a/internal/route/route.go b/internal/route/route.go index 3783f41f..51e639c5 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -228,10 +228,10 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-templates", customMiddleware.Handle(internalApi.RemoveOrganizationSystemNotificationTemplates, http.HandlerFunc(systemNotificationTemplateHandler.RemoveOrganizationSystemNotificationTemplates))).Methods(http.MethodPut) systemNotificationRuleHandler := delivery.NewSystemNotificationRuleHandler(usecaseFactory) - r.Handle(API_PREFIX+API_VERSION+"/system-notification-rules", customMiddleware.Handle(internalApi.CreateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.CreateSystemNotificationRule))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/system-notification-rules", customMiddleware.Handle(internalApi.GetSystemNotificationRules, http.HandlerFunc(systemNotificationRuleHandler.GetSystemNotificationRules))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.GetSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.GetSystemNotificationRule))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.UpdateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.UpdateSystemNotificationRule))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules", customMiddleware.Handle(internalApi.CreateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.CreateSystemNotificationRule))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules", customMiddleware.Handle(internalApi.GetSystemNotificationRules, http.HandlerFunc(systemNotificationRuleHandler.GetSystemNotificationRules))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.GetSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.GetSystemNotificationRule))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.UpdateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.UpdateSystemNotificationRule))).Methods(http.MethodPut) stackHandler := delivery.NewStackHandler(usecaseFactory) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", customMiddleware.Handle(internalApi.GetStacks, http.HandlerFunc(stackHandler.GetStacks))).Methods(http.MethodGet) diff --git a/internal/usecase/system-notification-rule.go b/internal/usecase/system-notification-rule.go index 8aa4c384..d69c5c15 100644 --- a/internal/usecase/system-notification-rule.go +++ b/internal/usecase/system-notification-rule.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" @@ -20,19 +21,22 @@ type ISystemNotificationRuleUsecase interface { Create(ctx context.Context, dto model.SystemNotificationRule) (systemNotificationRule uuid.UUID, err error) Update(ctx context.Context, dto model.SystemNotificationRule) error Delete(ctx context.Context, dto model.SystemNotificationRule) error - UpdateOrganizations(ctx context.Context, dto model.SystemNotificationRule) error GetByName(ctx context.Context, name string) (model.SystemNotificationRule, error) } type SystemNotificationRuleUsecase struct { - repo repository.ISystemNotificationRuleRepository - organizationRepo repository.IOrganizationRepository + repo repository.ISystemNotificationRuleRepository + organizationRepo repository.IOrganizationRepository + userRepo repository.IUserRepository + systemNotificationTemplateRepo repository.ISystemNotificationTemplateRepository } func NewSystemNotificationRuleUsecase(r repository.Repository) ISystemNotificationRuleUsecase { return &SystemNotificationRuleUsecase{ - repo: r.SystemNotificationRule, - organizationRepo: r.Organization, + repo: r.SystemNotificationRule, + organizationRepo: r.Organization, + userRepo: r.User, + systemNotificationTemplateRepo: r.SystemNotificationTemplate, } } @@ -46,10 +50,34 @@ func (u *SystemNotificationRuleUsecase) Create(ctx context.Context, dto model.Sy dto.UpdatorId = &userId if _, err = u.GetByName(ctx, dto.Name); err == nil { - return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("duplicate systemNotificationRule name"), "ST_CREATE_ALREADY_EXISTED_NAME", "") + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("duplicate systemNotificationRule name"), "SNR_CREATE_ALREADY_EXISTED_NAME", "") } - return systemNotificationRuleId, nil + // Users + dto.TargetUsers = make([]model.User, 0) + for _, strId := range dto.TargetUserIds { + userId, err := uuid.Parse(strId) + if err == nil { + user, err := u.userRepo.GetByUuid(ctx, userId) + if err == nil { + dto.TargetUsers = append(dto.TargetUsers, user) + } + } + } + + // Make parameters + for i, condition := range dto.SystemNotificationConditions { + dto.SystemNotificationConditions[i].Parameter = []byte(helper.ModelToJson(condition.Parameters)) + } + + systemNotificationRuleId, err = u.repo.Create(ctx, dto) + if err != nil { + return uuid.Nil, err + } + + // [TODO] update kubernetes resources + + return } func (u *SystemNotificationRuleUsecase) Update(ctx context.Context, dto model.SystemNotificationRule) error { @@ -58,10 +86,30 @@ func (u *SystemNotificationRuleUsecase) Update(ctx context.Context, dto model.Sy return httpErrors.NewBadRequestError(err, "SNR_NOT_EXISTED_STACK_TEMPLATE", "") } + // Users + dto.TargetUsers = make([]model.User, 0) + for _, strId := range dto.TargetUserIds { + userId, err := uuid.Parse(strId) + if err == nil { + user, err := u.userRepo.GetByUuid(ctx, userId) + if err == nil { + dto.TargetUsers = append(dto.TargetUsers, user) + } + } + } + + for i, condition := range dto.SystemNotificationConditions { + dto.SystemNotificationConditions[i].SystemNotificationRuleId = dto.ID + dto.SystemNotificationConditions[i].Parameter = []byte(helper.ModelToJson(condition.Parameters)) + } + err = u.repo.Update(ctx, dto) if err != nil { return err } + + // [TODO] update kubernetes resources + return nil } @@ -77,7 +125,7 @@ func (u *SystemNotificationRuleUsecase) GetByName(ctx context.Context, name stri out, err = u.repo.GetByName(ctx, name) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return out, httpErrors.NewNotFoundError(err, "SNR_FAILED_FETCH_STACK_TEMPLATE", "") + return out, httpErrors.NewNotFoundError(err, "SNR_FAILED_FETCH_SYSTEM_NOTIFICATION_RULE", "") } return out, err } @@ -96,12 +144,3 @@ func (u *SystemNotificationRuleUsecase) Fetch(ctx context.Context, organizationI func (u *SystemNotificationRuleUsecase) Delete(ctx context.Context, dto model.SystemNotificationRule) (err error) { return nil } - -func (u *SystemNotificationRuleUsecase) UpdateOrganizations(ctx context.Context, dto model.SystemNotificationRule) error { - _, err := u.repo.Get(ctx, dto.ID) - if err != nil { - return httpErrors.NewBadRequestError(err, "SNR_NOT_EXISTED_STACK_TEMPLATE", "") - } - - return nil -} diff --git a/pkg/domain/system-notification-rule.go b/pkg/domain/system-notification-rule.go index e21e3477..d891799a 100644 --- a/pkg/domain/system-notification-rule.go +++ b/pkg/domain/system-notification-rule.go @@ -5,13 +5,35 @@ import ( ) type SystemNotificationRuleResponse struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Creator SimpleUserResponse `json:"creator"` - Updator SimpleUserResponse `json:"updator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + MessageTitle string `json:"messageTitle"` + MessageContent string `json:"messageContent"` + MessageCondition string `json:"messageCondition"` + MessageActionProposal string `json:"messageActionProposal"` + TargetUsers []SimpleUserResponse `json:"targetUsers"` + SystemNotificationTemplate SimpleSystemNotificationTemplateResponse `json:"systemNotificationTemplate"` + SystemNotificationConditions []SystemNotificationConditionResponse `json:"systemNotificationConditions"` + Creator SimpleUserResponse `json:"creator"` + Updator SimpleUserResponse `json:"updator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type SystemNotificationParameter struct { + Order int `json:"order"` + Operator string `json:"operator"` + Value string `json:"value"` +} + +type SystemNotificationConditionResponse struct { + Order int `json:"order"` + Severity string `json:"severity"` + Duration int `json:"duration"` + Parameters []SystemNotificationParameter `json:"parameters"` + EnableEmail bool `json:"enableEmail"` + EnablePortal bool `json:"enablePortal"` } type SimpleSystemNotificationRuleResponse struct { @@ -30,8 +52,22 @@ type GetSystemNotificationRuleResponse struct { } type CreateSystemNotificationRuleRequest struct { - Name string `json:"name" validate:"required,name"` - Description string `json:"description"` + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + MessageTitle string `json:"messageTitle" validate:"required"` + MessageContent string `json:"messageContent" validate:"required"` + MessageCondition string `json:"messageCondition" validate:"required"` + MessageActionProposal string `json:"messageActionProposal"` + TargetUserIds []string `json:"targetUserIds"` + SystemNotificationTemplateId string `json:"systemNotificationTemplateId" validate:"required"` + SystemNotificationConditions []struct { + Order int `json:"order"` + Severity string `json:"severity"` + Duration int `json:"duration"` + Parameters []SystemNotificationParameter `json:"parameters"` + EnableEmail bool `json:"enableEmail"` + EnablePortal bool `json:"enablePortal"` + } `json:"systemNotificationConditions"` } type CreateSystemNotificationRuleResponse struct { @@ -39,7 +75,22 @@ type CreateSystemNotificationRuleResponse struct { } type UpdateSystemNotificationRuleRequest struct { - Description string `json:"description"` + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + MessageTitle string `json:"messageTitle" validate:"required"` + MessageContent string `json:"messageContent" validate:"required"` + MessageCondition string `json:"messageCondition" validate:"required"` + MessageActionProposal string `json:"messageActionProposal"` + TargetUserIds []string `json:"targetUserIds"` + SystemNotificationTemplateId string `json:"systemNotificationTemplateId" validate:"required"` + SystemNotificationConditions []struct { + Order int `json:"order"` + Severity string `json:"severity"` + Duration int `json:"duration"` + Parameters []SystemNotificationParameter `json:"parameters"` + EnableEmail bool `json:"enableEmail"` + EnablePortal bool `json:"enablePortal"` + } `json:"systemNotificationConditions"` } type CheckSystemNotificationRuleNameResponse struct { diff --git a/pkg/httpErrors/errorCode.go b/pkg/httpErrors/errorCode.go index f8e1b961..63fe9dfd 100644 --- a/pkg/httpErrors/errorCode.go +++ b/pkg/httpErrors/errorCode.go @@ -89,10 +89,10 @@ var errorMap = map[ErrorCode]string{ "SNT_NOT_EXISTED_ALERT_TEMPLATE": "업데이트할 알림템플릿이 존재하지 않습니다.", // SystemNotificationRule - "SNR_CREATE_ALREADY_EXISTED_NAME": "알림템플릿 설정에 이미 존재하는 이름입니다.", - "SNR_FAILED_FETCH_ALERT_TEMPLATE": "알림템플릿 설정을 가져오는데 실패했습니다.", - "SNR_FAILED_UPDATE_ORGANIZATION": "알림템플릿 설정에 조직을 설정하는데 실패했습니다.", - "SNR_NOT_EXISTED_ALERT_TEMPLATE": "업데이트할 알림템플릿 설정이 존재하지 않습니다.", + "SNR_CREATE_ALREADY_EXISTED_NAME": "알림 설정에 이미 존재하는 이름입니다.", + "SNR_FAILED_FETCH_SYSTEM_NOTIFICATION_RULE": "알림 설정을 가져오는데 실패했습니다.", + "SNR_FAILED_UPDATE_ORGANIZATION": "알림 설정에 조직을 설정하는데 실패했습니다.", + "SNR_NOT_EXISTED_SYSTEM_NOTIFICATION_RULE": "업데이트할 알림 설정이 존재하지 않습니다.", // AppGroup "AG_NOT_FOUND_CLUSTER": "지장한 클러스터가 존재하지 않습니다.", From 8f58fc4cce71bce422444596356d273f5abd0efe Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 25 Mar 2024 14:41:03 +0900 Subject: [PATCH 17/18] fix api docs --- api/swagger/docs.go | 193 +++++++++++++++--- api/swagger/swagger.json | 193 +++++++++++++++--- api/swagger/swagger.yaml | 137 ++++++++++--- go.mod | 2 +- go.sum | 6 +- internal/delivery/api/endpoint.go | 6 +- .../delivery/api/generated_endpoints.go.go | 32 +-- internal/delivery/http/permission.go | 189 ----------------- internal/delivery/http/role.go | 127 +++++++++++- internal/delivery/http/user.go | 104 +++++++++- internal/model/permission.go | 26 +-- internal/route/route.go | 6 +- 12 files changed, 678 insertions(+), 343 deletions(-) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 45504123..e2878fbc 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -246,7 +246,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Admin List Tks Roles", "parameters": [ @@ -275,7 +275,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Admin Get Tks Role", "parameters": [ @@ -314,7 +314,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Admin" + "Users" ], "summary": "Get user detail by admin", "parameters": [ @@ -356,7 +356,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Admin" + "Users" ], "summary": "Update user by admin", "parameters": [ @@ -5926,7 +5926,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "List Tks Roles", "parameters": [ @@ -5961,7 +5961,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Create Tks Role", "parameters": [ @@ -6004,7 +6004,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Get Tks Role", "parameters": [ @@ -6046,7 +6046,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Update Tks Role", "parameters": [ @@ -6091,7 +6091,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Delete Tks Role", "parameters": [ @@ -6125,16 +6125,29 @@ const docTemplate = `{ } ], "description": "Get Permissions By Role ID", - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ - "Permission" + "Roles" ], "summary": "Get Permissions By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", @@ -6158,10 +6171,17 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Permission" + "Roles" ], "summary": "Update Permissions By Role ID", "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, { "type": "string", "description": "Role ID", @@ -7734,6 +7754,47 @@ const docTemplate = `{ } } }, + "/organizations/{organizationId}/users/{accountId}/permissions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permissions By Account ID", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get Permissions By Account ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse" + } + } + } + } + }, "/organizations/{organizationId}/users/{accountId}/reset-password": { "put": { "security": [ @@ -9775,17 +9836,11 @@ const docTemplate = `{ "github_com_openinfradev_tks-api_pkg_domain.EndpointResponse": { "type": "object", "properties": { - "createdAt": { - "type": "string" - }, "group": { "type": "string" }, "name": { "type": "string" - }, - "updatedAt": { - "type": "string" } } }, @@ -10505,6 +10560,14 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse": { + "type": "object", + "properties": { + "permissions": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest": { "type": "object", "required": [ @@ -10708,8 +10771,14 @@ const docTemplate = `{ "passwordExpired": { "type": "boolean" }, + "projects": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectIdProjectRoleResponse" + } + }, "role": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleIdRoleNameResponse" }, "token": { "type": "string" @@ -10790,6 +10859,46 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + }, + "is_allowed": { + "type": "boolean" + }, + "key": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse": { + "type": "object", + "properties": { + "configuration": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "dashboard": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "notification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "project_management": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "stack": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse": { "type": "object", "required": [ @@ -10935,19 +11044,10 @@ const docTemplate = `{ "is_allowed": { "type": "boolean" }, - "name": { - "type": "string" - }, - "parent": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" - }, - "parent_id": { + "key": { "type": "string" }, - "role": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleResponse" - }, - "role_id": { + "name": { "type": "string" } } @@ -10964,10 +11064,10 @@ const docTemplate = `{ "notification": { "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" }, - "project_management": { + "policy": { "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" }, - "security_policy": { + "project_management": { "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" }, "stack": { @@ -11206,6 +11306,20 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.ProjectIdProjectRoleResponse": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "projectRoleId": { + "type": "string" + }, + "projectRoleName": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest": { "type": "object", "required": [ @@ -11533,6 +11647,17 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.RoleIdRoleNameResponse": { + "type": "object", + "properties": { + "roleId": { + "type": "string" + }, + "roleName": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.RoleResponse": { "type": "object", "properties": { diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index d04a4c49..27347d15 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -240,7 +240,7 @@ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Admin List Tks Roles", "parameters": [ @@ -269,7 +269,7 @@ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Admin Get Tks Role", "parameters": [ @@ -308,7 +308,7 @@ "application/json" ], "tags": [ - "Admin" + "Users" ], "summary": "Get user detail by admin", "parameters": [ @@ -350,7 +350,7 @@ "application/json" ], "tags": [ - "Admin" + "Users" ], "summary": "Update user by admin", "parameters": [ @@ -5920,7 +5920,7 @@ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "List Tks Roles", "parameters": [ @@ -5955,7 +5955,7 @@ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Create Tks Role", "parameters": [ @@ -5998,7 +5998,7 @@ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Get Tks Role", "parameters": [ @@ -6040,7 +6040,7 @@ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Update Tks Role", "parameters": [ @@ -6085,7 +6085,7 @@ "application/json" ], "tags": [ - "Role" + "Roles" ], "summary": "Delete Tks Role", "parameters": [ @@ -6119,16 +6119,29 @@ } ], "description": "Get Permissions By Role ID", - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ - "Permission" + "Roles" ], "summary": "Get Permissions By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", @@ -6152,10 +6165,17 @@ "application/json" ], "tags": [ - "Permission" + "Roles" ], "summary": "Update Permissions By Role ID", "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, { "type": "string", "description": "Role ID", @@ -7728,6 +7748,47 @@ } } }, + "/organizations/{organizationId}/users/{accountId}/permissions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permissions By Account ID", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get Permissions By Account ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse" + } + } + } + } + }, "/organizations/{organizationId}/users/{accountId}/reset-password": { "put": { "security": [ @@ -9769,17 +9830,11 @@ "github_com_openinfradev_tks-api_pkg_domain.EndpointResponse": { "type": "object", "properties": { - "createdAt": { - "type": "string" - }, "group": { "type": "string" }, "name": { "type": "string" - }, - "updatedAt": { - "type": "string" } } }, @@ -10499,6 +10554,14 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse": { + "type": "object", + "properties": { + "permissions": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest": { "type": "object", "required": [ @@ -10702,8 +10765,14 @@ "passwordExpired": { "type": "boolean" }, + "projects": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectIdProjectRoleResponse" + } + }, "role": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleIdRoleNameResponse" }, "token": { "type": "string" @@ -10784,6 +10853,46 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + }, + "is_allowed": { + "type": "boolean" + }, + "key": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse": { + "type": "object", + "properties": { + "configuration": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "dashboard": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "notification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "project_management": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "stack": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse": { "type": "object", "required": [ @@ -10929,19 +11038,10 @@ "is_allowed": { "type": "boolean" }, - "name": { - "type": "string" - }, - "parent": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" - }, - "parent_id": { + "key": { "type": "string" }, - "role": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleResponse" - }, - "role_id": { + "name": { "type": "string" } } @@ -10958,10 +11058,10 @@ "notification": { "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" }, - "project_management": { + "policy": { "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" }, - "security_policy": { + "project_management": { "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" }, "stack": { @@ -11200,6 +11300,20 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.ProjectIdProjectRoleResponse": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "projectRoleId": { + "type": "string" + }, + "projectRoleName": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest": { "type": "object", "required": [ @@ -11527,6 +11641,17 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.RoleIdRoleNameResponse": { + "type": "object", + "properties": { + "roleId": { + "type": "string" + }, + "roleName": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.RoleResponse": { "type": "object", "properties": { diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index 865f2519..b4b04e01 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -1299,14 +1299,10 @@ definitions: type: object github_com_openinfradev_tks-api_pkg_domain.EndpointResponse: properties: - createdAt: - type: string group: type: string name: type: string - updatedAt: - type: string type: object github_com_openinfradev_tks-api_pkg_domain.FilterResponse: properties: @@ -1768,6 +1764,11 @@ definitions: type: string type: object type: object + github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse: + properties: + permissions: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse' + type: object github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest: properties: cloudService: @@ -1902,8 +1903,12 @@ definitions: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' passwordExpired: type: boolean + projects: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectIdProjectRoleResponse' + type: array role: - $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleIdRoleNameResponse' token: type: string type: object @@ -1957,6 +1962,32 @@ definitions: example: 레이블 요구 type: string type: object + github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse: + properties: + children: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + type: array + is_allowed: + type: boolean + key: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse: + properties: + configuration: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + dashboard: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + notification: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + policy: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + project_management: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + stack: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + type: object github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse: properties: key: @@ -2053,15 +2084,9 @@ definitions: type: array is_allowed: type: boolean - name: - type: string - parent: - $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' - parent_id: + key: type: string - role: - $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RoleResponse' - role_id: + name: type: string type: object github_com_openinfradev_tks-api_pkg_domain.PermissionSetResponse: @@ -2072,9 +2097,9 @@ definitions: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' notification: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' - project_management: + policy: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' - security_policy: + project_management: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' stack: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' @@ -2241,6 +2266,15 @@ definitions: updatedAt: type: string type: object + github_com_openinfradev_tks-api_pkg_domain.ProjectIdProjectRoleResponse: + properties: + projectId: + type: string + projectRoleId: + type: string + projectRoleName: + type: string + type: object github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest: properties: projectRoleId: @@ -2456,6 +2490,13 @@ definitions: usage: type: integer type: object + github_com_openinfradev_tks-api_pkg_domain.RoleIdRoleNameResponse: + properties: + roleId: + type: string + roleName: + type: string + type: object github_com_openinfradev_tks-api_pkg_domain.RoleResponse: properties: createdAt: @@ -3634,7 +3675,7 @@ paths: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse' summary: Admin List Tks Roles tags: - - Role + - Roles /admin/organizations/{organizationId}/roles/{roleId}: get: description: Admin Get Tks Role @@ -3658,7 +3699,7 @@ paths: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse' summary: Admin Get Tks Role tags: - - Role + - Roles /admin/organizations/{organizationId}/users/{accountId}: get: consumes: @@ -3684,7 +3725,7 @@ paths: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetUserResponse' summary: Get user detail by admin tags: - - Admin + - Users put: consumes: - application/json @@ -3717,7 +3758,7 @@ paths: - JWT: [] summary: Update user by admin tags: - - Admin + - Users /admin/policy-templates: get: consumes: @@ -7250,7 +7291,7 @@ paths: - JWT: [] summary: List Tks Roles tags: - - Role + - Roles post: consumes: - application/json @@ -7278,7 +7319,7 @@ paths: - JWT: [] summary: Create Tks Role tags: - - Role + - Roles /organizations/{organizationId}/roles/{roleId}: delete: description: Delete Tks Role @@ -7302,7 +7343,7 @@ paths: - JWT: [] summary: Delete Tks Role tags: - - Role + - Roles get: description: Get Tks Role parameters: @@ -7327,7 +7368,7 @@ paths: - JWT: [] summary: Get Tks Role tags: - - Role + - Roles put: consumes: - application/json @@ -7358,12 +7399,21 @@ paths: - JWT: [] summary: Update Tks Role tags: - - Role + - Roles /organizations/{organizationId}/roles/{roleId}/permissions: get: - consumes: - - application/json description: Get Permissions By Role ID + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Role ID + in: path + name: roleId + required: true + type: string produces: - application/json responses: @@ -7375,12 +7425,17 @@ paths: - JWT: [] summary: Get Permissions By Role ID tags: - - Permission + - Roles put: consumes: - application/json description: Update Permissions By Role ID parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string - description: Role ID in: path name: roleId @@ -7401,7 +7456,7 @@ paths: - JWT: [] summary: Update Permissions By Role ID tags: - - Permission + - Roles /organizations/{organizationId}/stack-templates: get: consumes: @@ -8333,6 +8388,32 @@ paths: summary: Update user tags: - Users + /organizations/{organizationId}/users/{accountId}/permissions: + get: + description: Get Permissions By Account ID + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Account ID + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse' + security: + - JWT: [] + summary: Get Permissions By Account ID + tags: + - Users /organizations/{organizationId}/users/{accountId}/reset-password: put: consumes: diff --git a/go.mod b/go.mod index 47e17ec0..92db160f 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.18.0 - github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.2 diff --git a/go.sum b/go.sum index d3bf874f..9a81524e 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -132,8 +130,8 @@ github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 1bd43ae1..e740b404 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -29,6 +29,7 @@ const ( ResetPassword CheckId CheckEmail + GetPermissionsByAccountId // MyProfile GetMyProfile @@ -189,12 +190,11 @@ const ( GetTksRole DeleteTksRole UpdateTksRole + GetPermissionsByRoleId + UpdatePermissionsByRoleId // Permission GetPermissionTemplates - GetPermissionsByRoleId - UpdatePermissionsByRoleId - GetPermissionsByAccountId // Admin_User Admin_CreateUser diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go index 8d1efb92..9e2c008d 100644 --- a/internal/delivery/api/generated_endpoints.go.go +++ b/internal/delivery/api/generated_endpoints.go.go @@ -67,6 +67,10 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "CheckEmail", Group: "User", }, + GetPermissionsByAccountId: { + Name: "GetPermissionsByAccountId", + Group: "User", + }, GetMyProfile: { Name: "GetMyProfile", Group: "MyProfile", @@ -587,20 +591,16 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "UpdateTksRole", Group: "Role", }, - GetPermissionTemplates: { - Name: "GetPermissionTemplates", - Group: "Permission", - }, GetPermissionsByRoleId: { Name: "GetPermissionsByRoleId", - Group: "Permission", + Group: "Role", }, UpdatePermissionsByRoleId: { Name: "UpdatePermissionsByRoleId", - Group: "Permission", + Group: "Role", }, - GetPermissionsByAccountId: { - Name: "GetPermissionsByAccountId", + GetPermissionTemplates: { + Name: "GetPermissionTemplates", Group: "Permission", }, Admin_CreateUser: { @@ -842,6 +842,8 @@ func (e Endpoint) String() string { return "CheckId" case CheckEmail: return "CheckEmail" + case GetPermissionsByAccountId: + return "GetPermissionsByAccountId" case GetMyProfile: return "GetMyProfile" case UpdateMyProfile: @@ -1102,14 +1104,12 @@ func (e Endpoint) String() string { return "DeleteTksRole" case UpdateTksRole: return "UpdateTksRole" - case GetPermissionTemplates: - return "GetPermissionTemplates" case GetPermissionsByRoleId: return "GetPermissionsByRoleId" case UpdatePermissionsByRoleId: return "UpdatePermissionsByRoleId" - case GetPermissionsByAccountId: - return "GetPermissionsByAccountId" + case GetPermissionTemplates: + return "GetPermissionTemplates" case Admin_CreateUser: return "Admin_CreateUser" case Admin_ListUser: @@ -1250,6 +1250,8 @@ func GetEndpoint(name string) Endpoint { return CheckId case "CheckEmail": return CheckEmail + case "GetPermissionsByAccountId": + return GetPermissionsByAccountId case "GetMyProfile": return GetMyProfile case "UpdateMyProfile": @@ -1510,14 +1512,12 @@ func GetEndpoint(name string) Endpoint { return DeleteTksRole case "UpdateTksRole": return UpdateTksRole - case "GetPermissionTemplates": - return GetPermissionTemplates case "GetPermissionsByRoleId": return GetPermissionsByRoleId case "UpdatePermissionsByRoleId": return UpdatePermissionsByRoleId - case "GetPermissionsByAccountId": - return GetPermissionsByAccountId + case "GetPermissionTemplates": + return GetPermissionTemplates case "Admin_CreateUser": return Admin_CreateUser case "Admin_ListUser": diff --git a/internal/delivery/http/permission.go b/internal/delivery/http/permission.go index 04c8a947..f298a394 100644 --- a/internal/delivery/http/permission.go +++ b/internal/delivery/http/permission.go @@ -4,18 +4,13 @@ import ( "context" "net/http" - "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/httpErrors" ) type IPermissionHandler interface { GetPermissionTemplates(w http.ResponseWriter, r *http.Request) - GetPermissionsByRoleId(w http.ResponseWriter, r *http.Request) - UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) - GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) } type PermissionHandler struct { @@ -71,187 +66,3 @@ func convertModelToPermissionTemplateResponse(ctx context.Context, permission *m return &permissionResponse } - -// GetPermissionsByAccountId godoc -// -// @Tags Permission -// @Summary Get Permissions By Account ID -// @Description Get Permissions By Account ID -// @Accept json -// @Produce json -// @Success 200 {object} domain.GetUsersPermissionsResponse -// @Router /organizations/{organizationId}/users/{accountId}/permissions [get] -// @Security JWT -func (h PermissionHandler) GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) { - var organizationId, accountId string - - vars := mux.Vars(r) - if v, ok := vars["accountId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - accountId = v - } - if v, ok := vars["organizationId"]; !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) - return - } else { - organizationId = v - } - - user, err := h.userUsecase.GetByAccountId(r.Context(), accountId, organizationId) - if err != nil { - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) - return - } - - var roles []*model.Role - roles = append(roles, &user.Role) - - var permissionSets []*model.PermissionSet - for _, role := range roles { - permissionSet, err := h.permissionUsecase.GetPermissionSetByRoleId(r.Context(), role.ID) - if err != nil { - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) - return - } - permissionSets = append(permissionSets, permissionSet) - } - - mergedPermissionSet := h.permissionUsecase.MergePermissionWithOrOperator(r.Context(), permissionSets...) - - var permissions domain.MergedPermissionSetResponse - permissions.Dashboard = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Dashboard) - permissions.Stack = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Stack) - permissions.Policy = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Policy) - permissions.ProjectManagement = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.ProjectManagement) - permissions.Notification = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Notification) - permissions.Configuration = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Configuration) - - var out domain.GetUsersPermissionsResponse - out.Permissions = &permissions - ResponseJSON(w, r, http.StatusOK, out) - -} - -func convertModelToMergedPermissionSetResponse(ctx context.Context, permission *model.Permission) *domain.MergePermissionResponse { - var permissionResponse domain.MergePermissionResponse - - permissionResponse.Key = permission.Key - if permission.IsAllowed != nil { - permissionResponse.IsAllowed = permission.IsAllowed - } - - for _, child := range permission.Children { - permissionResponse.Children = append(permissionResponse.Children, convertModelToMergedPermissionSetResponse(ctx, child)) - } - - return &permissionResponse -} - -// GetPermissionsByRoleId godoc -// -// @Tags Permission -// @Summary Get Permissions By Role ID -// @Description Get Permissions By Role ID -// @Accept json -// @Produce json -// @Success 200 {object} domain.PermissionSetResponse -// @Router /organizations/{organizationId}/roles/{roleId}/permissions [get] -// @Security JWT -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(r.Context(), roleId) - if err != nil { - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) - return - } - - var permissionSetResponse domain.PermissionSetResponse - permissionSetResponse.Dashboard = convertModelToPermissionResponse(r.Context(), permissionSet.Dashboard) - permissionSetResponse.Stack = convertModelToPermissionResponse(r.Context(), permissionSet.Stack) - permissionSetResponse.Policy = convertModelToPermissionResponse(r.Context(), permissionSet.Policy) - permissionSetResponse.ProjectManagement = convertModelToPermissionResponse(r.Context(), permissionSet.ProjectManagement) - permissionSetResponse.Notification = convertModelToPermissionResponse(r.Context(), permissionSet.Notification) - permissionSetResponse.Configuration = convertModelToPermissionResponse(r.Context(), permissionSet.Configuration) - - var out domain.GetPermissionsByRoleIdResponse - out.Permissions = &permissionSetResponse - - ResponseJSON(w, r, http.StatusOK, out) -} - -func convertModelToPermissionResponse(ctx context.Context, permission *model.Permission) *domain.PermissionResponse { - var permissionResponse domain.PermissionResponse - - permissionResponse.ID = permission.ID - permissionResponse.Key = permission.Key - permissionResponse.Name = permission.Name - if permission.IsAllowed != nil { - permissionResponse.IsAllowed = permission.IsAllowed - } - - for _, endpoint := range permission.Endpoints { - permissionResponse.Endpoints = append(permissionResponse.Endpoints, convertModelToEndpointResponse(ctx, endpoint)) - } - - for _, child := range permission.Children { - permissionResponse.Children = append(permissionResponse.Children, convertModelToPermissionResponse(ctx, child)) - } - - return &permissionResponse -} - -func convertModelToEndpointResponse(ctx context.Context, endpoint *model.Endpoint) *domain.EndpointResponse { - var endpointResponse domain.EndpointResponse - - endpointResponse.Name = endpoint.Name - endpointResponse.Group = endpoint.Group - - return &endpointResponse -} - -// 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 /organizations/{organizationId}/roles/{roleId}/permissions [put] -// @Security JWT -func (h PermissionHandler) UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) { - // request - input := domain.UpdatePermissionsByRoleIdRequest{} - err := UnmarshalRequestInput(r, &input) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } - - for _, permissionResponse := range input.Permissions { - var permission model.Permission - permission.ID = permissionResponse.ID - permission.IsAllowed = permissionResponse.IsAllowed - - if err := h.permissionUsecase.UpdatePermission(r.Context(), &permission); 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 8aebd4ab..21658fd9 100644 --- a/internal/delivery/http/role.go +++ b/internal/delivery/http/role.go @@ -1,6 +1,7 @@ package http import ( + "context" "net/http" "github.com/gorilla/mux" @@ -20,6 +21,9 @@ type IRoleHandler interface { DeleteTksRole(w http.ResponseWriter, r *http.Request) UpdateTksRole(w http.ResponseWriter, r *http.Request) + GetPermissionsByRoleId(w http.ResponseWriter, r *http.Request) + UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) + Admin_ListTksRoles(w http.ResponseWriter, r *http.Request) Admin_GetTksRole(w http.ResponseWriter, r *http.Request) } @@ -38,7 +42,7 @@ func NewRoleHandler(usecase usecase.Usecase) *RoleHandler { // CreateTksRole godoc // -// @Tags Role +// @Tags Roles // @Summary Create Tks Role // @Description Create Tks Role // @Accept json @@ -98,7 +102,7 @@ func (h RoleHandler) CreateTksRole(w http.ResponseWriter, r *http.Request) { // ListTksRoles godoc // -// @Tags Role +// @Tags Roles // @Summary List Tks Roles // @Description List Tks Roles // @Produce json @@ -153,7 +157,7 @@ func (h RoleHandler) ListTksRoles(w http.ResponseWriter, r *http.Request) { // GetTksRole godoc // -// @Tags Role +// @Tags Roles // @Summary Get Tks Role // @Description Get Tks Role // @Produce json @@ -195,7 +199,7 @@ func (h RoleHandler) GetTksRole(w http.ResponseWriter, r *http.Request) { // DeleteTksRole godoc // -// @Tags Role +// @Tags Roles // @Summary Delete Tks Role // @Description Delete Tks Role // @Produce json @@ -227,7 +231,7 @@ func (h RoleHandler) DeleteTksRole(w http.ResponseWriter, r *http.Request) { // UpdateTksRole godoc // -// @Tags Role +// @Tags Roles // @Summary Update Tks Role // @Description Update Tks Role // @Accept json @@ -273,9 +277,118 @@ func (h RoleHandler) UpdateTksRole(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, nil) } +// GetPermissionsByRoleId godoc +// +// @Tags Roles +// @Summary Get Permissions By Role ID +// @Description Get Permissions By Role ID +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Success 200 {object} domain.PermissionSetResponse +// @Router /organizations/{organizationId}/roles/{roleId}/permissions [get] +// @Security JWT +func (h RoleHandler) 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(r.Context(), roleId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + var permissionSetResponse domain.PermissionSetResponse + permissionSetResponse.Dashboard = convertModelToPermissionResponse(r.Context(), permissionSet.Dashboard) + permissionSetResponse.Stack = convertModelToPermissionResponse(r.Context(), permissionSet.Stack) + permissionSetResponse.Policy = convertModelToPermissionResponse(r.Context(), permissionSet.Policy) + permissionSetResponse.ProjectManagement = convertModelToPermissionResponse(r.Context(), permissionSet.ProjectManagement) + permissionSetResponse.Notification = convertModelToPermissionResponse(r.Context(), permissionSet.Notification) + permissionSetResponse.Configuration = convertModelToPermissionResponse(r.Context(), permissionSet.Configuration) + + var out domain.GetPermissionsByRoleIdResponse + out.Permissions = &permissionSetResponse + + ResponseJSON(w, r, http.StatusOK, out) +} + +func convertModelToPermissionResponse(ctx context.Context, permission *model.Permission) *domain.PermissionResponse { + var permissionResponse domain.PermissionResponse + + permissionResponse.ID = permission.ID + permissionResponse.Key = permission.Key + permissionResponse.Name = permission.Name + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed + } + + for _, endpoint := range permission.Endpoints { + permissionResponse.Endpoints = append(permissionResponse.Endpoints, convertModelToEndpointResponse(ctx, endpoint)) + } + + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToPermissionResponse(ctx, child)) + } + + return &permissionResponse +} + +func convertModelToEndpointResponse(ctx context.Context, endpoint *model.Endpoint) *domain.EndpointResponse { + var endpointResponse domain.EndpointResponse + + endpointResponse.Name = endpoint.Name + endpointResponse.Group = endpoint.Group + + return &endpointResponse +} + +// UpdatePermissionsByRoleId godoc +// +// @Tags Roles +// @Summary Update Permissions By Role ID +// @Description Update Permissions By Role ID +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Param body body domain.UpdatePermissionsByRoleIdRequest true "Update Permissions By Role ID Request" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId}/permissions [put] +// @Security JWT +func (h RoleHandler) UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) { + // request + input := domain.UpdatePermissionsByRoleIdRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + for _, permissionResponse := range input.Permissions { + var permission model.Permission + permission.ID = permissionResponse.ID + permission.IsAllowed = permissionResponse.IsAllowed + + if err := h.permissionUsecase.UpdatePermission(r.Context(), &permission); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + // Admin_ListTksRoles godoc // -// @Tags Role +// @Tags Roles // @Summary Admin List Tks Roles // @Description Admin List Tks Roles // @Produce json @@ -330,7 +443,7 @@ func (h RoleHandler) Admin_ListTksRoles(w http.ResponseWriter, r *http.Request) // Admin_GetTksRole godoc // -// @Tags Role +// @Tags Roles // @Summary Admin Get Tks Role // @Description Admin Get Tks Role // @Produce json diff --git a/internal/delivery/http/user.go b/internal/delivery/http/user.go index 9d6f09ef..f9764f05 100644 --- a/internal/delivery/http/user.go +++ b/internal/delivery/http/user.go @@ -1,6 +1,7 @@ package http import ( + "context" "fmt" "net/http" "strings" @@ -34,6 +35,7 @@ type IUserHandler interface { CheckId(w http.ResponseWriter, r *http.Request) CheckEmail(w http.ResponseWriter, r *http.Request) + GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) // Admin Admin_Create(w http.ResponseWriter, r *http.Request) @@ -44,16 +46,18 @@ type IUserHandler interface { } type UserHandler struct { - usecase usecase.IUserUsecase - authUsecase usecase.IAuthUsecase - roleUsecase usecase.IRoleUsecase + usecase usecase.IUserUsecase + authUsecase usecase.IAuthUsecase + roleUsecase usecase.IRoleUsecase + permissionUsecase usecase.IPermissionUsecase } func NewUserHandler(h usecase.Usecase) IUserHandler { return &UserHandler{ - usecase: h.User, - authUsecase: h.Auth, - roleUsecase: h.Role, + usecase: h.User, + authUsecase: h.Auth, + roleUsecase: h.Role, + permissionUsecase: h.Permission, } } @@ -667,8 +671,86 @@ func (u UserHandler) CheckEmail(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, out) } +// GetPermissionsByAccountId godoc +// +// @Tags Users +// @Summary Get Permissions By Account ID +// @Description Get Permissions By Account ID +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param accountId path string true "Account ID" +// @Success 200 {object} domain.GetUsersPermissionsResponse +// @Router /organizations/{organizationId}/users/{accountId}/permissions [get] +// @Security JWT +func (u UserHandler) GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) { + var organizationId, accountId string + + vars := mux.Vars(r) + if v, ok := vars["accountId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + accountId = v + } + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + user, err := u.usecase.GetByAccountId(r.Context(), accountId, organizationId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + var roles []*model.Role + roles = append(roles, &user.Role) + + var permissionSets []*model.PermissionSet + for _, role := range roles { + permissionSet, err := u.permissionUsecase.GetPermissionSetByRoleId(r.Context(), role.ID) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + permissionSets = append(permissionSets, permissionSet) + } + + mergedPermissionSet := u.permissionUsecase.MergePermissionWithOrOperator(r.Context(), permissionSets...) + + var permissions domain.MergedPermissionSetResponse + permissions.Dashboard = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Dashboard) + permissions.Stack = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Stack) + permissions.Policy = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Policy) + permissions.ProjectManagement = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.ProjectManagement) + permissions.Notification = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Notification) + permissions.Configuration = convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Configuration) + + var out domain.GetUsersPermissionsResponse + out.Permissions = &permissions + ResponseJSON(w, r, http.StatusOK, out) + +} + +func convertModelToMergedPermissionSetResponse(ctx context.Context, permission *model.Permission) *domain.MergePermissionResponse { + var permissionResponse domain.MergePermissionResponse + + permissionResponse.Key = permission.Key + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed + } + + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToMergedPermissionSetResponse(ctx, child)) + } + + return &permissionResponse +} + // Admin_Create godoc -// @Tags Admin +// @Tags Users // @Summary Create user by admin // @Description Create user by admin // @Accept json @@ -766,7 +848,7 @@ func (u UserHandler) Admin_Create(w http.ResponseWriter, r *http.Request) { } // Admin_List godoc -// @Tags Admin +// @Tags Users // @Summary Get user list by admin // @Description Get user list by admin // @Accept json @@ -815,7 +897,7 @@ func (u UserHandler) Admin_List(w http.ResponseWriter, r *http.Request) { // Admin_Get godoc // -// @Tags Admin +// @Tags Users // @Summary Get user detail by admin // @Description Get user detail by admin // @Accept json @@ -859,7 +941,7 @@ func (u UserHandler) Admin_Get(w http.ResponseWriter, r *http.Request) { } // Admin_Delete godoc -// @Tags Admin +// @Tags Users // @Summary Delete user by admin // @Description Delete user by admin // @Accept json @@ -925,7 +1007,7 @@ func (u UserHandler) Admin_Delete(w http.ResponseWriter, r *http.Request) { // Admin_Update godoc // -// @Tags Admin +// @Tags Users // @Summary Update user by admin // @Description Update user by admin // @Accept json diff --git a/internal/model/permission.go b/internal/model/permission.go index 87e06b6e..7fafc931 100644 --- a/internal/model/permission.go +++ b/internal/model/permission.go @@ -276,14 +276,14 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.ListPolicyTemplate, - api.GetPolicyTemplate, - api.GetPolicyTemplateDeploy, - api.ListPolicyTemplateStatistics, - api.ListPolicyTemplateVersions, - api.GetPolicyTemplateVersion, - api.ExistsPolicyTemplateName, - api.ExistsPolicyTemplateKind, + api.Admin_ListPolicyTemplate, + api.Admin_GetPolicyTemplate, + api.Admin_GetPolicyTemplateDeploy, + api.Admin_ListPolicyTemplateStatistics, + api.Admin_ListPolicyTemplateVersions, + api.Admin_GetPolicyTemplateVersion, + api.Admin_ExistsPolicyTemplateName, + api.Admin_ExistsPolicyTemplateKind, // ClusterPolicyStatus api.ListClusterPolicyStatus, @@ -317,8 +317,8 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.CreatePolicyTemplate, - api.CreatePolicyTemplateVersion, + api.Admin_CreatePolicyTemplate, + api.Admin_CreatePolicyTemplateVersion, // Policy api.SetMandatoryPolicies, @@ -336,7 +336,7 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.UpdatePolicyTemplate, + api.Admin_UpdatePolicyTemplate, // ClusterPolicyStatus api.UpdateClusterPolicyTemplateStatus, @@ -359,8 +359,8 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.DeletePolicyTemplate, - api.DeletePolicyTemplateVersion, + api.Admin_DeletePolicyTemplate, + api.Admin_DeletePolicyTemplateVersion, // Policy api.DeletePolicy, diff --git a/internal/route/route.go b/internal/route/route.go index 51e639c5..36a6fbd4 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -123,6 +123,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile/password", customMiddleware.Handle(internalApi.UpdateMyPassword, http.HandlerFunc(userHandler.UpdateMyPassword))).Methods(http.MethodPut) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile/next-password-change", customMiddleware.Handle(internalApi.RenewPasswordExpiredDate, http.HandlerFunc(userHandler.RenewPasswordExpiredDate))).Methods(http.MethodPut) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile", customMiddleware.Handle(internalApi.DeleteMyProfile, http.HandlerFunc(userHandler.DeleteMyProfile))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/permissions", customMiddleware.Handle(internalApi.GetPermissionsByAccountId, http.HandlerFunc(userHandler.GetPermissionsByAccountId))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/users", customMiddleware.Handle(internalApi.Admin_CreateUser, http.HandlerFunc(userHandler.Admin_Create))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/users", customMiddleware.Handle(internalApi.Admin_ListUser, http.HandlerFunc(userHandler.Admin_List))).Methods(http.MethodGet) @@ -284,6 +285,8 @@ 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}/roles/{roleId}/permissions", customMiddleware.Handle(internalApi.GetPermissionsByRoleId, http.HandlerFunc(roleHandler.GetPermissionsByRoleId))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/permissions", customMiddleware.Handle(internalApi.UpdatePermissionsByRoleId, http.HandlerFunc(roleHandler.UpdatePermissionsByRoleId))).Methods(http.MethodPut) // Admin r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.Admin_ListTksRoles, http.HandlerFunc(roleHandler.Admin_ListTksRoles))).Methods(http.MethodGet) @@ -292,9 +295,6 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa 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.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/permissions", customMiddleware.Handle(internalApi.GetPermissionsByAccountId, http.HandlerFunc(permissionHandler.GetPermissionsByAccountId))).Methods(http.MethodGet) policyTemplateHandler := delivery.NewPolicyTemplateHandler(usecaseFactory) r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplate))).Methods(http.MethodGet) From 46b43877b38465da280c99fe98e85d9743896031 Mon Sep 17 00:00:00 2001 From: "taekyu.kang" Date: Mon, 25 Mar 2024 15:21:21 +0900 Subject: [PATCH 18/18] tirivial. fix swagger error --- api/swagger/docs.go | 29 +++++++++++++++++-- api/swagger/swagger.json | 29 +++++++++++++++++-- api/swagger/swagger.yaml | 22 ++++++++++++-- .../http/system-notification-template.go | 2 +- 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index e2878fbc..c11e1f95 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -1306,7 +1306,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest" } } } @@ -9609,11 +9609,34 @@ const docTemplate = `{ } } }, - "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest": { "type": "object", + "required": [ + "metricQuery", + "name", + "organizationIds" + ], "properties": { - "id": { + "description": { "type": "string" + }, + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse" + } + }, + "metricQuery": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 27347d15..2dc52e14 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -1300,7 +1300,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest" } } } @@ -9603,11 +9603,34 @@ } } }, - "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest": { "type": "object", + "required": [ + "metricQuery", + "name", + "organizationIds" + ], "properties": { - "id": { + "description": { "type": "string" + }, + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse" + } + }, + "metricQuery": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index b4b04e01..b879b812 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -1146,10 +1146,26 @@ definitions: id: type: string type: object - github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest: properties: - id: + description: + type: string + metricParameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MetricParameterResponse' + type: array + metricQuery: + type: string + name: type: string + organizationIds: + items: + type: string + type: array + required: + - metricQuery + - name + - organizationIds type: object github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleRequest: properties: @@ -4330,7 +4346,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest' security: - JWT: [] summary: Create alert template. ADMIN ONLY diff --git a/internal/delivery/http/system-notification-template.go b/internal/delivery/http/system-notification-template.go index 73053e17..e4b11b01 100644 --- a/internal/delivery/http/system-notification-template.go +++ b/internal/delivery/http/system-notification-template.go @@ -33,7 +33,7 @@ func NewSystemNotificationTemplateHandler(h usecase.Usecase) *SystemNotification // @Description Create alert template. ADMIN ONLY // @Accept json // @Produce json -// @Success 200 {object} domain.CreateSystemNotificationTemplateResponse +// @Success 200 {object} domain.CreateSystemNotificationTemplateRequest // @Router /admin/system-notification-templates [post] // @Security JWT func (h *SystemNotificationTemplateHandler) CreateSystemNotificationTemplate(w http.ResponseWriter, r *http.Request) {