From 9db7f4e9d567e90b7bad00d368a54e7c45f34deb Mon Sep 17 00:00:00 2001 From: dogfootman Date: Mon, 22 Sep 2025 16:44:27 +0900 Subject: [PATCH 01/14] =?UTF-8?q?=EC=97=AD=ED=95=A0=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EC=84=A0:=20Platform-Role=20?= =?UTF-8?q?=EC=97=AD=ED=95=A0=20=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=B0=8F=20=EC=83=9D=EC=84=B1=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=B4=EC=99=84,=20=EC=97=AD=ED=95=A0=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EC=8B=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=EB=A1=A4?= =?UTF-8?q?=EB=B0=B1=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handler/role_handler.go | 62 +++++++++++++- src/service/keycloak_service.go | 139 +++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 7 deletions(-) diff --git a/src/handler/role_handler.go b/src/handler/role_handler.go index 136fde05..5f42938e 100644 --- a/src/handler/role_handler.go +++ b/src/handler/role_handler.go @@ -1465,10 +1465,36 @@ func (h *RoleHandler) AssignPlatformRole(c echo.Context) error { return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("플랫폼 역할 할당 실패: %v", err)}) } - // keycloak 역할 할당 - err := h.keycloakService.AssignRealmRoleToUser(c.Request().Context(), user.KcId, req.RoleName) - //err = h.keycloakService.AssignRealmRoleToUser(c.Request().Context(), user.KcId, req.RoleName) + // Keycloak role 존재 여부 확인 및 생성 + roleExists, err := h.keycloakService.CheckRealmRoleExists(c.Request().Context(), req.RoleName) if err != nil { + log.Printf("Failed to check realm role existence: %v", err) + // DB 롤백을 위해 역할 제거 + if rollbackErr := h.roleService.RemovePlatformRole(userID, roleID); rollbackErr != nil { + log.Printf("Failed to rollback platform role assignment: %v", rollbackErr) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("키클로크 역할 확인 실패: %v", err)}) + } + + // Keycloak role이 없으면 생성하고 생성 완료까지 대기 + if !roleExists { + if err := h.keycloakService.CreateRealmRoleAndWait(c.Request().Context(), req.RoleName); err != nil { + log.Printf("Failed to create realm role: %v", err) + // DB 롤백을 위해 역할 제거 + if rollbackErr := h.roleService.RemovePlatformRole(userID, roleID); rollbackErr != nil { + log.Printf("Failed to rollback platform role assignment: %v", rollbackErr) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("키클로크 역할 생성 실패: %v", err)}) + } + } + + // Keycloak에 역할 할당 + if err := h.keycloakService.AssignRealmRoleToUser(c.Request().Context(), user.KcId, req.RoleName); err != nil { + log.Printf("Failed to assign realm role: %v", err) + // DB 롤백을 위해 역할 제거 + if rollbackErr := h.roleService.RemovePlatformRole(userID, roleID); rollbackErr != nil { + log.Printf("Failed to rollback platform role assignment: %v", rollbackErr) + } return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("키클로크 역할 할당 실패: %v", err)}) } } @@ -1541,11 +1567,39 @@ func (h *RoleHandler) RemovePlatformRole(c echo.Context) error { return c.JSON(http.StatusBadRequest, map[string]string{"error": "역할 ID 또는 역할명이 필요합니다"}) } - // 역할 제거 + // 사용자 정보 조회 (Keycloak ID 필요) + user, err := h.userService.GetUserByID(c.Request().Context(), userID) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("사용자 조회 실패: %v", err)}) + } + if user == nil { + return c.JSON(http.StatusNotFound, map[string]string{"error": "해당 사용자를 찾을 수 없습니다"}) + } + + // 역할 정보 조회 (RoleName 필요) - Platform 역할로 조회 + role, err := h.roleService.GetRoleByID(roleID, constants.RoleTypePlatform) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("역할 조회 실패: %v", err)}) + } + if role == nil { + return c.JSON(http.StatusNotFound, map[string]string{"error": "해당 역할을 찾을 수 없습니다"}) + } + + // DB에서 역할 제거 if err := h.roleService.RemovePlatformRole(userID, roleID); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("플랫폼 역할 제거 실패: %v", err)}) } + // Keycloak에서 역할 제거 + if err := h.keycloakService.RemoveRealmRoleFromUser(c.Request().Context(), user.KcId, role.Name); err != nil { + log.Printf("Failed to remove realm role from user: %v", err) + // DB 롤백을 위해 역할 재할당 + if rollbackErr := h.roleService.AssignPlatformRole(userID, roleID); rollbackErr != nil { + log.Printf("Failed to rollback platform role removal: %v", rollbackErr) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("키클로크 역할 제거 실패: %v", err)}) + } + return c.JSON(http.StatusOK, map[string]string{"message": "플랫폼 역할이 성공적으로 제거되었습니다"}) } diff --git a/src/service/keycloak_service.go b/src/service/keycloak_service.go index c2148126..d042abfb 100644 --- a/src/service/keycloak_service.go +++ b/src/service/keycloak_service.go @@ -62,6 +62,14 @@ type KeycloakService interface { GetImpersonationTokenByServiceAccount(ctx context.Context) (*gocloak.JWT, error) // AssignRealmRoleToUser assigns a realm role to a user AssignRealmRoleToUser(ctx context.Context, kcUserId, roleName string) error + // CheckRealmRoleExists checks if a realm role exists + CheckRealmRoleExists(ctx context.Context, roleName string) (bool, error) + // CreateRealmRole creates a realm role + CreateRealmRole(ctx context.Context, roleName string) error + // CreateRealmRoleAndWait creates a realm role and waits for it to be available + CreateRealmRoleAndWait(ctx context.Context, roleName string) error + // RemoveRealmRoleFromUser removes a realm role from a user + RemoveRealmRoleFromUser(ctx context.Context, kcUserId, roleName string) error // IssueWorkspaceTicket 워크스페이스 티켓을 발행합니다. IssueWorkspaceTicket(ctx context.Context, kcUserId string, workspaceID uint) (string, map[string]interface{}, error) // 기본 Role 정의 @@ -811,7 +819,7 @@ func (s *keycloakService) SetupInitialKeycloakAdmin(ctx context.Context, adminTo // 3. platformAdmin 역할 할당. patformAdmin 역할이 없으면 생성 log.Printf("[DEBUG] Setting platformAdmin role") platformAdminRoleName := "platformAdmin" - platformAdminRole := gocloak.Role{} + var platformAdminRole gocloak.Role realmRole, err := config.KC.Client.GetRealmRole(ctx, adminToken.AccessToken, config.KC.Realm, platformAdminRoleName) if err != nil { log.Printf("failed to get platformAdmin role: %v", err) @@ -839,7 +847,7 @@ func (s *keycloakService) SetupInitialKeycloakAdmin(ctx context.Context, adminTo } else { platformAdminRole = *realmRole } - log.Printf("platformAdminRole: %s", platformAdminRole) + log.Printf("platformAdminRole: %+v", platformAdminRole) // platformAdminRole, err := config.KC.Client.GetRealmRole(ctx, adminToken.AccessToken, config.KC.Realm, "platformAdmin") // if err != nil { // log.Printf("failed to get platformAdmin role: %v", err) @@ -1139,7 +1147,7 @@ func (s *keycloakService) GetImpersonationTokenByAdminToken(ctx context.Context, return "", fmt.Errorf("failed to decode impersonation response: %w", err) } - log.Printf("[DEBUG] Impersonation resp.StatusCode : ", resp.StatusCode) //if result.Token == "" { + log.Printf("[DEBUG] Impersonation resp.StatusCode : %d", resp.StatusCode) //if result.Token == "" { // resp.Body를 다시 읽기 위해 전체 바이트로 읽음 respBody, _ := ioutil.ReadAll(resp.Body) log.Printf("[DEBUG] Impersonation response body: %s", string(respBody)) @@ -1336,3 +1344,128 @@ func (s *keycloakService) GetClientCredentialsToken(ctx context.Context) (*goclo //log.Printf("[DEBUG] client credentials token: %s", token) return token, nil } + +// CheckRealmRoleExists checks if a realm role exists +func (s *keycloakService) CheckRealmRoleExists(ctx context.Context, roleName string) (bool, error) { + if config.KC == nil || config.KC.Client == nil { + return false, fmt.Errorf("keycloak configuration not initialized") + } + + token, err := config.KC.GetAdminToken(ctx) + if err != nil { + return false, fmt.Errorf("failed to get admin token: %w", err) + } + + _, err = config.KC.Client.GetRealmRole(ctx, token.AccessToken, config.KC.Realm, roleName) + if err != nil { + // Role not found + return false, nil + } + return true, nil +} + +// CreateRealmRole creates a realm role +func (s *keycloakService) CreateRealmRole(ctx context.Context, roleName string) error { + if config.KC == nil || config.KC.Client == nil { + return fmt.Errorf("keycloak configuration not initialized") + } + + token, err := config.KC.GetAdminToken(ctx) + if err != nil { + return fmt.Errorf("failed to get admin token: %w", err) + } + + newRole := gocloak.Role{ + Name: &roleName, + Description: gocloak.StringP("Platform role"), + } + + result, err := config.KC.Client.CreateRealmRole(ctx, token.AccessToken, config.KC.Realm, newRole) + if err != nil { + return fmt.Errorf("failed to create realm role %s: %w", roleName, err) + } + + log.Printf("Successfully created realm role: %s, result: %s", roleName, result) + return nil +} + +// RemoveRealmRoleFromUser removes a realm role from a user +func (s *keycloakService) RemoveRealmRoleFromUser(ctx context.Context, kcUserId, roleName string) error { + if config.KC == nil || config.KC.Client == nil { + return fmt.Errorf("keycloak configuration not initialized") + } + + token, err := config.KC.GetAdminToken(ctx) + if err != nil { + return fmt.Errorf("failed to get admin token: %w", err) + } + + // Get the role by name + roles, err := config.KC.Client.GetRealmRoles(ctx, token.AccessToken, config.KC.Realm, gocloak.GetRoleParams{ + Search: &roleName, + }) + if err != nil { + return fmt.Errorf("failed to get realm role %s: %w", roleName, err) + } + if len(roles) == 0 { + log.Printf("Realm role %s not found, skipping removal", roleName) + return nil + } + if len(roles) > 1 { + log.Printf("Warning: Found multiple roles matching '%s'. Using the first one.", roleName) + } + + // Remove the role from the user + err = config.KC.Client.DeleteRealmRoleFromUser(ctx, token.AccessToken, config.KC.Realm, kcUserId, []gocloak.Role{*roles[0]}) + if err != nil { + return fmt.Errorf("failed to remove realm role %s from user %s: %w", roleName, kcUserId, err) + } + + log.Printf("Successfully removed realm role %s from user %s", roleName, kcUserId) + return nil +} + +// CreateRealmRoleAndWait creates a realm role and waits for it to be available +func (s *keycloakService) CreateRealmRoleAndWait(ctx context.Context, roleName string) error { + if config.KC == nil || config.KC.Client == nil { + return fmt.Errorf("keycloak configuration not initialized") + } + + token, err := config.KC.GetAdminToken(ctx) + if err != nil { + return fmt.Errorf("failed to get admin token: %w", err) + } + + newRole := gocloak.Role{ + Name: &roleName, + Description: gocloak.StringP("Platform role"), + } + + result, err := config.KC.Client.CreateRealmRole(ctx, token.AccessToken, config.KC.Realm, newRole) + if err != nil { + return fmt.Errorf("failed to create realm role %s: %w", roleName, err) + } + + log.Printf("Realm role creation initiated: %s, result: %s", roleName, result) + + // 1초마다 최대 20번 시도하여 role 생성 확인 + maxRetries := 20 + for i := 0; i < maxRetries; i++ { + time.Sleep(1 * time.Second) + + exists, err := s.CheckRealmRoleExists(ctx, roleName) + if err != nil { + log.Printf("Failed to check realm role existence (attempt %d/%d): %v", i+1, maxRetries, err) + continue + } + + if exists { + log.Printf("Realm role %s successfully created and available (attempt %d/%d)", roleName, i+1, maxRetries) + return nil + } + + log.Printf("Realm role %s not yet available, waiting... (attempt %d/%d)", roleName, i+1, maxRetries) + } + + return fmt.Errorf("realm role %s was not available after %d attempts", roleName, maxRetries) +} From 2a8e3b77dec5735abe1211141321b8a268cf957b Mon Sep 17 00:00:00 2001 From: dogfootman Date: Thu, 30 Oct 2025 14:36:29 +0900 Subject: [PATCH 02/14] Update Go version to 1.25.0 in Dockerfile and go.mod, and add new indirect dependencies for OpenTelemetry and other libraries. --- Dockerfile.mciammanager | 4 ++-- src/go.mod | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Dockerfile.mciammanager b/Dockerfile.mciammanager index 778d42d5..bc12ad38 100644 --- a/Dockerfile.mciammanager +++ b/Dockerfile.mciammanager @@ -1,6 +1,6 @@ # --- Build Stage --- -FROM golang:1.23.1-alpine AS builder -# Updated Go version to match go.mod (1.23.1) +FROM golang:1.25.0-alpine AS builder +# Updated Go version to match go.mod (1.25.0) # Install build dependencies if any (e.g., git for private modules) RUN apk add --no-cache git diff --git a/src/go.mod b/src/go.mod index 8c7eec50..18db9811 100644 --- a/src/go.mod +++ b/src/go.mod @@ -1,6 +1,6 @@ module github.com/m-cmp/mc-iam-manager -go 1.23.1 +go 1.25.0 require ( cloud.google.com/go/iam v1.5.2 @@ -10,7 +10,9 @@ require ( github.com/aws/aws-sdk-go-v2/credentials v1.17.67 github.com/aws/aws-sdk-go-v2/service/iam v1.41.1 github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 + github.com/go-playground/validator/v10 v10.26.0 github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 github.com/labstack/echo/v4 v4.13.3 github.com/lib/pq v1.10.9 @@ -19,6 +21,7 @@ require ( github.com/swaggo/swag v1.16.4 google.golang.org/api v0.232.0 gopkg.in/yaml.v3 v3.0.1 + gorm.io/datatypes v1.2.5 gorm.io/driver/postgres v1.5.11 gorm.io/driver/sqlite v1.5.7 gorm.io/gorm v1.26.1 @@ -42,17 +45,17 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/swag v0.23.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-sql-driver/mysql v1.9.2 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -75,6 +78,11 @@ require ( github.com/swaggo/files/v2 v2.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/net v0.39.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect @@ -89,6 +97,5 @@ require ( google.golang.org/grpc v1.72.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gorm.io/datatypes v1.2.5 // indirect gorm.io/driver/mysql v1.5.7 // indirect ) From dc660711a0497549879820fe7c4ba7d6b67437f0 Mon Sep 17 00:00:00 2001 From: dogfootman Date: Fri, 14 Nov 2025 11:49:51 +0900 Subject: [PATCH 03/14] Update API documentation and handlers for project and workspace management. Added endpoint to retrieve workspaces assigned to a project, updated project creation to optionally assign a workspace, and improved error handling. Modified swagger definitions for better clarity and added new constants for authentication methods. --- .gitignore | 5 +- docker-compose.yaml | 4 +- docs/swagger.json | 7231 ++++++++++++++++++++---- docs/swagger.yaml | 5163 +++++++++++++---- src/docs/docs.go | 168 +- src/docs/swagger.json | 168 +- src/handler/project_handler.go | 73 +- src/handler/workspace_handler.go | 25 +- src/main.go | 2 + src/model/request.go | 1 + src/repository/workspace_repository.go | 19 +- src/service/project_service.go | 113 +- 12 files changed, 10757 insertions(+), 2215 deletions(-) diff --git a/.gitignore b/.gitignore index 17f0f9a2..f19de1f4 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ asset/mcmpapi/mcmpapi-*.yaml dockercontainer-volume container-volume old -dockerfiles/nginx/nginx.conf \ No newline at end of file +dockerfiles/nginx/nginx.conf + +# Documentation (local only) +doc/ \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 89870a01..f38bda0e 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -14,7 +14,7 @@ services: ##### MC-INFRA-CONNECTOR ######################################################################################################################### mc-infra-connector: - image: cloudbaristaorg/cb-spider:0.11.1 + image: cloudbaristaorg/cb-spider:edge pull_policy: missing container_name: mc-infra-connector platform: linux/amd64 @@ -47,7 +47,7 @@ services: ##### MC-INFRA-MANAGER ######################################################################################################################### mc-infra-manager: - image: cloudbaristaorg/cb-tumblebug:0.11.1 + image: cloudbaristaorg/cb-tumblebug:edge container_name: mc-infra-manager pull_policy: missing platform: linux/amd64 diff --git a/docs/swagger.json b/docs/swagger.json index 0d6928bf..23b1d808 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -6,17 +6,46 @@ "contact": {}, "version": "1.0" }, - "host": "localhost:3000", + "host": "localhost", "basePath": "/api/v1", "paths": { - "/api/call": { - "post": { - "security": [ - { - "BearerAuth": [] - } + "/api/auth/certs": { + "get": { + "description": "Retrieve authentication certificates for MC-IAM-Manager to be used in target frameworks for token validation.", + "consumes": [ + "application/json" ], - "description": "Executes a defined MCMP API action with query parameters and a request body.", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get authentication certificates", + "operationId": "mciamAuthCerts", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/auth/login": { + "post": { + "description": "Authenticate user and issue JWT token.", "consumes": [ "application/json" ], @@ -24,29 +53,41 @@ "application/json" ], "tags": [ - "McmpAPI" + "auth" ], - "summary": "Call an external MCMP API action (Generic Request)", + "summary": "User login", + "operationId": "mciamLogin", "parameters": [ { - "description": "Generic API Call Request", - "name": "callRequest", + "description": "Login Credentials", + "name": "credentials", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/mcmpapi.ServiceApiCallRequest" + "$ref": "#/definitions/idp.UserLogin" } } ], + "responses": {} + } + }, + "/api/auth/logout": { + "post": { + "description": "Invalidate the user's refresh token and log out.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout user", + "operationId": "mciamLogout", "responses": { "200": { - "description": "External API Response (structure depends on the called API)", - "schema": { - "type": "object" - } - }, - "400": { - "description": "error: Invalid request body or parameters", + "description": "OK", "schema": { "type": "object", "additionalProperties": { @@ -54,17 +95,53 @@ } } }, - "404": { - "description": "error: Service or action not found", + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { "type": "string" } } + } + } + } + }, + "/api/auth/refresh": { + "post": { + "description": "Refresh JWT access token using a valid refresh token.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Refresh access token", + "operationId": "mciamRefreshToken", + "parameters": [ + { + "description": "Refresh token", + "name": "refresh_token", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "New token information", + "schema": { + "type": "object", + "additionalProperties": true + } }, - "500": { - "description": "error: Internal server error or failed to call external API", + "400": { + "description": "error: Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -72,8 +149,8 @@ } } }, - "503": { - "description": "error: External API unavailable", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -84,9 +161,9 @@ } } }, - "/api/permissions": { + "/api/auth/temp-credential-csps": { "get": { - "description": "모든 권한 목록을 조회합니다.", + "description": "Get temporary credential provider information for AWS and GCP", "consumes": [ "application/json" ], @@ -94,23 +171,29 @@ "application/json" ], "tags": [ - "permissions" + "auth" ], - "summary": "권한 목록 조회", + "summary": "Get temporary credential CSP information", + "operationId": "mciamGetTempCredentialProviders", "responses": { "200": { - "description": "OK", + "description": "CSP temporary credential information", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Permission" - } + "type": "object", + "additionalProperties": true } } } - }, + } + }, + "/api/auth/validate": { "post": { - "description": "새로운 권한을 생성합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Validate the current access token and refresh if expired", "consumes": [ "application/json" ], @@ -118,33 +201,58 @@ "application/json" ], "tags": [ - "permissions" + "auth" ], - "summary": "권한 생성", + "summary": "Validate access token", + "operationId": "mciamValidateToken", "parameters": [ { - "description": "권한 정보", - "name": "permission", + "description": "Refresh token", + "name": "refresh_token", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Permission" + "type": "string" } } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "Token validation result with new token if refreshed", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "error: Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", "schema": { - "$ref": "#/definitions/model.Permission" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } } }, - "/api/permissions/{id}": { + "/api/csp-credentials": { "get": { - "description": "ID로 특정 권한을 조회합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "모든 CSP 인증 정보 목록을 조회합니다", "consumes": [ "application/json" ], @@ -152,29 +260,80 @@ "application/json" ], "tags": [ - "permissions" + "csp-credentials" + ], + "summary": "CSP 인증 정보 목록 조회", + "operationId": "mciamListCredentials", + "responses": {} + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "새로운 CSP 인증 정보를 생성합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" ], - "summary": "ID로 권한 조회", + "tags": [ + "csp-credentials" + ], + "summary": "새 CSP 인증 정보 생성", + "operationId": "mciamCreateCredential", + "responses": {} + } + }, + "/api/csp-credentials/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "특정 CSP 인증 정보를 ID로 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 ID로 조회", + "operationId": "mciamGetCredentialByID", "parameters": [ { "type": "string", - "description": "권한 ID", + "description": "Credential ID", "name": "id", "in": "path", "required": true } ], "responses": { - "200": { - "description": "OK", + "404": { + "description": "error: Credential not found", "schema": { - "$ref": "#/definitions/model.Permission" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } }, "put": { - "description": "기존 권한을 수정합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 업데이트합니다", "consumes": [ "application/json" ], @@ -182,38 +341,38 @@ "application/json" ], "tags": [ - "permissions" + "csp-credentials" ], - "summary": "권한 수정", + "summary": "CSP 인증 정보 업데이트", + "operationId": "mciamUpdateCredential", "parameters": [ { "type": "string", - "description": "권한 ID", + "description": "Credential ID", "name": "id", "in": "path", "required": true - }, - { - "description": "권한 정보", - "name": "permission", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Permission" - } } ], "responses": { - "200": { - "description": "OK", + "404": { + "description": "error: Credential not found", "schema": { - "$ref": "#/definitions/model.Permission" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } }, "delete": { - "description": "권한을 삭제합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 삭제합니다", "consumes": [ "application/json" ], @@ -221,13 +380,14 @@ "application/json" ], "tags": [ - "permissions" + "csp-credentials" ], - "summary": "권한 삭제", + "summary": "CSP 인증 정보 삭제", + "operationId": "mciamDeleteCredential", "parameters": [ { "type": "string", - "description": "권한 ID", + "description": "Credential ID", "name": "id", "in": "path", "required": true @@ -236,13 +396,40 @@ "responses": { "204": { "description": "No Content" + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } } } } }, - "/api/platform-roles": { - "get": { - "description": "모든 플랫폼 역할을 조회합니다.", + "/api/initial-admin": { + "post": { + "description": "Creates the initial platform admin user with necessary permissions. platform admin 생성인데", "consumes": [ "application/json" ], @@ -250,23 +437,34 @@ "application/json" ], "tags": [ - "platform-roles" + "admin" + ], + "summary": "Setup initial platform admin", + "operationId": "setupInitialAdmin", + "parameters": [ + { + "description": "Setup Initial Admin Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.SetupInitialAdminRequest" + } + } ], - "summary": "플랫폼 역할 목록 조회", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.PlatformRole" - } + "$ref": "#/definitions/model.Response" } } } - }, + } + }, + "/api/mcmp-api-permission-action-mappings": { "post": { - "description": "새로운 플랫폼 역할을 생성합니다.", + "description": "Creates a new mapping between a permission and an API action", "consumes": [ "application/json" ], @@ -274,33 +472,31 @@ "application/json" ], "tags": [ - "platform-roles" + "mcmp-api-permission-action-mappings" ], - "summary": "플랫폼 역할 생성", + "summary": "Create permission-action mapping", + "operationId": "createMcmpApiPermissionActionMapping", "parameters": [ { - "description": "Platform Role", - "name": "role", + "description": "Mapping to create", + "name": "mapping", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PlatformRole" + "$ref": "#/definitions/mcmpapi.McmpApiPermissionActionMapping" } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/model.PlatformRole" - } + "204": { + "description": "No Content" } } } }, - "/api/platform-roles/{id}": { - "get": { - "description": "ID로 플랫폼 역할을 조회합니다.", + "/api/mcmp-api-permission-action-mappings/actions/list": { + "post": { + "description": "Returns all workspace actions mapped to a specific permission", "consumes": [ "application/json" ], @@ -308,14 +504,15 @@ "application/json" ], "tags": [ - "platform-roles" + "mcmp-api-permission-action-mappings" ], - "summary": "플랫폼 역할 조회", + "summary": "Get workspace actions by permission ID", + "operationId": "listWorkspaceActionsByPermissionID", "parameters": [ { - "type": "integer", - "description": "Platform Role ID", - "name": "id", + "type": "string", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true } @@ -324,13 +521,18 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.PlatformRole" + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" + } } } } - }, - "put": { - "description": "기존 플랫폼 역할을 수정합니다.", + } + }, + "/api/mcmp-api-permission-action-mappings/actions/{actionId}/permissions": { + "get": { + "description": "Returns all permissions mapped to a specific API action", "consumes": [ "application/json" ], @@ -338,67 +540,35 @@ "application/json" ], "tags": [ - "platform-roles" + "mcmp-api-permission-action-mappings" ], - "summary": "플랫폼 역할 수정", + "summary": "Get permissions by action ID", + "operationId": "listPermissionsByActionID", "parameters": [ { "type": "integer", - "description": "Platform Role ID", - "name": "id", + "description": "Action ID", + "name": "actionId", "in": "path", "required": true - }, - { - "description": "Platform Role", - "name": "role", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.PlatformRole" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.PlatformRole" + "type": "array", + "items": { + "type": "string" + } } } } - }, - "delete": { - "description": "플랫폼 역할을 삭제합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "platform-roles" - ], - "summary": "플랫폼 역할 삭제", - "parameters": [ - { - "type": "integer", - "description": "Platform Role ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - } } }, - "/api/roles/{roleType}/{roleId}/permissions": { - "get": { - "description": "특정 역할의 권한 목록을 조회합니다.", + "/api/mcmp-api-permission-action-mappings/list": { + "post": { + "description": "Returns all platform actions mapped to a specific permission", "consumes": [ "application/json" ], @@ -406,21 +576,15 @@ "application/json" ], "tags": [ - "permissions" + "mcmp-api-permission-action-mappings" ], - "summary": "역할의 권한 목록 조회", + "summary": "List platform actions by permission ID", + "operationId": "listPlatformActions", "parameters": [ { "type": "string", - "description": "역할 타입 ('platform' or 'workspace')", - "name": "roleType", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "역할 ID", - "name": "roleId", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true } @@ -431,16 +595,16 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/model.Permission" + "$ref": "#/definitions/mcmpapi.McmpApiAction" } } } } } }, - "/api/roles/{roleType}/{roleId}/permissions/{permissionId}": { - "post": { - "description": "역할에 권한을 할당합니다.", + "/api/mcmp-api-permission-action-mappings/permissions/{permissionId}/actions/{actionId}": { + "put": { + "description": "Updates an existing mapping between a permission and an API action", "consumes": [ "application/json" ], @@ -448,40 +612,49 @@ "application/json" ], "tags": [ - "permissions" + "mcmp-api-permission-action-mappings" ], - "summary": "역할에 권한 할당", + "summary": "Update permission-action mapping", + "operationId": "updateMapping", "parameters": [ { "type": "string", - "description": "역할 타입 ('platform' or 'workspace')", - "name": "roleType", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true }, { "type": "integer", - "description": "역할 ID", - "name": "roleId", + "description": "Action ID", + "name": "actionId", "in": "path", "required": true }, { - "type": "string", - "description": "권한 ID", - "name": "permissionId", - "in": "path", - "required": true + "description": "Updated mapping", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/mcmpapi.McmpApiPermissionActionMapping" + } } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } } } }, "delete": { - "description": "역할에서 권한을 제거합니다.", + "description": "Deletes a mapping between a permission and an API action", "consumes": [ "application/json" ], @@ -489,28 +662,22 @@ "application/json" ], "tags": [ - "permissions" + "mcmp-api-permission-action-mappings" ], - "summary": "역할에서 권한 제거", + "summary": "Delete permission-action mapping", + "operationId": "deleteMapping", "parameters": [ { "type": "string", - "description": "역할 타입 ('platform' or 'workspace')", - "name": "roleType", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true }, { "type": "integer", - "description": "역할 ID", - "name": "roleId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "권한 ID", - "name": "permissionId", + "description": "Action ID", + "name": "actionId", "in": "path", "required": true } @@ -522,9 +689,9 @@ } } }, - "/auth/login": { - "post": { - "description": "사용자 ID와 비밀번호로 로그인하여 JWT 토큰을 발급받습니다.", + "/api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions": { + "get": { + "description": "Returns all platform actions mapped to a specific permission", "consumes": [ "application/json" ], @@ -532,69 +699,34 @@ "application/json" ], "tags": [ - "auth" + "mcmp-api-permission-action-mappings" ], - "summary": "로그인", + "summary": "Get platform actions by permission ID", + "operationId": "getPlatformActionsByPermissionID", "parameters": [ { - "description": "로그인 정보 (Id, Password)", - "name": "login", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/idp.UserLogin" - } + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "로그인 성공 및 토큰 정보 (gocloak.JWT 구조체와 유사)", - "schema": { - "type": "object", - "additionalProperties": true - } - }, - "400": { - "description": "error: 잘못된 요청 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "error: 인증 실패 (자격 증명 오류)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "403": { - "description": "error: 계정이 비활성화되었거나 승인 대기 중입니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류 (Keycloak 통신, DB 동기화 등)", + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" } } } } } }, - "/mcmp-apis": { - "get": { + "/api/mcmp-apis/list": { + "post": { "security": [ { "BearerAuth": [] @@ -611,6 +743,7 @@ "McmpAPI" ], "summary": "Get All Stored MCMP API Definitions", + "operationId": "listServicesAndActions", "parameters": [ { "type": "string", @@ -644,7 +777,7 @@ } } }, - "/mcmp-apis/call": { + "/api/mcmp-apis/mcmpApiCall": { "post": { "security": [ { @@ -662,6 +795,7 @@ "McmpAPI" ], "summary": "Call an external MCMP API action (Structured Request)", + "operationId": "mcmpApiCall", "parameters": [ { "description": "API Call Request", @@ -669,7 +803,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/mcmpapi.McmpApiCallRequest" + "$ref": "#/definitions/model.McmpApiCallRequest" } } ], @@ -719,109 +853,7 @@ } } }, - "/mcmp-apis/sync": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Triggers the synchronization of MCMP API definitions from the configured YAML URL to the database.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "McmpAPI // Updated tag" - ], - "summary": "Sync MCMP API Definitions", - "responses": { - "200": { - "description": "message: Successfully triggered MCMP API sync\" // Updated message", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "message: Failed to trigger MCMP API sync\" // Updated message", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/mcmp-apis/test/mc-infra-manager/getallns": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Calls the GetAllNs action of the mc-infra-manager service via the CallApi service.", - "produces": [ - "application/json" - ], - "tags": [ - "McmpAPI", - "Test" - ], - "summary": "Test Call to mc-infra-manager GetAllNs", - "responses": { - "200": { - "description": "Response from mc-infra-manager GetAllNs", - "schema": { - "type": "object" - } - }, - "400": { - "description": "error: Bad Request (e.g., invalid parameters)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: Service or Action Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "503": { - "description": "error: External API Service Unavailable", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/mcmp-apis/{serviceName}": { + "/api/mcmp-apis/name/{serviceName}": { "put": { "security": [ { @@ -839,6 +871,7 @@ "McmpAPI" ], "summary": "Update MCMP API Service Definition", + "operationId": "UpdateFrameworkService", "parameters": [ { "type": "string", @@ -897,7 +930,7 @@ } } }, - "/mcmp-apis/{serviceName}/versions/{version}/activate": { + "/api/mcmp-apis/name/{serviceName}/versions/{version}/activate": { "put": { "security": [ { @@ -915,6 +948,7 @@ "McmpAPI" ], "summary": "Set Active Version for a Service", + "operationId": "setActiveVersion", "parameters": [ { "type": "string", @@ -965,14 +999,14 @@ } } }, - "/menus": { - "get": { + "/api/mcmp-apis/syncMcmpAPIs": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "현재 로그인한 사용자의 Platform Role에 따라 접근 가능한 메뉴 목록을 트리 구조로 조회합니다.", + "description": "Triggers the synchronization of MCMP API definitions from the configured YAML URL to the database.", "consumes": [ "application/json" ], @@ -980,21 +1014,13 @@ "application/json" ], "tags": [ - "menus" + "McmpAPI" ], - "summary": "현재 사용자의 메뉴 트리 조회", + "summary": "Sync MCMP API Definitions", + "operationId": "syncMcmpAPIs", "responses": { "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.MenuTreeNode" - } - } - }, - "401": { - "description": "error: Unauthorized", + "description": "message: Successfully triggered MCMP API sync", "schema": { "type": "object", "additionalProperties": { @@ -1003,7 +1029,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "message: Failed to trigger MCMP API sync", "schema": { "type": "object", "additionalProperties": { @@ -1012,75 +1038,34 @@ } } } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "새로운 메뉴를 생성합니다", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "menus" - ], - "summary": "새 메뉴 생성", - "parameters": [ - { - "description": "Menu Info", - "name": "menu", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Menu" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/model.Menu" - } - } - } } }, - "/menus/all": { + "/api/mcmp-apis/test/mc-infra-manager/getallns": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "모든 메뉴 목록을 트리 구조로 조회합니다. 관리자 권한이 필요합니다.", - "consumes": [ - "application/json" - ], + "description": "Calls the GetAllNs action of the mc-infra-manager service via the CallApi service.", "produces": [ "application/json" ], "tags": [ - "menus" + "McmpAPI", + "Test" ], - "summary": "모든 메뉴 트리 조회 (관리자용)", + "summary": "Test Call to mc-infra-manager GetAllNs", + "operationId": "testCallGetAllNs", "responses": { "200": { - "description": "OK", + "description": "Response from mc-infra-manager GetAllNs", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.MenuTreeNode" - } + "type": "object" } }, - "401": { - "description": "error: Unauthorized", + "400": { + "description": "error: Bad Request (e.g., invalid parameters)", "schema": { "type": "object", "additionalProperties": { @@ -1088,8 +1073,8 @@ } } }, - "403": { - "description": "error: Forbidden", + "404": { + "description": "error: Service or Action Not Found", "schema": { "type": "object", "additionalProperties": { @@ -1098,59 +1083,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/menus/register-from-body": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "요청 본문에 포함된 YAML 텍스트를 파싱하여 메뉴를 데이터베이스에 등록하거나 업데이트합니다. Content-Type은 text/plain, text/yaml, application/yaml 등을 권장합니다.", - "consumes": [ - "text/plain" - ], - "produces": [ - "application/json" - ], - "tags": [ - "menus" - ], - "summary": "요청 본문의 YAML 내용으로 메뉴 등록/업데이트", - "parameters": [ - { - "example": "\"menus:\\n - id: new-item\\n parentid: dashboard\\n displayname: New Menu Item\\n restype: menu\\n isaction: false\\n priority: 10\\n menunumber: 9999\"", - "description": "Menu definitions in YAML format (must contain 'menus:' root key)", - "name": "yaml", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "message: Successfully registered menus from request body", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "error: 잘못된 요청 본문 또는 YAML 형식 오류", + "description": "error: Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1158,8 +1091,8 @@ } } }, - "500": { - "description": "error: 서버 내부 오류", + "503": { + "description": "error: External API Service Unavailable", "schema": { "type": "object", "additionalProperties": { @@ -1170,14 +1103,14 @@ } } }, - "/menus/register-from-yaml": { + "/api/menus": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "filePath 쿼리 파라미터로 지정된 로컬 YAML 파일 또는 파라미터가 없을 경우 .env 파일의 MCWEBCONSOLE_MENUYAML URL에서 메뉴를 가져와 데이터베이스에 등록/업데이트합니다. URL에서 가져올 경우 asset/menu/menu.yaml에 저장됩니다.", + "description": "Create a new menu", "consumes": [ "application/json" ], @@ -1187,45 +1120,37 @@ "tags": [ "menus" ], - "summary": "YAML 파일 또는 URL에서 메뉴 등록/업데이트", + "summary": "Create new menu", + "operationId": "createMenu", "parameters": [ { - "type": "string", - "description": "YAML 파일 경로 (선택 사항, 없으면 .env의 URL 또는 기본 로컬 경로 사용)", - "name": "filePath", - "in": "query" + "description": "Menu Info", + "name": "menu", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Menu" + } } ], "responses": { - "200": { - "description": "message: Successfully registered menus from YAML", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 실패 메시지", + "201": { + "description": "Created", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/model.Menu" } } } } }, - "/menus/{id}": { - "get": { + "/api/menus/id/{menuId}": { + "put": { "security": [ { "BearerAuth": [] } ], - "description": "특정 메뉴를 ID로 조회합니다", + "description": "Update menu information", "consumes": [ "application/json" ], @@ -1235,7 +1160,8 @@ "tags": [ "menus" ], - "summary": "메뉴 ID로 조회", + "summary": "Update menu information", + "operationId": "updateMenu", "parameters": [ { "type": "string", @@ -1243,6 +1169,15 @@ "name": "id", "in": "path", "required": true + }, + { + "description": "Menu Info", + "name": "menu", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Menu" + } } ], "responses": { @@ -1254,13 +1189,13 @@ } } }, - "put": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "메뉴 정보를 업데이트합니다", + "description": "Get menu details by ID", "consumes": [ "application/json" ], @@ -1270,23 +1205,15 @@ "tags": [ "menus" ], - "summary": "메뉴 정보 업데이트", + "summary": "Get menu by ID", + "operationId": "getMenuByID", "parameters": [ { "type": "string", "description": "Menu ID", - "name": "id", + "name": "menuId", "in": "path", "required": true - }, - { - "description": "Menu Info", - "name": "menu", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Menu" - } } ], "responses": { @@ -1304,7 +1231,7 @@ "BearerAuth": [] } ], - "description": "메뉴를 삭제합니다", + "description": "Delete a menu", "consumes": [ "application/json" ], @@ -1314,7 +1241,8 @@ "tags": [ "menus" ], - "summary": "메뉴 삭제", + "summary": "Delete menu", + "operationId": "deleteMenu", "parameters": [ { "type": "string", @@ -1331,14 +1259,14 @@ } } }, - "/projects": { - "get": { + "/api/menus/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "모든 프로젝트 목록을 조회합니다 (연결된 워크스페이스 정보 포함).", + "description": "List all menus as a tree structure. Admin permission required.", "consumes": [ "application/json" ], @@ -1346,16 +1274,35 @@ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "모든 프로젝트 조회", + "summary": "List all menus", + "operationId": "listMenus", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.MenuTreeNode" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" } } }, @@ -1369,14 +1316,16 @@ } } } - }, + } + }, + "/api/menus/platform-roles": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "새로운 프로젝트를 생성합니다.", + "description": "Create a new menu mapping", "consumes": [ "application/json" ], @@ -1384,17 +1333,18 @@ "application/json" ], "tags": [ - "projects" + "menu" ], - "summary": "프로젝트 생성", + "summary": "Create menu mapping", + "operationId": "createMenusRolesMapping", "parameters": [ { - "description": "프로젝트 정보 (ID, CreatedAt, UpdatedAt, Workspaces 제외)", - "name": "project", + "description": "Menu Mapping", + "name": "mapping", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.CreateMenuMappingRequest" } } ], @@ -1402,11 +1352,14 @@ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/model.Project" + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "400": { - "description": "error: 잘못된 요청 형식", + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -1415,7 +1368,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1424,16 +1377,14 @@ } } } - } - }, - "/projects/name/{name}": { - "get": { + }, + "delete": { "security": [ { "BearerAuth": [] } ], - "description": "이름으로 특정 프로젝트를 조회합니다 (연결된 워크스페이스 정보 포함).", + "description": "Delete the mapping between a platform role and a menu.", "consumes": [ "application/json" ], @@ -1441,27 +1392,36 @@ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "이름으로 프로젝트 조회", + "summary": "Delete platform role-menu mapping", + "operationId": "deleteMenusRolesMapping", "parameters": [ { "type": "string", - "description": "프로젝트 이름", - "name": "name", - "in": "path", - "required": true + "description": "Platform Role ID", + "name": "roleId", + "in": "query" + }, + { + "type": "string", + "description": "Menu ID", + "name": "menuId", + "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "message: Menu mapping deleted successfully", "schema": { - "$ref": "#/definitions/model.Project" + "type": "object", + "additionalProperties": { + "type": "string" + } } }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "400": { + "description": "error: platform role and menu ID are required", "schema": { "type": "object", "additionalProperties": { @@ -1481,14 +1441,14 @@ } } }, - "/projects/{id}": { - "get": { + "/api/menus/platform-roles/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "ID로 특정 프로젝트를 조회합니다 (연결된 워크스페이스 정보 포함).", + "description": "List menus mapped to a specific platform role.", "consumes": [ "application/json" ], @@ -1496,36 +1456,36 @@ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "ID로 프로젝트 조회", + "summary": "List menus mapped to platform role", + "operationId": "listMappedMenusByRole", "parameters": [ { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/model.Project" - } + "type": "string", + "description": "Platform Role ID", + "name": "roleId", + "in": "query" }, - "400": { - "description": "error: 잘못된 프로젝트 ID", + { + "type": "string", + "description": "Menu ID", + "name": "menuId", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/model.Menu" } } }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "400": { + "description": "error: platform role is required", "schema": { "type": "object", "additionalProperties": { @@ -1543,14 +1503,16 @@ } } } - }, - "put": { + } + }, + "/api/menus/setup/initial-menus": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "기존 프로젝트 정보를 부분적으로 수정합니다.", + "description": "Register or update menus from a local YAML file specified by the filePath query parameter, or from the MCWEBCONSOLE_MENUYAML URL in .env if not provided. If loaded from URL, the file is saved to asset/menu/menu.yaml.", "consumes": [ "application/json" ], @@ -1558,45 +1520,21 @@ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "프로젝트 수정", + "summary": "Register/Update menus from YAML file or URL", + "operationId": "registerMenusFromYAML", "parameters": [ { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "수정할 필드와 값 (예: {\\", - "name": "updates", - "in": "body", - "required": true, - "schema": { - "type": "object" - } + "type": "string", + "description": "YAML file path (optional, uses .env URL or default local path if not provided)", + "name": "filePath", + "in": "query" } ], "responses": { "200": { - "description": "업데이트된 프로젝트 정보", - "schema": { - "$ref": "#/definitions/model.Project" - } - }, - "400": { - "description": "error: 잘못된 요청 형식 또는 ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "description": "message: Successfully registered menus from YAML", "schema": { "type": "object", "additionalProperties": { @@ -1605,7 +1543,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "error: 실패 메시지", "schema": { "type": "object", "additionalProperties": { @@ -1614,39 +1552,42 @@ } } } - }, - "delete": { + } + }, + "/api/menus/setup/initial-menus2": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "프로젝트를 삭제합니다. 연결된 워크스페이스와의 관계도 해제됩니다.", + "description": "Parse YAML text in the request body and register or update menus in the database. Recommended Content-Type: text/plain, text/yaml, application/yaml.", "consumes": [ - "application/json" + "text/plain" ], "produces": [ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "프로젝트 삭제", + "summary": "Register/Update menus from YAML in request body", + "operationId": "registerMenusFromBody", "parameters": [ { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true + "example": "\"menus:\\n - id: new-item\\n parentid: dashboard\\n displayname: New Menu Item\\n restype: menu\\n isaction: false\\n priority: 10\\n menunumber: 9999\"", + "description": "Menu definitions in YAML format (must contain 'menus:' root key)", + "name": "yaml", + "in": "body", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 프로젝트 ID", + "200": { + "description": "message: Successfully registered menus from request body", "schema": { "type": "object", "additionalProperties": { @@ -1654,8 +1595,8 @@ } } }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "400": { + "description": "error: 잘못된 요청 본문 또는 YAML 형식 오류", "schema": { "type": "object", "additionalProperties": { @@ -1675,14 +1616,14 @@ } } }, - "/projects/{id}/workspaces/{workspaceId}": { + "/api/menus/tree/list": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 프로젝트에 워크스페이스를 연결합니다.", + "description": "List all menus as a tree structure. Admin permission required.", "consumes": [ "application/json" ], @@ -1690,31 +1631,22 @@ "application/json" ], "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } + "menus" ], + "summary": "List all menus Tree", + "operationId": "listMenusTree", "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.MenuTreeNode" + } + } }, - "400": { - "description": "error: 잘못된 ID 형식", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -1722,8 +1654,8 @@ } } }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -1741,14 +1673,16 @@ } } } - }, - "delete": { + } + }, + "/api/menus/user-menu-tree": { + "get": { "security": [ { "BearerAuth": [] } ], - "description": "특정 프로젝트에서 워크스페이스 연결을 해제합니다.", + "description": "Get menu tree based on user's platform roles", "consumes": [ "application/json" ], @@ -1756,40 +1690,22 @@ "application/json" ], "tags": [ - "projects" - ], - "summary": "프로젝트에서 워크스페이스 연결 해제", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } + "menus" ], + "summary": "Get user menu tree by platform roles", + "operationId": "getUserMenuTree", "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", + "200": { + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/model.MenuTreeNode" } } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1800,70 +1716,101 @@ } } }, - "/readyz": { - "get": { - "description": "애플리케이션의 준비 상태를 확인합니다. status=detail 쿼리 파라미터로 상세 상태를 확인할 수 있습니다.", + "/api/permissions/mciam": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new permission with the specified information.", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Health" + "permissions" ], - "summary": "애플리케이션 준비 상태 확인", + "summary": "Create new permission", + "operationId": "createMciamPermission", "parameters": [ { - "type": "string", - "description": "상세 상태 확인 여부 ('detail')", - "name": "status", - "in": "query" + "description": "Permission Info", + "name": "permission", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.MciamPermission" + } } ], "responses": { - "200": { - "description": "상세 상태 정보 (status=detail)", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/service.HealthStatus" + "$ref": "#/definitions/model.MciamPermission" } }, - "503": { - "description": "상세 상태 확인 중 오류 발생 시", + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/service.HealthStatus" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } } }, - "/user/workspaces": { + "/api/permissions/mciam/id/{id}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "현재 로그인한 사용자가 접근 가능한 워크스페이스 및 각 워크스페이스에서의 역할 목록을 조회합니다.", + "description": "Retrieve permission details by permission ID.", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "users", - "workspaces", - "roles", - "me" + "permissions" + ], + "summary": "Get permission by ID", + "operationId": "getMciamPermissionByID", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true + } ], - "summary": "내 워크스페이스 및 역할 목록 조회", "responses": { "200": { - "description": "성공 시 워크스페이스 및 역할 정보 목록 반환", + "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/service.WorkspaceRoleInfo" - } + "$ref": "#/definitions/model.MciamPermission" } }, - "401": { - "description": "error: Unauthorized", + "404": { + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -1872,7 +1819,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1883,51 +1830,37 @@ } } }, - "/users": { - "get": { + "/api/permissions/mciam/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "모든 사용자 목록을 조회합니다. 'admin' 또는 'platformAdmin' 역할이 필요합니다.", + "description": "Retrieve a list of all permissions.", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "users" + "permissions" ], - "summary": "사용자 목록 조회 (관리자용)", + "summary": "List all permissions", + "operationId": "listMciamPermissions", "responses": { "200": { - "description": "성공 시 사용자 목록 반환", + "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/model.User" - } - } - }, - "401": { - "description": "error: Unauthorized", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "403": { - "description": "error: Forbidden (권한 부족)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "$ref": "#/definitions/model.MciamPermission" } } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1936,43 +1869,88 @@ } } } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "responses": {} } }, - "/users/{id}": { + "/api/permissions/mciam/{id}": { "put": { "security": [ { "BearerAuth": [] } ], + "description": "Update the details of an existing permission.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "permissions" + ], + "summary": "Update permission", + "operationId": "updateMciamPermission", "parameters": [ { - "type": "integer", - "description": "User DB ID", - "name": "id", + "type": "string", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true + }, + { + "description": "Permission Info", + "name": "permission", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.MciamPermission" + } } ], - "responses": {} - } - }, - "/users/{id}/approve": { - "post": { + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.MciamPermission" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { "security": [ { "BearerAuth": [] } ], - "description": "지정된 사용자를 활성화하고 시스템 사용을 승인합니다. 'admin' 또는 'platformadmin' 역할이 필요합니다.", + "description": "Delete a permission by its ID.", "consumes": [ "application/json" ], @@ -1980,14 +1958,15 @@ "application/json" ], "tags": [ - "users" + "permissions" ], - "summary": "사용자 승인 (관리자용)", + "summary": "Delete permission", + "operationId": "deleteMciamPermission", "parameters": [ { "type": "string", - "description": "사용자 Keycloak ID", - "name": "id", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true } @@ -1996,8 +1975,8 @@ "204": { "description": "No Content" }, - "400": { - "description": "error: 잘못된 사용자 ID", + "404": { + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2005,8 +1984,57 @@ } } }, - "401": { - "description": "error: Unauthorized", + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/projects": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Create new project", + "operationId": "createProject", + "parameters": [ + { + "description": "Project Info", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateProjectRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.Project" + } + }, + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2014,8 +2042,8 @@ } } }, - "403": { - "description": "error: Forbidden (권한 부족)", + "404": { + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2024,7 +2052,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2035,14 +2063,14 @@ } } }, - "/workspaces": { + "/api/projects/id/{projectId}/workspaces": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "모든 워크스페이스 목록을 조회합니다 (연결된 프로젝트 정보 포함).", + "description": "Retrieve list of workspaces that the project is assigned to", "consumes": [ "application/json" ], @@ -2050,9 +2078,19 @@ "application/json" ], "tags": [ - "workspaces" + "projects" + ], + "summary": "Get workspaces assigned to project", + "operationId": "getProjectWorkspaces", + "parameters": [ + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } ], - "summary": "모든 워크스페이스 조회", "responses": { "200": { "description": "OK", @@ -2063,8 +2101,26 @@ } } }, + "400": { + "description": "error: Invalid project ID", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Project not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { - "description": "error: 서버 내부 오류", + "description": "error: Internal server error", "schema": { "type": "object", "additionalProperties": { @@ -2073,14 +2129,16 @@ } } } - }, + } + }, + "/api/projects/list": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "새로운 워크스페이스를 생성합니다.", + "description": "Retrieve a list of all projects.", "consumes": [ "application/json" ], @@ -2088,38 +2146,22 @@ "application/json" ], "tags": [ - "workspaces" - ], - "summary": "워크스페이스 생성", - "parameters": [ - { - "description": "워크스페이스 정보 (ID, CreatedAt, UpdatedAt, Projects 제외)", - "name": "workspace", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Workspace" - } - } + "projects" ], + "summary": "List all projects", + "operationId": "listProjects", "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/model.Workspace" - } - }, - "400": { - "description": "error: 잘못된 요청 형식", + "200": { + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" } } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2130,14 +2172,14 @@ } } }, - "/workspaces/name/{name}": { + "/api/projects/name/{projectName}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "이름으로 특정 워크스페이스를 조회합니다 (연결된 프로젝트 정보 포함).", + "description": "Get project details by name", "consumes": [ "application/json" ], @@ -2145,13 +2187,14 @@ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "이름으로 워크스페이스 조회", + "summary": "Get project by name", + "operationId": "getProjectByName", "parameters": [ { "type": "string", - "description": "워크스페이스 이름", + "description": "Project Name", "name": "name", "in": "path", "required": true @@ -2161,11 +2204,11 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.Project" } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2174,7 +2217,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2185,14 +2228,14 @@ } } }, - "/workspaces/{id}": { + "/api/projects/{id}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "ID로 특정 워크스페이스를 조회합니다 (연결된 프로젝트 정보 포함).", + "description": "Retrieve project details by project ID.", "consumes": [ "application/json" ], @@ -2200,14 +2243,15 @@ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "ID로 워크스페이스 조회", + "summary": "Get project by ID", + "operationId": "getProjectByID", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", + "type": "string", + "description": "Project ID", + "name": "projectId", "in": "path", "required": true } @@ -2216,20 +2260,11 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.Workspace" - } - }, - "400": { - "description": "error: 잘못된 워크스페이스 ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/model.Project" } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2238,7 +2273,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2254,7 +2289,7 @@ "BearerAuth": [] } ], - "description": "기존 워크스페이스 정보를 부분적으로 수정합니다.", + "description": "Update the details of an existing project.", "consumes": [ "application/json" ], @@ -2262,36 +2297,37 @@ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "워크스페이스 수정", + "summary": "Update project", + "operationId": "updateProject", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", + "type": "string", + "description": "Project ID", + "name": "projectId", "in": "path", "required": true }, { - "description": "수정할 필드와 값 (예: {\\", - "name": "updates", + "description": "Project Info", + "name": "project", "in": "body", "required": true, "schema": { - "type": "object" + "$ref": "#/definitions/model.Project" } } ], "responses": { "200": { - "description": "업데이트된 워크스페이스 정보", + "description": "OK", "schema": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.Project" } }, "400": { - "description": "error: 잘못된 요청 형식 또는 ID", + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2300,7 +2336,7 @@ } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2309,7 +2345,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2325,7 +2361,7 @@ "BearerAuth": [] } ], - "description": "워크스페이스를 삭제합니다. 연결된 프로젝트와의 관계도 해제됩니다.", + "description": "Delete a project by its ID.", "consumes": [ "application/json" ], @@ -2333,14 +2369,15 @@ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "워크스페이스 삭제", + "summary": "Delete project", + "operationId": "deleteProject", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", + "type": "string", + "description": "Project ID", + "name": "projectId", "in": "path", "required": true } @@ -2349,17 +2386,8 @@ "204": { "description": "No Content" }, - "400": { - "description": "error: 잘못된 워크스페이스 ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2368,7 +2396,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2379,14 +2407,14 @@ } } }, - "/workspaces/{id}/projects": { - "get": { + "/api/projects/{id}/workspaces/{workspaceId}": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스 ID에 연결된 모든 프로젝트 목록을 조회합니다.", + "description": "프로젝트에 워크스페이스를 연결합니다.", "consumes": [ "application/json" ], @@ -2394,30 +2422,32 @@ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "워크스페이스에 연결된 프로젝트 목록 조회", + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", "parameters": [ { "type": "integer", - "description": "워크스페이스 ID", + "description": "프로젝트 ID", "name": "id", "in": "path", "required": true + }, + { + "type": "integer", + "description": "워크스페이스 ID", + "name": "workspaceId", + "in": "path", + "required": true } ], "responses": { - "200": { - "description": "성공 시 프로젝트 목록 반환", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Project" - } - } + "204": { + "description": "No Content" }, "400": { - "description": "error: 잘못된 워크스페이스 ID", + "description": "error: 잘못된 ID 형식", "schema": { "type": "object", "additionalProperties": { @@ -2426,7 +2456,7 @@ } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", "schema": { "type": "object", "additionalProperties": { @@ -2446,14 +2476,14 @@ } } }, - "/workspaces/{id}/projects/{projectId}": { + "/api/resource-types/cloud-resources": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스에 프로젝트를 연결합니다.", + "description": "새로운 리소스 타입을 생성합니다", "consumes": [ "application/json" ], @@ -2461,31 +2491,30 @@ "application/json" ], "tags": [ - "workspaces" + "resource-types" ], - "summary": "워크스페이스에 프로젝트 연결", + "summary": "Cloud에서 관리되는 Resource(vm, nlb, k8s 등의 그룹) 새 리소스 타입 생성", + "operationId": "createResourceType", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "프로젝트 ID", - "name": "projectId", - "in": "path", - "required": true + "description": "Resource Type Info", + "name": "resourceType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.ResourceType" + } } ], "responses": { - "204": { - "description": "No Content" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.ResourceType" + } }, "400": { - "description": "error: 잘못된 ID 형식", + "description": "error: Invalid request", "schema": { "type": "object", "additionalProperties": { @@ -2493,8 +2522,8 @@ } } }, - "404": { - "description": "error: 워크스페이스 또는 프로젝트를 찾을 수 없습니다", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2502,8 +2531,8 @@ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -2512,14 +2541,16 @@ } } } - }, - "delete": { + } + }, + "/api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId": { + "get": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스에서 프로젝트 연결을 해제합니다.", + "description": "특정 리소스 타입을 ID로 조회합니다", "consumes": [ "application/json" ], @@ -2527,31 +2558,28 @@ "application/json" ], "tags": [ - "workspaces" + "resource-types" ], - "summary": "워크스페이스에서 프로젝트 연결 해제", + "summary": "리소스 타입 ID로 조회", + "operationId": "getCloudResourceTypeByID", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", + "type": "string", + "description": "Resource Type ID", "name": "id", "in": "path", "required": true - }, - { - "type": "integer", - "description": "프로젝트 ID", - "name": "projectId", - "in": "path", - "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.ResourceType" + } }, - "400": { - "description": "error: 잘못된 ID 형식", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2559,8 +2587,17 @@ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Resource Type not found", "schema": { "type": "object", "additionalProperties": { @@ -2569,16 +2606,14 @@ } } } - } - }, - "/workspaces/{id}/users": { - "get": { + }, + "put": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스에 속한 모든 사용자와 각 사용자의 역할을 조회합니다.", + "description": "리소스 타입 정보를 업데이트합니다", "consumes": [ "application/json" ], @@ -2586,32 +2621,37 @@ "application/json" ], "tags": [ - "workspaces", - "users", - "roles" + "resource-types" ], - "summary": "워크스페이스 사용자 및 역할 목록 조회", + "summary": "리소스 타입 업데이트", + "operationId": "updateResourceType", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", + "type": "string", + "description": "Resource Type ID", "name": "id", "in": "path", "required": true + }, + { + "description": "Resource Type Info", + "name": "resourceType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.ResourceType" + } } ], "responses": { "200": { - "description": "성공 시 사용자 및 역할 목록 반환", + "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/service.UserWithRoles" - } + "$ref": "#/definitions/model.ResourceType" } }, "400": { - "description": "error: 잘못된 워크스페이스 ID", + "description": "error: Invalid request", "schema": { "type": "object", "additionalProperties": { @@ -2619,8 +2659,8 @@ } } }, - "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2628,8 +2668,17 @@ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Resource Type not found", "schema": { "type": "object", "additionalProperties": { @@ -2638,16 +2687,14 @@ } } } - } - }, - "/workspaces/{workspaceId}/users/{userId}/roles/{roleId}": { - "post": { + }, + "delete": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스 내의 사용자에게 특정 워크스페이스 역할을 할당합니다.", + "description": "리소스 타입을 삭제합니다", "consumes": [ "application/json" ], @@ -2655,30 +2702,15 @@ "application/json" ], "tags": [ - "workspaces", - "roles", - "users" + "resource-types" ], - "summary": "워크스페이스 사용자에게 역할 할당", + "summary": "리소스 타입 삭제", + "operationId": "deleteResourceType", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "사용자 DB ID (db_id)", - "name": "userId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 역할 ID", - "name": "roleId", + "type": "string", + "description": "Resource Type ID", + "name": "id", "in": "path", "required": true } @@ -2687,8 +2719,17 @@ "204": { "description": "No Content" }, - "400": { - "description": "error: 잘못된 ID 형식", + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -2697,16 +2738,48 @@ } }, "404": { - "description": "error: 사용자, 역할 또는 워크스페이스를 찾을 수 없습니다", + "description": "error: Resource Type not found", "schema": { "type": "object", "additionalProperties": { "type": "string" } } + } + } + } + }, + "/api/resource-types/cloud-resources/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "모든 리소스 타입 목록을 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "resource-types" + ], + "summary": "리소스 타입 목록 조회", + "operationId": "listCloudResourceTypes", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.ResourceType" + } + } }, - "409": { - "description": "error: 역할이 해당 워크스페이스에 속하지 않음", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2714,8 +2787,8 @@ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -2724,14 +2797,16 @@ } } } - }, - "delete": { + } + }, + "/api/roles": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스 내의 사용자에게서 특정 워크스페이스 역할을 제거합니다.", + "description": "Create a new role", "consumes": [ "application/json" ], @@ -2739,40 +2814,30 @@ "application/json" ], "tags": [ - "workspaces", - "roles", - "users" + "roles" ], - "summary": "워크스페이스 사용자 역할 제거", + "summary": "Create role", + "operationId": "createRole", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "사용자 DB ID (db_id)", - "name": "userId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 역할 ID", - "name": "roleId", - "in": "path", - "required": true + "description": "Role Info", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } } ], "responses": { - "204": { - "description": "No Content" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } }, "400": { - "description": "error: 잘못된 ID 형식", + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2780,8 +2845,51 @@ } } }, - "404": { - "description": "error: 역할 또는 워크스페이스를 찾을 수 없습니다\" // User existence check is optional here", + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/assign/platform-role": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Assign a platform role to a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Assign platform role", + "operationId": "assignPlatformRole", + "parameters": [ + { + "description": "Platform Role Assignment Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", "schema": { "type": "object", "additionalProperties": { @@ -2789,8 +2897,8 @@ } } }, - "409": { - "description": "error: 역할이 해당 워크스페이스에 속하지 않음", + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2799,7 +2907,7 @@ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2809,176 +2917,4797 @@ } } } - } - }, - "definitions": { - "idp.UserLogin": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "password": { + }, + "/api/roles/assign/workspace-role": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Assign a workspace role to a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Assign workspace role", + "operationId": "assignWorkspaceRole", + "parameters": [ + { + "description": "Workspace Role Assignment Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignWorkspaceRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new csp role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create csp role", + "operationId": "createCspRole", + "parameters": [ + { + "description": "CSP Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new mapping between role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create role-CSP role mapping", + "operationId": "addCspRoleMappings", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/batch": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create multiple new csp roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create multiple csp roles", + "operationId": "createCspRoles", + "parameters": [ + { + "description": "Multiple CSP Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspRolesRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspRole" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/id/:roleId": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a mapping between role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role-CSP role mapping", + "operationId": "getCspRoleMappingByRoleId", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/id/{roleId}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update role information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Update csp role", + "operationId": "updateCspRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Role Info", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete csp role", + "operationId": "deleteCspRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a mapping between role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role-CSP role mapping", + "operationId": "listCspRoleMappings", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get csp role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get csp role by ID", + "operationId": "getCspRoleByID", + "parameters": [ + { + "type": "string", + "description": "CSP Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a list of all csp roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List csp roles", + "operationId": "listCSPRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get csp role details by Name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get csp role by Name", + "operationId": "getCspRoleByName", + "parameters": [ + { + "type": "string", + "description": "CSP Role Name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role by ID", + "operationId": "getRoleByRoleID", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the details of an existing role.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Update role", + "operationId": "updateRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Role Info", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a role by its name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete role", + "operationId": "deleteRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/id/{roleId}/assign": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Assign a role to a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Assign role", + "operationId": "assignRole", + "parameters": [ + { + "description": "Role Assignment Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/id/{roleId}/unassign": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a role from a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Remove role", + "operationId": "removeRole", + "parameters": [ + { + "description": "Role Removal Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of all roles.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List all roles", + "operationId": "listRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/csp-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by csp role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List users by csp role", + "operationId": "listUsersByCspRole", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List role master mappings", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List role master mappings", + "operationId": "listRoleMasterMappings", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/platform-roles/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by platform role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List users by platform role", + "operationId": "listUsersByPlatformRole", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/role/id/:roleId": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get role master mappings", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role master mappings", + "operationId": "getRoleMasterMappings", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/workspace-roles/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by workspace role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List users by workspace role", + "operationId": "listUsersByWorkspaceRole", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/menu-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a list of all menu roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List menu roles", + "operationId": "listPlatformRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve role details by role name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role by Name", + "operationId": "getRoleByRoleName", + "parameters": [ + { + "type": "string", + "description": "Role name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/platform-roles": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new menu role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create menu role", + "operationId": "createPlatformRole", + "parameters": [ + { + "description": "Menu Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/platform-roles/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get platform role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get platform role by ID", + "operationId": "getPlatformRoleByID", + "parameters": [ + { + "type": "string", + "description": "Platform Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a platform role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete platform role", + "operationId": "deletePlatformRole", + "parameters": [ + { + "type": "string", + "description": "Platform Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/platform-roles/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get menu role details by Name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get menu role by Name", + "operationId": "getPlatformRoleByName", + "parameters": [ + { + "type": "string", + "description": "Menu Role Name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/unassign/csp-roles": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a mapping between workspace role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete workspace role-CSP role mapping", + "operationId": "removeCspRoleMappings", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/unassign/platform-role": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a platform role from a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Remove platform role", + "operationId": "removePlatformRole", + "parameters": [ + { + "description": "Platform Role Removal Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/unassign/workspace-role": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a workspace role from a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Remove workspace role", + "operationId": "removeWorkspaceRole", + "parameters": [ + { + "description": "Workspace Role Removal Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new workspace role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create workspace role", + "operationId": "createWorkspaceRole", + "parameters": [ + { + "description": "Workspace Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspace role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get workspace role by ID", + "operationId": "getWorkspaceRoleByID", + "parameters": [ + { + "type": "string", + "description": "Workspace Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a workspace role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete workspace role", + "operationId": "deleteWorkspaceRole", + "parameters": [ + { + "type": "string", + "description": "Workspace Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a list of all workspace roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List workspace roles", + "operationId": "listWorkspaceRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspace role details by Name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get workspace role by Name", + "operationId": "getWorkspaceRoleByName", + "parameters": [ + { + "type": "string", + "description": "Workspace Role Name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/{roleType}/{roleId}/mciam-permissions": { + "get": { + "description": "특정 역할의 MC-IAM 권한 ID 목록을 조회합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles", + "mciam-permissions" + ], + "summary": "역할의 MC-IAM 권한 목록 조회 - Renamed", + "operationId": "getRoleMciamPermissions", + "parameters": [ + { + "type": "string", + "description": "역할 타입 ('platform' or 'workspace')", + "name": "roleType", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "역할 ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "권한 ID 목록", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/{roleType}/{roleId}/mciam-permissions/{permissionId}": { + "post": { + "description": "역할에 MC-IAM 권한을 할당합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles", + "mciam-permissions" + ], + "summary": "역할에 MC-IAM 권한 할당 - Renamed", + "operationId": "assignMciamPermissionToRole", + "parameters": [ + { + "type": "string", + "description": "역할 타입 ('platform' or 'workspace')", + "name": "roleType", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "역할 ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "MC-IAM 권한 ID", + "name": "permissionId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "description": "역할에서 MC-IAM 권한을 제거합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles", + "mciam-permissions" + ], + "summary": "역할에서 MC-IAM 권한 제거 - Renamed", + "operationId": "removeMciamPermissionFromRole", + "parameters": [ + { + "type": "string", + "description": "역할 타입 ('platform' or 'workspace')", + "name": "roleType", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "역할 ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "MC-IAM 권한 ID", + "name": "permissionId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/setup/check-user-roles": { + "get": { + "description": "Check all roles assigned to a user. 특정 유저가 가진 role 목록을 조회합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Check user roles", + "operationId": "checkUserRoles", + "parameters": [ + { + "type": "string", + "description": "Username to check roles", + "name": "username", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/model.Response" + } + } + } + } + }, + "/api/setup/initial-role-menu-permission": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSV 파일을 읽어서 메뉴 권한을 초기화합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Initialize menu permissions from CSV", + "operationId": "initializeMenuPermissions", + "parameters": [ + { + "type": "string", + "description": "CSV file path (optional, uses default if not provided)", + "name": "filePath", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/model.Response" + } + } + } + } + }, + "/api/setup/sync-projects": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "mc-infra-manager의 네임스페이스 목록을 조회하여 로컬 DB에 없는 프로젝트를 추가합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "mc-infra-manager와 프로젝트 동기화", + "operationId": "syncProjects", + "responses": { + "200": { + "description": "message: Project synchronization successful", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류 또는 동기화 실패", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new user with the specified information.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Create new user", + "operationId": "createUser", + "parameters": [ + { + "description": "User Info", + "name": "user", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.User" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve user details by user ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user by ID", + "operationId": "getUserByID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/status": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update user status (active/inactive)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Update user status", + "operationId": "updateUserStatus", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Status", + "name": "status", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UserStatusRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/workspaces/id/{workspaceId}/roles/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspaces and roles for a specific user and workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user workspace and workspace roles by user ID and workspace ID", + "operationId": "getUserWorkspaceAndWorkspaceRolesByUserIDAndWorkspaceID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/workspaces/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspaces for a specific user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user workspaces by user ID", + "operationId": "getUserWorkspacesByUserID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/workspaces/roles/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspaces and roles for a specific user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user workspace and workspace roles by user ID", + "operationId": "getUserWorkspaceAndWorkspaceRolesByUserID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/kc/{kcUserId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get user details by KcID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user by KcID", + "operationId": "getUserByKcID", + "parameters": [ + { + "type": "string", + "description": "User KcID", + "name": "kcUserId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of all users.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List all users", + "operationId": "listUsers", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.User" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/menus-tree/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the menu tree accessible to the current user's platform role.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "menus" + ], + "summary": "Get current user's menu tree", + "operationId": "listUserMenuTree", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.MenuTreeNode" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/menus/list": { + "post": { + "description": "Get the menu list accessible to the current user's platform role.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "menus" + ], + "summary": "Get current user's menu list", + "operationId": "listUserMenu", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Menu" + } + } + } + } + } + }, + "/api/users/name/{username}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get user details by username", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user by username", + "operationId": "getUserByUsername", + "parameters": [ + { + "type": "string", + "description": "Username", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/workspaces/id/{workspaceId}/projects/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List projects for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List user projects by workspace", + "operationId": "listUserProjectsByWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/workspaces/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List workspaces for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List user workspaces", + "operationId": "listUserWorkspaces", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/workspaces/roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List workspaces and roles for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List user workspace and roles", + "operationId": "listUserWorkspaceAndWorkspaceRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the details of an existing user.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Update user", + "operationId": "updateUser", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Info", + "name": "user", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.User" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a user by their ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Delete user", + "operationId": "deleteUser", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new workspace with the specified information. Optionally assign existing projects to the workspace.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Create new workspace", + "operationId": "createWorkspace", + "parameters": [ + { + "description": "Workspace Info", + "name": "workspace", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Workspace" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/assign/projects": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a project to a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Add project to workspace", + "operationId": "addProjectToWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "400": { + "description": "error: Invalid request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace or Project not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve workspace details by workspace ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Get workspace by ID", + "operationId": "getWorkspaceByID", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the details of an existing workspace.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Update workspace", + "operationId": "updateWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Workspace Info", + "name": "workspace", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Workspace" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a workspace by its ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Delete workspace", + "operationId": "deleteWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}/projects/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve project list belonging to specific workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace projects", + "operationId": "getWorkspaceProjectsByWorkspaceId", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}/users/id/{userId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get roles assigned to a user in a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get user workspace roles", + "operationId": "getUserWorkspaceRoles", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}/users/list": { + "post": { + "description": "Retrieve users and roles list belonging to workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List users and roles by workspace", + "operationId": "listUsersAndRolesByWorkspace", + "parameters": [ + { + "type": "integer", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + }, + "400": { + "description": "error: Invalid workspace ID", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of all workspaces.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List all workspaces", + "operationId": "listWorkspaces", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/name/{workspaceName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve specific workspace by name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Get workspace by name", + "operationId": "getWorkspaceByName", + "parameters": [ + { + "type": "string", + "description": "Workspace Name", + "name": "workspaceName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/projects/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve project list belonging to specific workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace projects", + "operationId": "listWorkspaceProjects", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/temporary-credentials": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get temporary credentials for CSP", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "Get temporary credentials", + "operationId": "mciamGetTemporaryCredentials", + "responses": {} + } + }, + "/api/workspaces/unassign/projects": { + "delete": { + "description": "Remove a project from a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Remove project from workspace", + "operationId": "removeProjectFromWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, + "/api/workspaces/users-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve the list of users and roles assigned to the workspace.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List users and roles in workspace", + "operationId": "listAllWorkspaceUsersAndRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.WorkspaceWithUsersAndRoles" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by workspace criteria", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace users", + "operationId": "listWorkspaceUsers", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.WorkspaceWithUsersAndRoles" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/workspace-ticket": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Set workspace ticket", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Set workspace ticket", + "operationId": "mciamWorkspaceTicket", + "responses": { + "200": { + "description": "message: Workspace ticket set successfully", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a user to a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Add user to workspace", + "operationId": "addUserToWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users/{userId}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a user from a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Remove user from workspace", + "operationId": "removeUserFromWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/readyz": { + "get": { + "description": "Check the health status of the service.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Health check", + "operationId": "mciamCheckHealth", + "parameters": [ + { + "type": "string", + "description": "Detail check components (nginx,db,keycloak,all)", + "name": "detail", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + }, + "definitions": { + "constants.AuthMethod": { + "type": "string", + "enum": [ + "OIDC", + "SAML" + ], + "x-enum-varnames": [ + "AuthMethodOIDC", + "AuthMethodSAML" + ] + }, + "constants.CSPType": { + "type": "string", + "enum": [ + "aws", + "gcp", + "azure" + ], + "x-enum-varnames": [ + "CSPTypeAWS", + "CSPTypeGCP", + "CSPTypeAzure" + ] + }, + "constants.IAMRoleType": { + "type": "string", + "enum": [ + "platform", + "workspace", + "csp" + ], + "x-enum-comments": { + "RoleTypeCSP": "CSP 역할", + "RoleTypePlatform": "플랫폼 역할", + "RoleTypeWorkspace": "워크스페이스 역할" + }, + "x-enum-descriptions": [ + "플랫폼 역할", + "워크스페이스 역할", + "CSP 역할" + ], + "x-enum-varnames": [ + "RoleTypePlatform", + "RoleTypeWorkspace", + "RoleTypeCSP" + ] + }, + "idp.UserLogin": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "mcmpapi.McmpApiAction": { + "type": "object", + "properties": { + "actionName": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "description": "Auto-incrementing primary key", + "type": "integer" + }, + "method": { + "type": "string" + }, + "resourcePath": { + "type": "string" + }, + "serviceName": { + "description": "Foreign key reference (indexed)", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "mcmpapi.McmpApiAuthInfo": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "mcmpapi.McmpApiDefinitions": { + "type": "object", + "properties": { + "serviceActions": { + "description": "Use renamed ServiceAction", + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/mcmpapi.McmpApiServiceAction" + } + } + }, + "services": { + "description": "Use renamed ServiceDefinition", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/mcmpapi.McmpApiServiceDefinition" + } + } + } + }, + "mcmpapi.McmpApiPermissionActionMapping": { + "type": "object", + "properties": { + "actionID": { + "type": "integer" + }, + "actionName": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "permissionID": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "mcmpapi.McmpApiServiceAction": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "method": { + "type": "string" + }, + "resourcePath": { "type": "string" } } }, - "mcmpapi.ApiQueryParam": { + "mcmpapi.McmpApiServiceDefinition": { "type": "object", - "required": [ - "key", - "value" - ], "properties": { - "key": { + "auth": { + "description": "Use renamed AuthInfo", + "allOf": [ + { + "$ref": "#/definitions/mcmpapi.McmpApiAuthInfo" + } + ] + }, + "baseURL": { "type": "string" }, - "value": { + "version": { "type": "string" } } }, - "mcmpapi.McmpApiAuthInfo": { + "model.AssignRoleRequest": { "type": "object", "properties": { - "password": { + "roleId": { + "description": "역할 ID (문자열로 받음)", "type": "string" }, - "type": { + "roleName": { + "description": "역할명", + "type": "string" + }, + "roleType": { + "description": "역할 타입 (platform/workspace)", + "type": "string" + }, + "userId": { + "description": "사용자 ID (문자열로 받음)", + "type": "string" + }, + "username": { + "description": "사용자명", + "type": "string" + }, + "workspaceId": { + "description": "워크스페이스 ID (문자열로 받음)", + "type": "string" + } + } + }, + "model.AssignWorkspaceRoleRequest": { + "type": "object", + "properties": { + "roleId": { + "description": "역할 ID (문자열로 받음)", + "type": "string" + }, + "roleName": { + "description": "역할명", + "type": "string" + }, + "userId": { + "description": "사용자 ID (문자열로 받음)", "type": "string" }, "username": { + "description": "사용자명", + "type": "string" + }, + "workspaceId": { + "description": "워크스페이스 ID (문자열로 받음)", + "type": "string" + } + } + }, + "model.CreateCspRoleRequest": { + "type": "object", + "properties": { + "cspRoleName": { + "description": "csp의 RoleName. 여러 role이 있기때문에 csp에 정의한 role로 구분하기 위해 사용", + "type": "string" + }, + "cspType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "iamIdentifier": { + "type": "string" + }, + "iamRoleId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idpIdentifier": { + "type": "string" + }, + "path": { + "type": "string" + }, + "status": { "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Tag" + } } } }, - "mcmpapi.McmpApiCallRequest": { + "model.CreateCspRolesRequest": { "type": "object", "required": [ - "actionName", - "serviceName" + "cspRoles" ], "properties": { - "actionName": { - "description": "Target action name (operationId)", + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" + } + } + } + }, + "model.CreateMenuMappingRequest": { + "type": "object", + "required": [ + "menuIds", + "roleId" + ], + "properties": { + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "roleId": { + "type": "string" + } + } + }, + "model.CreateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { "type": "string" }, - "requestParams": { - "description": "Parameters for the external API call", - "allOf": [ - { - "$ref": "#/definitions/mcmpapi.McmpApiRequestParams" - } - ] + "name": { + "type": "string" }, - "serviceName": { - "description": "Target service name", + "workspaceId": { + "description": "optional workspace to assign project to", "type": "string" } } }, - "mcmpapi.McmpApiDefinitions": { + "model.CreateRoleRequest": { "type": "object", + "required": [ + "name" + ], "properties": { - "serviceActions": { - "description": "Use renamed ServiceAction", - "type": "object", - "additionalProperties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/mcmpapi.McmpApiServiceAction" - } + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" } }, - "services": { - "description": "Use renamed ServiceDefinition", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/mcmpapi.McmpApiServiceDefinition" + "description": { + "type": "string" + }, + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "integer" + }, + "roleTypes": { + "description": "RoleTypes []constants.IAMRoleType `json:\"roleTypes\" validate:\"required,dive,oneof=platform workspace csp\"`", + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" } } } }, - "mcmpapi.McmpApiRequestParams": { + "model.CspRole": { "type": "object", "properties": { - "body": { - "description": "Request body (accept any JSON structure) - Changed from json.RawMessage for swag compatibility" + "create_date": { + "type": "string" }, - "pathParams": { - "description": "Parameters to replace in the resource path (e.g., {userId})", - "type": "object", - "additionalProperties": { + "created_at": { + "type": "string" + }, + "csp_type": { + "type": "string" + }, + "deleted_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "iam_identifier": { + "type": "string" + }, + "iam_role_id": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "idp_identifier": { + "type": "string" + }, + "max_session_duration": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "permissions": { + "type": "array", + "items": { "type": "string" } }, - "queryParams": { - "description": "Parameters to append as query string (?key=value)", - "type": "object", - "additionalProperties": { - "type": "string" + "permissions_boundary": { + "type": "string" + }, + "role_last_used": { + "$ref": "#/definitions/model.RoleLastUsed" + }, + "status": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Tag" + } + }, + "updated_at": { + "type": "string" + } + } + }, + "model.FilterRoleMasterMappingRequest": { + "type": "object", + "properties": { + "authMethod": { + "type": "string" + }, + "cspRoleId": { + "type": "string" + }, + "cspRoleName": { + "type": "string" + }, + "cspType": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectName": { + "type": "string" + }, + "roleId": { + "type": "string" + }, + "roleTypes": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" } + }, + "userId": { + "type": "string" + }, + "username": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "workspaceName": { + "type": "string" } } }, - "mcmpapi.McmpApiServiceAction": { + "model.MciamPermission": { "type": "object", "properties": { + "action": { + "description": "e.g., create, read, update, delete", + "type": "string" + }, + "createdAt": { + "description": "Match DB schema", + "type": "string" + }, "description": { "type": "string" }, - "method": { + "frameworkId": { + "description": "FK to mcmp_resource_types.framework_id", "type": "string" }, - "resourcePath": { + "id": { + "description": "Format: \u003cframework_id\u003e:\u003cresource_type_id\u003e:\u003caction\u003e", + "type": "string" + }, + "name": { + "type": "string" + }, + "resourceTypeId": { + "description": "FK to mcmp_resource_types.id", + "type": "string" + }, + "updatedAt": { + "description": "Match DB schema", "type": "string" } } }, - "mcmpapi.McmpApiServiceDefinition": { + "model.McmpApiCallRequest": { "type": "object", + "required": [ + "actionName", + "serviceName" + ], "properties": { - "auth": { - "description": "Use renamed AuthInfo", + "actionName": { + "description": "Target action name (operationId)", + "type": "string" + }, + "requestParams": { + "description": "Parameters for the external API call", "allOf": [ { - "$ref": "#/definitions/mcmpapi.McmpApiAuthInfo" + "$ref": "#/definitions/model.McmpApiRequestParams" } - ] - }, - "baseURL": { - "type": "string" + ] }, - "version": { + "serviceName": { + "description": "Target service name", "type": "string" } } }, - "mcmpapi.ServiceApiCallRequest": { - "type": "object" + "model.McmpApiRequestParams": { + "type": "object", + "properties": { + "body": { + "description": "Request body (accept any JSON structure) - Changed from json.RawMessage for swag compatibility" + }, + "pathParams": { + "description": "Parameters to replace in the resource path (e.g., {userId})", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "queryParams": { + "description": "Parameters to append as query string (?key=value)", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } }, "model.Menu": { "type": "object", "properties": { - "display_name": { + "displayName": { "type": "string" }, "id": { "type": "string" }, - "is_action": { + "isAction": { "type": "boolean" }, - "menu_number": { + "menuNumber": { "type": "integer" }, - "parent_id": { + "parentId": { "type": "string" }, "priority": { "type": "integer" }, - "res_type": { + "resType": { "type": "string" } } @@ -2993,78 +7722,126 @@ "$ref": "#/definitions/model.MenuTreeNode" } }, - "display_name": { + "displayName": { "type": "string" }, "id": { "type": "string" }, - "is_action": { + "isAction": { "type": "boolean" }, - "menu_number": { + "menuNumber": { "type": "integer" }, - "parent_id": { + "parentId": { "type": "string" }, "priority": { "type": "integer" }, - "res_type": { + "resType": { "type": "string" } } }, - "model.Permission": { + "model.Project": { "type": "object", "properties": { "created_at": { "type": "string" }, "description": { - "description": "Increased size to match roles", "type": "string" }, "id": { - "description": "Changed to string", - "type": "string" + "type": "integer" }, "name": { - "description": "Assuming Name column exists or needs to be added", + "type": "string" + }, + "nsid": { + "description": "Namespace ID", "type": "string" }, "updated_at": { "type": "string" + }, + "workspaces": { + "description": "M:N relationship", + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } } } }, - "model.PlatformRole": { + "model.ResourceType": { "type": "object", "properties": { - "created_at": { + "createdAt": { "type": "string" }, "description": { "type": "string" }, + "frameworkId": { + "description": "Identifier of the framework (e.g., \"mc-iam-manager\", \"mc-infra-manager\")", + "type": "string" + }, "id": { - "type": "integer" + "description": "Unique identifier within the framework (e.g., \"workspace\", \"vm\")", + "type": "string" }, "name": { + "description": "Display name (e.g., \"Workspace\", \"Virtual Machine\")", "type": "string" }, - "updated_at": { + "updatedAt": { "type": "string" } } }, - "model.Project": { + "model.Response": { + "type": "object", + "properties": { + "error": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + }, + "model.RoleLastUsed": { + "type": "object", + "properties": { + "last_used_date": { + "type": "string" + }, + "region": { + "type": "string" + } + } + }, + "model.RoleMaster": { "type": "object", "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + }, "created_at": { "type": "string" }, + "csp_role_mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterCspRoleMapping" + } + }, "description": { "type": "string" }, @@ -3074,19 +7851,141 @@ "name": { "type": "string" }, - "nsid": { - "description": "Namespace ID", - "type": "string" + "parent": { + "$ref": "#/definitions/model.RoleMaster" + }, + "parent_id": { + "type": "integer" + }, + "predefined": { + "type": "boolean" + }, + "role_subs": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleSub" + } }, "updated_at": { "type": "string" + } + } + }, + "model.RoleMasterCspRoleMapping": { + "type": "object", + "properties": { + "auth_method": { + "$ref": "#/definitions/constants.AuthMethod" + }, + "createdAt": { + "type": "string" }, - "workspaces": { - "description": "M:N relationship", + "cspRoles": { + "description": "서비스 레이어에서 조합", "type": "array", "items": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.CspRole" + } + }, + "description": { + "type": "string" + }, + "roleId": { + "type": "integer" + } + } + }, + "model.RoleMasterCspRoleMappingRequest": { + "type": "object", + "properties": { + "authMethod": { + "$ref": "#/definitions/constants.AuthMethod" + }, + "cspRoleId": { + "type": "string" + }, + "cspType": { + "$ref": "#/definitions/constants.CSPType" + }, + "description": { + "type": "string" + }, + "roleId": { + "type": "string" + } + } + }, + "model.RoleMasterMapping": { + "type": "object", + "properties": { + "role_id": { + "type": "integer" + }, + "role_master_csp_role_mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterCspRoleMapping" + } + }, + "role_name": { + "type": "string" + }, + "user_platform_roles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserPlatformRole" } + }, + "user_workspace_roles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + } + }, + "model.RoleSub": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "role_id": { + "type": "integer" + }, + "role_type": { + "$ref": "#/definitions/constants.IAMRoleType" + }, + "updated_at": { + "type": "string" + } + } + }, + "model.SetupInitialAdminRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "model.Tag": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" } } }, @@ -3124,10 +8023,10 @@ "type": "string" }, "platform_roles": { - "description": "관계 정의 (Foreign Key는 DB ID인 'ID' 필드를 참조해야 함)", + "description": "관계 정의", "type": "array", "items": { - "$ref": "#/definitions/model.PlatformRole" + "$ref": "#/definitions/model.RoleMaster" } }, "updated_at": { @@ -3138,116 +8037,132 @@ "type": "string" }, "workspace_roles": { - "description": "Changed foreignKey to ID", "type": "array", "items": { - "$ref": "#/definitions/model.WorkspaceRole" + "$ref": "#/definitions/model.RoleMaster" } } } }, - "model.Workspace": { + "model.UserPlatformRole": { "type": "object", "properties": { "created_at": { "type": "string" }, - "description": { - "type": "string" - }, - "id": { + "role_id": { "type": "integer" }, - "name": { - "type": "string" - }, - "projects": { - "description": "M:N relationship", - "type": "array", - "items": { - "$ref": "#/definitions/model.Project" - } + "user_id": { + "type": "integer" }, - "updated_at": { + "username": { + "description": "사용자 정보 (JOIN으로 가져올 필드들)", "type": "string" } } }, - "model.WorkspaceRole": { + "model.UserStatusRequest": { "type": "object", "properties": { - "created_at": { - "type": "string" - }, - "description": { - "type": "string" - }, "id": { - "type": "integer" + "description": "DB에 저장되는 정보 (mcmp_users 테이블)", + "type": "string" }, - "name": { - "description": "이름은 고유해야 함", + "kc_id": { + "description": "Keycloak User ID", "type": "string" }, - "updated_at": { + "status": { + "description": "사용자 상태", "type": "string" } } }, - "service.HealthStatus": { + "model.UserWorkspaceRole": { "type": "object", "properties": { - "db_connection": { + "created_at": { "type": "string" }, - "keycloak_admin_login": { - "type": "string" + "role": { + "$ref": "#/definitions/model.RoleMaster" }, - "keycloak_client_check": { - "type": "string" + "role_id": { + "type": "integer" }, - "keycloak_realm_check": { + "role_name": { "type": "string" }, - "mcmp_actions_count": { - "type": "integer" + "user": { + "$ref": "#/definitions/model.User" }, - "mcmp_services_count": { + "user_id": { "type": "integer" }, - "menus_count": { - "type": "integer" + "username": { + "type": "string" }, - "platform_roles_count": { - "type": "integer" + "workspace": { + "$ref": "#/definitions/model.Workspace" }, - "workspace_roles_count": { + "workspace_id": { "type": "integer" + }, + "workspace_name": { + "type": "string" } } }, - "service.UserWithRoles": { + "model.Workspace": { "type": "object", "properties": { - "roles": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "projects": { "type": "array", "items": { - "$ref": "#/definitions/model.WorkspaceRole" + "$ref": "#/definitions/model.Project" } }, - "user": { - "$ref": "#/definitions/model.User" + "updated_at": { + "type": "string" } } }, - "service.WorkspaceRoleInfo": { + "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { - "role": { - "$ref": "#/definitions/model.WorkspaceRole" + "created_at": { + "type": "string" }, - "workspace": { - "$ref": "#/definitions/model.Workspace" + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } } } } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index fb1a3d4f..573635a8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,5 +1,41 @@ basePath: /api/v1 definitions: + constants.AuthMethod: + enum: + - OIDC + - SAML + type: string + x-enum-varnames: + - AuthMethodOIDC + - AuthMethodSAML + constants.CSPType: + enum: + - aws + - gcp + - azure + type: string + x-enum-varnames: + - CSPTypeAWS + - CSPTypeGCP + - CSPTypeAzure + constants.IAMRoleType: + enum: + - platform + - workspace + - csp + type: string + x-enum-comments: + RoleTypeCSP: CSP 역할 + RoleTypePlatform: 플랫폼 역할 + RoleTypeWorkspace: 워크스페이스 역할 + x-enum-descriptions: + - 플랫폼 역할 + - 워크스페이스 역할 + - CSP 역할 + x-enum-varnames: + - RoleTypePlatform + - RoleTypeWorkspace + - RoleTypeCSP idp.UserLogin: properties: id: @@ -7,15 +43,26 @@ definitions: password: type: string type: object - mcmpapi.ApiQueryParam: + mcmpapi.McmpApiAction: properties: - key: + actionName: type: string - value: + createdAt: + type: string + description: + type: string + id: + description: Auto-incrementing primary key + type: integer + method: + type: string + resourcePath: + type: string + serviceName: + description: Foreign key reference (indexed) + type: string + updatedAt: type: string - required: - - key - - value type: object mcmpapi.McmpApiAuthInfo: properties: @@ -26,22 +73,6 @@ definitions: username: type: string type: object - mcmpapi.McmpApiCallRequest: - properties: - actionName: - description: Target action name (operationId) - type: string - requestParams: - allOf: - - $ref: '#/definitions/mcmpapi.McmpApiRequestParams' - description: Parameters for the external API call - serviceName: - description: Target service name - type: string - required: - - actionName - - serviceName - type: object mcmpapi.McmpApiDefinitions: properties: serviceActions: @@ -57,21 +88,20 @@ definitions: description: Use renamed ServiceDefinition type: object type: object - mcmpapi.McmpApiRequestParams: + mcmpapi.McmpApiPermissionActionMapping: properties: - body: - description: Request body (accept any JSON structure) - Changed from json.RawMessage - for swag compatibility - pathParams: - additionalProperties: - type: string - description: Parameters to replace in the resource path (e.g., {userId}) - type: object - queryParams: - additionalProperties: - type: string - description: Parameters to append as query string (?key=value) - type: object + actionID: + type: integer + actionName: + type: string + createdAt: + type: string + id: + type: integer + permissionID: + type: string + updatedAt: + type: string type: object mcmpapi.McmpApiServiceAction: properties: @@ -93,138 +123,298 @@ definitions: version: type: string type: object - mcmpapi.ServiceApiCallRequest: - type: object - model.Menu: + model.AssignRoleRequest: properties: - display_name: + roleId: + description: 역할 ID (문자열로 받음) type: string - id: + roleName: + description: 역할명 type: string - is_action: - type: boolean - menu_number: - type: integer - parent_id: + roleType: + description: 역할 타입 (platform/workspace) type: string - priority: - type: integer - res_type: + userId: + description: 사용자 ID (문자열로 받음) + type: string + username: + description: 사용자명 + type: string + workspaceId: + description: 워크스페이스 ID (문자열로 받음) type: string type: object - model.MenuTreeNode: + model.AssignWorkspaceRoleRequest: properties: - children: - description: Slice of pointers to child nodes - items: - $ref: '#/definitions/model.MenuTreeNode' - type: array - display_name: + roleId: + description: 역할 ID (문자열로 받음) type: string - id: + roleName: + description: 역할명 type: string - is_action: - type: boolean - menu_number: - type: integer - parent_id: + userId: + description: 사용자 ID (문자열로 받음) type: string - priority: - type: integer - res_type: + username: + description: 사용자명 + type: string + workspaceId: + description: 워크스페이스 ID (문자열로 받음) type: string type: object - model.Permission: + model.CreateCspRoleRequest: properties: - created_at: + cspRoleName: + description: csp의 RoleName. 여러 role이 있기때문에 csp에 정의한 role로 구분하기 위해 사용 + type: string + cspType: type: string description: - description: Increased size to match roles + type: string + iamIdentifier: + type: string + iamRoleId: type: string id: - description: Changed to string type: string - name: - description: Assuming Name column exists or needs to be added + idpIdentifier: type: string - updated_at: + path: + type: string + status: type: string + tags: + items: + $ref: '#/definitions/model.Tag' + type: array type: object - model.PlatformRole: + model.CreateCspRolesRequest: properties: - created_at: + cspRoles: + items: + $ref: '#/definitions/model.CreateCspRoleRequest' + type: array + required: + - cspRoles + type: object + model.CreateMenuMappingRequest: + properties: + menuIds: + items: + type: string + type: array + roleId: type: string + required: + - menuIds + - roleId + type: object + model.CreateProjectRequest: + properties: description: type: string - id: - type: integer name: type: string - updated_at: + workspaceId: + description: optional workspace to assign project to type: string + required: + - name type: object - model.Project: + model.CreateRoleRequest: + properties: + cspRoles: + items: + $ref: '#/definitions/model.CreateCspRoleRequest' + type: array + description: + type: string + menuIds: + items: + type: string + type: array + name: + type: string + parentId: + type: integer + roleTypes: + description: RoleTypes []constants.IAMRoleType `json:"roleTypes" validate:"required,dive,oneof=platform + workspace csp"` + items: + $ref: '#/definitions/constants.IAMRoleType' + type: array + required: + - name + type: object + model.CspRole: properties: + create_date: + type: string created_at: type: string + csp_type: + type: string + deleted_at: + type: string description: type: string + iam_identifier: + type: string + iam_role_id: + type: string id: type: integer + idp_identifier: + type: string + max_session_duration: + type: integer name: type: string - nsid: - description: Namespace ID + path: + type: string + permissions: + items: + type: string + type: array + permissions_boundary: type: string + role_last_used: + $ref: '#/definitions/model.RoleLastUsed' + status: + type: string + tags: + items: + $ref: '#/definitions/model.Tag' + type: array updated_at: type: string - workspaces: - description: M:N relationship + type: object + model.FilterRoleMasterMappingRequest: + properties: + authMethod: + type: string + cspRoleId: + type: string + cspRoleName: + type: string + cspType: + type: string + projectId: + type: string + projectName: + type: string + roleId: + type: string + roleTypes: items: - $ref: '#/definitions/model.Workspace' + $ref: '#/definitions/constants.IAMRoleType' type: array + userId: + type: string + username: + type: string + workspaceId: + type: string + workspaceName: + type: string type: object - model.User: + model.MciamPermission: properties: - created_at: + action: + description: e.g., create, read, update, delete + type: string + createdAt: + description: Match DB schema type: string description: type: string - email: - description: Ignore Email for DB + frameworkId: + description: FK to mcmp_resource_types.framework_id type: string - enabled: - description: Enabled status managed by Keycloak - type: boolean - firstName: - description: Ignore FirstName for DB + id: + description: 'Format: ::' + type: string + name: + type: string + resourceTypeId: + description: FK to mcmp_resource_types.id + type: string + updatedAt: + description: Match DB schema + type: string + type: object + model.McmpApiCallRequest: + properties: + actionName: + description: Target action name (operationId) + type: string + requestParams: + allOf: + - $ref: '#/definitions/model.McmpApiRequestParams' + description: Parameters for the external API call + serviceName: + description: Target service name + type: string + required: + - actionName + - serviceName + type: object + model.McmpApiRequestParams: + properties: + body: + description: Request body (accept any JSON structure) - Changed from json.RawMessage + for swag compatibility + pathParams: + additionalProperties: + type: string + description: Parameters to replace in the resource path (e.g., {userId}) + type: object + queryParams: + additionalProperties: + type: string + description: Parameters to append as query string (?key=value) + type: object + type: object + model.Menu: + properties: + displayName: type: string id: - description: DB에 저장되는 정보 (mcmp_users 테이블) + type: string + isAction: + type: boolean + menuNumber: type: integer - kc_id: - description: Keycloak User ID + parentId: type: string - lastName: - description: Ignore LastName for DB + priority: + type: integer + resType: type: string - platform_roles: - description: 관계 정의 (Foreign Key는 DB ID인 'ID' 필드를 참조해야 함) + type: object + model.MenuTreeNode: + properties: + children: + description: Slice of pointers to child nodes items: - $ref: '#/definitions/model.PlatformRole' + $ref: '#/definitions/model.MenuTreeNode' type: array - updated_at: + displayName: type: string - username: - description: Keycloak 정보 + id: + type: string + isAction: + type: boolean + menuNumber: + type: integer + parentId: + type: string + priority: + type: integer + resType: type: string - workspace_roles: - description: Changed foreignKey to ID - items: - $ref: '#/definitions/model.WorkspaceRole' - type: array type: object - model.Workspace: + model.Project: properties: created_at: type: string @@ -234,126 +424,2882 @@ definitions: type: integer name: type: string - projects: + nsid: + description: Namespace ID + type: string + updated_at: + type: string + workspaces: description: M:N relationship items: - $ref: '#/definitions/model.Project' + $ref: '#/definitions/model.Workspace' type: array - updated_at: - type: string type: object - model.WorkspaceRole: + model.ResourceType: properties: - created_at: + createdAt: type: string description: type: string + frameworkId: + description: Identifier of the framework (e.g., "mc-iam-manager", "mc-infra-manager") + type: string id: - type: integer + description: Unique identifier within the framework (e.g., "workspace", "vm") + type: string name: - description: 이름은 고유해야 함 + description: Display name (e.g., "Workspace", "Virtual Machine") type: string - updated_at: + updatedAt: type: string type: object - service.HealthStatus: + model.Response: properties: - db_connection: - type: string - keycloak_admin_login: + error: + type: boolean + message: type: string - keycloak_client_check: + type: object + model.RoleLastUsed: + properties: + last_used_date: type: string - keycloak_realm_check: + region: type: string - mcmp_actions_count: - type: integer - mcmp_services_count: - type: integer - menus_count: - type: integer - platform_roles_count: - type: integer - workspace_roles_count: - type: integer type: object - service.UserWithRoles: + model.RoleMaster: properties: - roles: + children: items: - $ref: '#/definitions/model.WorkspaceRole' + $ref: '#/definitions/model.RoleMaster' type: array - user: - $ref: '#/definitions/model.User' + created_at: + type: string + csp_role_mappings: + items: + $ref: '#/definitions/model.RoleMasterCspRoleMapping' + type: array + description: + type: string + id: + type: integer + name: + type: string + parent: + $ref: '#/definitions/model.RoleMaster' + parent_id: + type: integer + predefined: + type: boolean + role_subs: + items: + $ref: '#/definitions/model.RoleSub' + type: array + updated_at: + type: string + type: object + model.RoleMasterCspRoleMapping: + properties: + auth_method: + $ref: '#/definitions/constants.AuthMethod' + createdAt: + type: string + cspRoles: + description: 서비스 레이어에서 조합 + items: + $ref: '#/definitions/model.CspRole' + type: array + description: + type: string + roleId: + type: integer + type: object + model.RoleMasterCspRoleMappingRequest: + properties: + authMethod: + $ref: '#/definitions/constants.AuthMethod' + cspRoleId: + type: string + cspType: + $ref: '#/definitions/constants.CSPType' + description: + type: string + roleId: + type: string + type: object + model.RoleMasterMapping: + properties: + role_id: + type: integer + role_master_csp_role_mappings: + items: + $ref: '#/definitions/model.RoleMasterCspRoleMapping' + type: array + role_name: + type: string + user_platform_roles: + items: + $ref: '#/definitions/model.UserPlatformRole' + type: array + user_workspace_roles: + items: + $ref: '#/definitions/model.UserWorkspaceRole' + type: array + type: object + model.RoleSub: + properties: + created_at: + type: string + id: + type: integer + role_id: + type: integer + role_type: + $ref: '#/definitions/constants.IAMRoleType' + updated_at: + type: string + type: object + model.SetupInitialAdminRequest: + properties: + email: + type: string + password: + type: string + username: + type: string + type: object + model.Tag: + properties: + key: + type: string + value: + type: string + type: object + model.User: + properties: + created_at: + type: string + description: + type: string + email: + description: Ignore Email for DB + type: string + enabled: + description: Enabled status managed by Keycloak + type: boolean + firstName: + description: Ignore FirstName for DB + type: string + id: + description: DB에 저장되는 정보 (mcmp_users 테이블) + type: integer + kc_id: + description: Keycloak User ID + type: string + lastName: + description: Ignore LastName for DB + type: string + platform_roles: + description: 관계 정의 + items: + $ref: '#/definitions/model.RoleMaster' + type: array + updated_at: + type: string + username: + description: Keycloak 정보 + type: string + workspace_roles: + items: + $ref: '#/definitions/model.RoleMaster' + type: array + type: object + model.UserPlatformRole: + properties: + created_at: + type: string + role_id: + type: integer + user_id: + type: integer + username: + description: 사용자 정보 (JOIN으로 가져올 필드들) + type: string + type: object + model.UserStatusRequest: + properties: + id: + description: DB에 저장되는 정보 (mcmp_users 테이블) + type: string + kc_id: + description: Keycloak User ID + type: string + status: + description: 사용자 상태 + type: string type: object - service.WorkspaceRoleInfo: + model.UserWorkspaceRole: properties: + created_at: + type: string role: - $ref: '#/definitions/model.WorkspaceRole' + $ref: '#/definitions/model.RoleMaster' + role_id: + type: integer + role_name: + type: string + user: + $ref: '#/definitions/model.User' + user_id: + type: integer + username: + type: string workspace: $ref: '#/definitions/model.Workspace' + workspace_id: + type: integer + workspace_name: + type: string + type: object + model.Workspace: + properties: + created_at: + type: string + description: + type: string + id: + type: integer + name: + type: string + projects: + items: + $ref: '#/definitions/model.Project' + type: array + updated_at: + type: string + type: object + model.WorkspaceWithUsersAndRoles: + properties: + created_at: + type: string + description: + type: string + id: + type: integer + name: + type: string + updated_at: + type: string + users: + items: + $ref: '#/definitions/model.UserWorkspaceRole' + type: array type: object -host: localhost:3000 +host: localhost info: contact: {} description: MC IAM Manager API Documentation title: MC IAM Manager API version: "1.0" paths: - /api/call: + /api/auth/certs: + get: + consumes: + - application/json + description: Retrieve authentication certificates for MC-IAM-Manager to be used + in target frameworks for token validation. + operationId: mciamAuthCerts + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + summary: Get authentication certificates + tags: + - auth + /api/auth/login: + post: + consumes: + - application/json + description: Authenticate user and issue JWT token. + operationId: mciamLogin + parameters: + - description: Login Credentials + in: body + name: credentials + required: true + schema: + $ref: '#/definitions/idp.UserLogin' + produces: + - application/json + responses: {} + summary: User login + tags: + - auth + /api/auth/logout: + post: + consumes: + - application/json + description: Invalidate the user's refresh token and log out. + operationId: mciamLogout + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + summary: Logout user + tags: + - auth + /api/auth/refresh: + post: + consumes: + - application/json + description: Refresh JWT access token using a valid refresh token. + operationId: mciamRefreshToken + parameters: + - description: Refresh token + in: body + name: refresh_token + required: true + schema: + type: string + produces: + - application/json + responses: + "200": + description: New token information + schema: + additionalProperties: true + type: object + "400": + description: 'error: Bad Request' + schema: + additionalProperties: + type: string + type: object + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + summary: Refresh access token + tags: + - auth + /api/auth/temp-credential-csps: + get: + consumes: + - application/json + description: Get temporary credential provider information for AWS and GCP + operationId: mciamGetTempCredentialProviders + produces: + - application/json + responses: + "200": + description: CSP temporary credential information + schema: + additionalProperties: true + type: object + summary: Get temporary credential CSP information + tags: + - auth + /api/auth/validate: + post: + consumes: + - application/json + description: Validate the current access token and refresh if expired + operationId: mciamValidateToken + parameters: + - description: Refresh token + in: body + name: refresh_token + required: true + schema: + type: string + produces: + - application/json + responses: + "200": + description: Token validation result with new token if refreshed + schema: + additionalProperties: true + type: object + "400": + description: 'error: Bad Request' + schema: + additionalProperties: + type: string + type: object + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Validate access token + tags: + - auth + /api/csp-credentials: + get: + consumes: + - application/json + description: 모든 CSP 인증 정보 목록을 조회합니다 + operationId: mciamListCredentials + produces: + - application/json + responses: {} + security: + - BearerAuth: [] + summary: CSP 인증 정보 목록 조회 + tags: + - csp-credentials + post: + consumes: + - application/json + description: 새로운 CSP 인증 정보를 생성합니다 + operationId: mciamCreateCredential + produces: + - application/json + responses: {} + security: + - BearerAuth: [] + summary: 새 CSP 인증 정보 생성 + tags: + - csp-credentials + /api/csp-credentials/{id}: + delete: + consumes: + - application/json + description: CSP 인증 정보를 삭제합니다 + operationId: mciamDeleteCredential + parameters: + - description: Credential ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Credential not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: CSP 인증 정보 삭제 + tags: + - csp-credentials + get: + consumes: + - application/json + description: 특정 CSP 인증 정보를 ID로 조회합니다 + operationId: mciamGetCredentialByID + parameters: + - description: Credential ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "404": + description: 'error: Credential not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: CSP 인증 정보 ID로 조회 + tags: + - csp-credentials + put: + consumes: + - application/json + description: CSP 인증 정보를 업데이트합니다 + operationId: mciamUpdateCredential + parameters: + - description: Credential ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "404": + description: 'error: Credential not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: CSP 인증 정보 업데이트 + tags: + - csp-credentials + /api/initial-admin: + post: + consumes: + - application/json + description: Creates the initial platform admin user with necessary permissions. + platform admin 생성인데 + operationId: setupInitialAdmin + parameters: + - description: Setup Initial Admin Request + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.SetupInitialAdminRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.Response' + summary: Setup initial platform admin + tags: + - admin + /api/mcmp-api-permission-action-mappings: + post: + consumes: + - application/json + description: Creates a new mapping between a permission and an API action + operationId: createMcmpApiPermissionActionMapping + parameters: + - description: Mapping to create + in: body + name: mapping + required: true + schema: + $ref: '#/definitions/mcmpapi.McmpApiPermissionActionMapping' + produces: + - application/json + responses: + "204": + description: No Content + summary: Create permission-action mapping + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-api-permission-action-mappings/actions/{actionId}/permissions: + get: + consumes: + - application/json + description: Returns all permissions mapped to a specific API action + operationId: listPermissionsByActionID + parameters: + - description: Action ID + in: path + name: actionId + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + summary: Get permissions by action ID + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-api-permission-action-mappings/actions/list: + post: + consumes: + - application/json + description: Returns all workspace actions mapped to a specific permission + operationId: listWorkspaceActionsByPermissionID + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/mcmpapi.McmpApiAction' + type: array + summary: Get workspace actions by permission ID + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-api-permission-action-mappings/list: + post: + consumes: + - application/json + description: Returns all platform actions mapped to a specific permission + operationId: listPlatformActions + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/mcmpapi.McmpApiAction' + type: array + summary: List platform actions by permission ID + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-api-permission-action-mappings/permissions/{permissionId}/actions/{actionId}: + delete: + consumes: + - application/json + description: Deletes a mapping between a permission and an API action + operationId: deleteMapping + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + - description: Action ID + in: path + name: actionId + required: true + type: integer + produces: + - application/json + responses: + "204": + description: No Content + summary: Delete permission-action mapping + tags: + - mcmp-api-permission-action-mappings + put: + consumes: + - application/json + description: Updates an existing mapping between a permission and an API action + operationId: updateMapping + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + - description: Action ID + in: path + name: actionId + required: true + type: integer + - description: Updated mapping + in: body + name: mapping + required: true + schema: + $ref: '#/definitions/mcmpapi.McmpApiPermissionActionMapping' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + summary: Update permission-action mapping + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions: + get: + consumes: + - application/json + description: Returns all platform actions mapped to a specific permission + operationId: getPlatformActionsByPermissionID + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/mcmpapi.McmpApiAction' + type: array + summary: Get platform actions by permission ID + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-apis/list: + post: + consumes: + - application/json + description: Retrieves all MCMP API service and action definitions currently + stored in the database. + operationId: listServicesAndActions + parameters: + - description: Filter by service name + in: query + name: serviceName + type: string + - description: Filter by action name (operationId) + in: query + name: actionName + type: string + produces: + - application/json + responses: + "200": + description: Successfully retrieved API definitions + schema: + $ref: '#/definitions/mcmpapi.McmpApiDefinitions' + "500": + description: 'message: Failed to retrieve API definitions' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get All Stored MCMP API Definitions + tags: + - McmpAPI + /api/mcmp-apis/mcmpApiCall: + post: + consumes: + - application/json + description: Executes a defined MCMP API action with parameters structured in + McmpApiCallRequest. + operationId: mcmpApiCall + parameters: + - description: API Call Request + in: body + name: callRequest + required: true + schema: + $ref: '#/definitions/model.McmpApiCallRequest' + produces: + - application/json + responses: + "200": + description: External API Response (structure depends on the called API) + schema: + type: object + "400": + description: 'error: Invalid request body or parameters' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Service or action not found' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Internal server error or failed to call external API' + schema: + additionalProperties: + type: string + type: object + "503": + description: 'error: External API unavailable' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Call an external MCMP API action (Structured Request) + tags: + - McmpAPI + /api/mcmp-apis/name/{serviceName}: + put: + consumes: + - application/json + description: Updates specific fields (e.g., BaseURL, Auth info) of an MCMP API + service definition identified by its name. Cannot update name or version. + operationId: UpdateFrameworkService + parameters: + - description: Service Name to update + in: path + name: serviceName + required: true + type: string + - description: Fields to update (e.g., {\ + in: body + name: updates + required: true + schema: + type: object + produces: + - application/json + responses: + "200": + description: 'message: Service updated successfully" // Or return updated + service?' + schema: + additionalProperties: + type: string + type: object + "400": + description: 'error: Invalid service name or request body' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Service not found' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Failed to update service' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Update MCMP API Service Definition + tags: + - McmpAPI + /api/mcmp-apis/name/{serviceName}/versions/{version}/activate: + put: + consumes: + - application/json + description: Sets the specified version of an MCMP API service as the active + one. + operationId: setActiveVersion + parameters: + - description: Service Name + in: path + name: serviceName + required: true + type: string + - description: Version to activate + in: path + name: version + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: 'error: Invalid service name or version' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Service or version not found' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Failed to set active version' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Set Active Version for a Service + tags: + - McmpAPI + /api/mcmp-apis/syncMcmpAPIs: + post: + consumes: + - application/json + description: Triggers the synchronization of MCMP API definitions from the configured + YAML URL to the database. + operationId: syncMcmpAPIs + produces: + - application/json + responses: + "200": + description: 'message: Successfully triggered MCMP API sync' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'message: Failed to trigger MCMP API sync' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Sync MCMP API Definitions + tags: + - McmpAPI + /api/mcmp-apis/test/mc-infra-manager/getallns: + get: + description: Calls the GetAllNs action of the mc-infra-manager service via the + CallApi service. + operationId: testCallGetAllNs + produces: + - application/json + responses: + "200": + description: Response from mc-infra-manager GetAllNs + schema: + type: object + "400": + description: 'error: Bad Request (e.g., invalid parameters)' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Service or Action Not Found' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Internal Server Error' + schema: + additionalProperties: + type: string + type: object + "503": + description: 'error: External API Service Unavailable' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Test Call to mc-infra-manager GetAllNs + tags: + - McmpAPI + - Test + /api/menus: + post: + consumes: + - application/json + description: Create a new menu + operationId: createMenu + parameters: + - description: Menu Info + in: body + name: menu + required: true + schema: + $ref: '#/definitions/model.Menu' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.Menu' + security: + - BearerAuth: [] + summary: Create new menu + tags: + - menus + /api/menus/id/{menuId}: + delete: + consumes: + - application/json + description: Delete a menu + operationId: deleteMenu + parameters: + - description: Menu ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + security: + - BearerAuth: [] + summary: Delete menu + tags: + - menus + post: + consumes: + - application/json + description: Get menu details by ID + operationId: getMenuByID + parameters: + - description: Menu ID + in: path + name: menuId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.Menu' + security: + - BearerAuth: [] + summary: Get menu by ID + tags: + - menus + put: + consumes: + - application/json + description: Update menu information + operationId: updateMenu + parameters: + - description: Menu ID + in: path + name: id + required: true + type: string + - description: Menu Info + in: body + name: menu + required: true + schema: + $ref: '#/definitions/model.Menu' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.Menu' + security: + - BearerAuth: [] + summary: Update menu information + tags: + - menus + /api/menus/list: + post: + consumes: + - application/json + description: List all menus as a tree structure. Admin permission required. + operationId: listMenus + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.MenuTreeNode' + type: array + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: 서버 내부 오류' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List all menus + tags: + - menus + /api/menus/platform-roles: + delete: + consumes: + - application/json + description: Delete the mapping between a platform role and a menu. + operationId: deleteMenusRolesMapping + parameters: + - description: Platform Role ID + in: query + name: roleId + type: string + - description: Menu ID + in: query + name: menuId + type: string + produces: + - application/json + responses: + "200": + description: 'message: Menu mapping deleted successfully' + schema: + additionalProperties: + type: string + type: object + "400": + description: 'error: platform role and menu ID are required' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: 서버 내부 오류' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Delete platform role-menu mapping + tags: + - menus + post: + consumes: + - application/json + description: Create a new menu mapping + operationId: createMenusRolesMapping + parameters: + - description: Menu Mapping + in: body + name: mapping + required: true + schema: + $ref: '#/definitions/model.CreateMenuMappingRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create menu mapping + tags: + - menu + /api/menus/platform-roles/list: + post: + consumes: + - application/json + description: List menus mapped to a specific platform role. + operationId: listMappedMenusByRole + parameters: + - description: Platform Role ID + in: query + name: roleId + type: string + - description: Menu ID + in: query + name: menuId + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Menu' + type: array + "400": + description: 'error: platform role is required' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: 서버 내부 오류' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List menus mapped to platform role + tags: + - menus + /api/menus/setup/initial-menus: + post: + consumes: + - application/json + description: Register or update menus from a local YAML file specified by the + filePath query parameter, or from the MCWEBCONSOLE_MENUYAML URL in .env if + not provided. If loaded from URL, the file is saved to asset/menu/menu.yaml. + operationId: registerMenusFromYAML + parameters: + - description: YAML file path (optional, uses .env URL or default local path + if not provided) + in: query + name: filePath + type: string + produces: + - application/json + responses: + "200": + description: 'message: Successfully registered menus from YAML' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: 실패 메시지' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Register/Update menus from YAML file or URL + tags: + - menus + /api/menus/setup/initial-menus2: + post: + consumes: + - text/plain + description: 'Parse YAML text in the request body and register or update menus + in the database. Recommended Content-Type: text/plain, text/yaml, application/yaml.' + operationId: registerMenusFromBody + parameters: + - description: Menu definitions in YAML format (must contain 'menus:' root key) + example: '"menus:\n - id: new-item\n parentid: dashboard\n displayname: + New Menu Item\n restype: menu\n isaction: false\n priority: 10\n menunumber: + 9999"' + in: body + name: yaml + required: true + schema: + type: string + produces: + - application/json + responses: + "200": + description: 'message: Successfully registered menus from request body' + schema: + additionalProperties: + type: string + type: object + "400": + description: 'error: 잘못된 요청 본문 또는 YAML 형식 오류' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: 서버 내부 오류' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Register/Update menus from YAML in request body + tags: + - menus + /api/menus/tree/list: + post: + consumes: + - application/json + description: List all menus as a tree structure. Admin permission required. + operationId: listMenusTree + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.MenuTreeNode' + type: array + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: 서버 내부 오류' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List all menus Tree + tags: + - menus + /api/menus/user-menu-tree: + get: + consumes: + - application/json + description: Get menu tree based on user's platform roles + operationId: getUserMenuTree + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.MenuTreeNode' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get user menu tree by platform roles + tags: + - menus + /api/permissions/mciam: + post: + consumes: + - application/json + description: Create a new permission with the specified information. + operationId: createMciamPermission + parameters: + - description: Permission Info + in: body + name: permission + required: true + schema: + $ref: '#/definitions/model.MciamPermission' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.MciamPermission' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create new permission + tags: + - permissions + /api/permissions/mciam/{id}: + delete: + consumes: + - application/json + description: Delete a permission by its ID. + operationId: deleteMciamPermission + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Delete permission + tags: + - permissions + put: + consumes: + - application/json + description: Update the details of an existing permission. + operationId: updateMciamPermission + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + - description: Permission Info + in: body + name: permission + required: true + schema: + $ref: '#/definitions/model.MciamPermission' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.MciamPermission' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Update permission + tags: + - permissions + /api/permissions/mciam/id/{id}: + get: + consumes: + - application/json + description: Retrieve permission details by permission ID. + operationId: getMciamPermissionByID + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.MciamPermission' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get permission by ID + tags: + - permissions + /api/permissions/mciam/list: + post: + consumes: + - application/json + description: Retrieve a list of all permissions. + operationId: listMciamPermissions + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.MciamPermission' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List all permissions + tags: + - permissions + /api/projects: + post: + consumes: + - application/json + description: Create a new project with the specified information. Optionally + specify a workspace to assign the project to. + operationId: createProject + parameters: + - description: Project Info + in: body + name: project + required: true + schema: + $ref: '#/definitions/model.CreateProjectRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.Project' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create new project + tags: + - projects + /api/projects/{id}: + delete: + consumes: + - application/json + description: Delete a project by its ID. + operationId: deleteProject + parameters: + - description: Project ID + in: path + name: projectId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Delete project + tags: + - projects + get: + consumes: + - application/json + description: Retrieve project details by project ID. + operationId: getProjectByID + parameters: + - description: Project ID + in: path + name: projectId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.Project' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get project by ID + tags: + - projects + put: + consumes: + - application/json + description: Update the details of an existing project. + operationId: updateProject + parameters: + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Project Info + in: body + name: project + required: true + schema: + $ref: '#/definitions/model.Project' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.Project' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Update project + tags: + - projects + /api/projects/{id}/workspaces/{workspaceId}: + post: + consumes: + - application/json + description: 프로젝트에 워크스페이스를 연결합니다. + operationId: addWorkspaceToProject + parameters: + - description: 프로젝트 ID + in: path + name: id + required: true + type: integer + - description: 워크스페이스 ID + in: path + name: workspaceId + required: true + type: integer + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: 'error: 잘못된 ID 형식' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: 서버 내부 오류' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: 프로젝트에 워크스페이스 연결 + tags: + - projects + /api/projects/id/{projectId}/workspaces: + get: + consumes: + - application/json + description: Retrieve list of workspaces that the project is assigned to + operationId: getProjectWorkspaces + parameters: + - description: Project ID + in: path + name: projectId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Workspace' + type: array + "400": + description: 'error: Invalid project ID' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Project not found' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Internal server error' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get workspaces assigned to project + tags: + - projects + /api/projects/list: + post: + consumes: + - application/json + description: Retrieve a list of all projects. + operationId: listProjects + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Project' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List all projects + tags: + - projects + /api/projects/name/{projectName}: + get: + consumes: + - application/json + description: Get project details by name + operationId: getProjectByName + parameters: + - description: Project Name + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.Project' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get project by name + tags: + - projects + /api/resource-types/cloud-resources: + post: + consumes: + - application/json + description: 새로운 리소스 타입을 생성합니다 + operationId: createResourceType + parameters: + - description: Resource Type Info + in: body + name: resourceType + required: true + schema: + $ref: '#/definitions/model.ResourceType' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.ResourceType' + "400": + description: 'error: Invalid request' + schema: + additionalProperties: + type: string + type: object + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Cloud에서 관리되는 Resource(vm, nlb, k8s 등의 그룹) 새 리소스 타입 생성 + tags: + - resource-types + /api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId: + delete: + consumes: + - application/json + description: 리소스 타입을 삭제합니다 + operationId: deleteResourceType + parameters: + - description: Resource Type ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Resource Type not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: 리소스 타입 삭제 + tags: + - resource-types + get: + consumes: + - application/json + description: 특정 리소스 타입을 ID로 조회합니다 + operationId: getCloudResourceTypeByID + parameters: + - description: Resource Type ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.ResourceType' + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Resource Type not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: 리소스 타입 ID로 조회 + tags: + - resource-types + put: + consumes: + - application/json + description: 리소스 타입 정보를 업데이트합니다 + operationId: updateResourceType + parameters: + - description: Resource Type ID + in: path + name: id + required: true + type: string + - description: Resource Type Info + in: body + name: resourceType + required: true + schema: + $ref: '#/definitions/model.ResourceType' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.ResourceType' + "400": + description: 'error: Invalid request' + schema: + additionalProperties: + type: string + type: object + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Resource Type not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: 리소스 타입 업데이트 + tags: + - resource-types + /api/resource-types/cloud-resources/list: + post: + consumes: + - application/json + description: 모든 리소스 타입 목록을 조회합니다 + operationId: listCloudResourceTypes + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.ResourceType' + type: array + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: 리소스 타입 목록 조회 + tags: + - resource-types + /api/roles: + post: + consumes: + - application/json + description: Create a new role + operationId: createRole + parameters: + - description: Role Info + in: body + name: role + required: true + schema: + $ref: '#/definitions/model.CreateRoleRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.RoleMaster' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create role + tags: + - roles + /api/roles/{roleType}/{roleId}/mciam-permissions: + get: + consumes: + - application/json + description: 특정 역할의 MC-IAM 권한 ID 목록을 조회합니다. + operationId: getRoleMciamPermissions + parameters: + - description: 역할 타입 ('platform' or 'workspace') + in: path + name: roleType + required: true + type: string + - description: 역할 ID + in: path + name: roleId + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 권한 ID 목록 + schema: + items: + type: string + type: array + summary: 역할의 MC-IAM 권한 목록 조회 - Renamed + tags: + - roles + - mciam-permissions + /api/roles/{roleType}/{roleId}/mciam-permissions/{permissionId}: + delete: + consumes: + - application/json + description: 역할에서 MC-IAM 권한을 제거합니다. + operationId: removeMciamPermissionFromRole + parameters: + - description: 역할 타입 ('platform' or 'workspace') + in: path + name: roleType + required: true + type: string + - description: 역할 ID + in: path + name: roleId + required: true + type: integer + - description: MC-IAM 권한 ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + summary: 역할에서 MC-IAM 권한 제거 - Renamed + tags: + - roles + - mciam-permissions + post: + consumes: + - application/json + description: 역할에 MC-IAM 권한을 할당합니다. + operationId: assignMciamPermissionToRole + parameters: + - description: 역할 타입 ('platform' or 'workspace') + in: path + name: roleType + required: true + type: string + - description: 역할 ID + in: path + name: roleId + required: true + type: integer + - description: MC-IAM 권한 ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + summary: 역할에 MC-IAM 권한 할당 - Renamed + tags: + - roles + - mciam-permissions + /api/roles/assign/platform-role: + post: + consumes: + - application/json + description: Assign a platform role to a user + operationId: assignPlatformRole + parameters: + - description: Platform Role Assignment Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AssignRoleRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Assign platform role + tags: + - roles + /api/roles/assign/workspace-role: + post: + consumes: + - application/json + description: Assign a workspace role to a user + operationId: assignWorkspaceRole + parameters: + - description: Workspace Role Assignment Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AssignWorkspaceRoleRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Assign workspace role + tags: + - roles + /api/roles/csp: + post: + consumes: + - application/json + description: Create a new csp role + operationId: createCspRole + parameters: + - description: CSP Role Creation Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.CreateRoleRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create csp role + tags: + - roles + /api/roles/csp-roles: + post: + consumes: + - application/json + description: Create a new mapping between role and CSP role + operationId: addCspRoleMappings + parameters: + - description: Mapping Info + in: body + name: mapping + required: true + schema: + $ref: '#/definitions/model.RoleMasterCspRoleMappingRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.RoleMasterCspRoleMappingRequest' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create role-CSP role mapping + tags: + - roles + /api/roles/csp-roles/batch: + post: + consumes: + - application/json + description: Create multiple new csp roles + operationId: createCspRoles + parameters: + - description: Multiple CSP Role Creation Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.CreateCspRolesRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + items: + $ref: '#/definitions/model.CspRole' + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create multiple csp roles + tags: + - roles + /api/roles/csp-roles/id/:roleId: + get: + consumes: + - application/json + description: Get a mapping between role and CSP role + operationId: getCspRoleMappingByRoleId + parameters: + - description: Mapping Info + in: body + name: mapping + required: true + schema: + $ref: '#/definitions/model.RoleMasterCspRoleMappingRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMasterCspRoleMappingRequest' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get role-CSP role mapping + tags: + - roles + /api/roles/csp-roles/id/{roleId}: + delete: + consumes: + - application/json + description: Delete a role + operationId: deleteCspRole + parameters: + - description: Role ID + in: path + name: roleId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Delete csp role + tags: + - roles + put: + consumes: + - application/json + description: Update role information + operationId: updateCspRole + parameters: + - description: Role ID + in: path + name: roleId + required: true + type: string + - description: Role Info + in: body + name: role + required: true + schema: + $ref: '#/definitions/model.CreateRoleRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMaster' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Update csp role + tags: + - roles + /api/roles/csp-roles/list: + post: + consumes: + - application/json + description: Get a mapping between role and CSP role + operationId: listCspRoleMappings + parameters: + - description: Mapping Info + in: body + name: mapping + required: true + schema: + $ref: '#/definitions/model.RoleMasterCspRoleMappingRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMasterCspRoleMappingRequest' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get role-CSP role mapping + tags: + - roles + /api/roles/csp/id/{roleId}: + get: + consumes: + - application/json + description: Get csp role details by ID + operationId: getCspRoleByID + parameters: + - description: CSP Role ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMaster' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get csp role by ID + tags: + - roles + /api/roles/csp/list: + post: + consumes: + - application/json + description: Get a list of all csp roles + operationId: listCSPRoles + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.RoleMaster' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List csp roles + tags: + - roles + /api/roles/csp/name/{roleName}: + get: + consumes: + - application/json + description: Get csp role details by Name + operationId: getCspRoleByName + parameters: + - description: CSP Role Name + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMaster' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get csp role by Name + tags: + - roles + /api/roles/id/{roleId}: + delete: + consumes: + - application/json + description: Delete a role by its name. + operationId: deleteRole + parameters: + - description: Role ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Delete role + tags: + - roles + get: + consumes: + - application/json + description: Get role details by ID + operationId: getRoleByRoleID + parameters: + - description: Role ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMaster' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get role by ID + tags: + - roles + put: + consumes: + - application/json + description: Update the details of an existing role. + operationId: updateRole + parameters: + - description: Role ID + in: path + name: id + required: true + type: string + - description: Role Info + in: body + name: role + required: true + schema: + $ref: '#/definitions/model.CreateRoleRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMaster' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Update role + tags: + - roles + /api/roles/id/{roleId}/assign: post: consumes: - application/json - description: Executes a defined MCMP API action with query parameters and a - request body. + description: Assign a role to a user + operationId: assignRole parameters: - - description: Generic API Call Request + - description: Role Assignment Info in: body - name: callRequest + name: request required: true schema: - $ref: '#/definitions/mcmpapi.ServiceApiCallRequest' + $ref: '#/definitions/model.AssignRoleRequest' produces: - application/json responses: "200": - description: External API Response (structure depends on the called API) + description: OK schema: + additionalProperties: + type: string type: object "400": - description: 'error: Invalid request body or parameters' + description: Bad Request schema: additionalProperties: type: string type: object - "404": - description: 'error: Service or action not found' + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object - "500": - description: 'error: Internal server error or failed to call external API' + security: + - BearerAuth: [] + summary: Assign role + tags: + - roles + /api/roles/id/{roleId}/unassign: + delete: + consumes: + - application/json + description: Remove a role from a user + operationId: removeRole + parameters: + - description: Role Removal Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AssignRoleRequest' + produces: + - application/json + responses: + "200": + description: OK schema: additionalProperties: type: string type: object - "503": - description: 'error: External API unavailable' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: Call an external MCMP API action (Generic Request) + summary: Remove role tags: - - McmpAPI - /api/permissions: - get: + - roles + /api/roles/list: + post: consumes: - application/json - description: 모든 권한 목록을 조회합니다. + description: Retrieve a list of all roles. + operationId: listRoles produces: - application/json responses: @@ -361,102 +3307,71 @@ paths: description: OK schema: items: - $ref: '#/definitions/model.Permission' + $ref: '#/definitions/model.RoleMaster' type: array - summary: 권한 목록 조회 + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List all roles tags: - - permissions + - roles + /api/roles/mappings/csp-roles/list: post: consumes: - application/json - description: 새로운 권한을 생성합니다. + description: List users by csp role + operationId: listUsersByCspRole parameters: - - description: 권한 정보 + - description: Filter Role Master Mapping Request in: body - name: permission + name: request required: true schema: - $ref: '#/definitions/model.Permission' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/model.Permission' - summary: 권한 생성 - tags: - - permissions - /api/permissions/{id}: - delete: - consumes: - - application/json - description: 권한을 삭제합니다. - parameters: - - description: 권한 ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "204": - description: No Content - summary: 권한 삭제 - tags: - - permissions - get: - consumes: - - application/json - description: ID로 특정 권한을 조회합니다. - parameters: - - description: 권한 ID - in: path - name: id - required: true - type: string + $ref: '#/definitions/model.FilterRoleMasterMappingRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/model.Permission' - summary: ID로 권한 조회 + items: + $ref: '#/definitions/model.RoleMasterMapping' + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List users by csp role tags: - - permissions - put: + - roles + /api/roles/mappings/list: + post: consumes: - application/json - description: 기존 권한을 수정합니다. + description: List role master mappings + operationId: listRoleMasterMappings parameters: - - description: 권한 ID - in: path - name: id - required: true - type: string - - description: 권한 정보 + - description: Filter Role Master Mapping Request in: body - name: permission + name: request required: true schema: - $ref: '#/definitions/model.Permission' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/model.Permission' - summary: 권한 수정 - tags: - - permissions - /api/platform-roles: - get: - consumes: - - application/json - description: 모든 플랫폼 역할을 조회합니다. + $ref: '#/definitions/model.FilterRoleMasterMappingRequest' produces: - application/json responses: @@ -464,113 +3379,146 @@ paths: description: OK schema: items: - $ref: '#/definitions/model.PlatformRole' + $ref: '#/definitions/model.RoleMasterMapping' type: array - summary: 플랫폼 역할 목록 조회 + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List role master mappings tags: - - platform-roles + - roles + /api/roles/mappings/platform-roles/users/list: post: consumes: - application/json - description: 새로운 플랫폼 역할을 생성합니다. + description: List users by platform role + operationId: listUsersByPlatformRole parameters: - - description: Platform Role + - description: Filter Role Master Mapping Request in: body - name: role + name: request required: true schema: - $ref: '#/definitions/model.PlatformRole' + $ref: '#/definitions/model.FilterRoleMasterMappingRequest' produces: - application/json responses: - "201": - description: Created + "200": + description: OK schema: - $ref: '#/definitions/model.PlatformRole' - summary: 플랫폼 역할 생성 - tags: - - platform-roles - /api/platform-roles/{id}: - delete: - consumes: - - application/json - description: 플랫폼 역할을 삭제합니다. - parameters: - - description: Platform Role ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "204": - description: No Content - summary: 플랫폼 역할 삭제 + items: + $ref: '#/definitions/model.RoleMasterMapping' + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List users by platform role tags: - - platform-roles + - roles + /api/roles/mappings/role/id/:roleId: get: consumes: - application/json - description: ID로 플랫폼 역할을 조회합니다. + description: Get role master mappings + operationId: getRoleMasterMappings parameters: - - description: Platform Role ID - in: path - name: id + - description: Filter Role Master Mapping Request + in: body + name: request required: true - type: integer + schema: + $ref: '#/definitions/model.FilterRoleMasterMappingRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/model.PlatformRole' - summary: 플랫폼 역할 조회 + $ref: '#/definitions/model.RoleMasterMapping' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get role master mappings tags: - - platform-roles - put: + - roles + /api/roles/mappings/workspace-roles/users/list: + post: consumes: - application/json - description: 기존 플랫폼 역할을 수정합니다. + description: List users by workspace role + operationId: listUsersByWorkspaceRole parameters: - - description: Platform Role ID - in: path - name: id - required: true - type: integer - - description: Platform Role + - description: Filter Role Master Mapping Request in: body - name: role + name: request required: true schema: - $ref: '#/definitions/model.PlatformRole' + $ref: '#/definitions/model.FilterRoleMasterMappingRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/model.PlatformRole' - summary: 플랫폼 역할 수정 + items: + $ref: '#/definitions/model.RoleMasterMapping' + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List users by workspace role tags: - - platform-roles - /api/roles/{roleType}/{roleId}/permissions: - get: + - roles + /api/roles/menu-roles/list: + post: consumes: - application/json - description: 특정 역할의 권한 목록을 조회합니다. - parameters: - - description: 역할 타입 ('platform' or 'workspace') - in: path - name: roleType - required: true - type: string - - description: 역할 ID - in: path - name: roleId - required: true - type: integer + description: Get a list of all menu roles + operationId: listPlatformRoles produces: - application/json responses: @@ -578,444 +3526,492 @@ paths: description: OK schema: items: - $ref: '#/definitions/model.Permission' + $ref: '#/definitions/model.RoleMaster' type: array - summary: 역할의 권한 목록 조회 - tags: - - permissions - /api/roles/{roleType}/{roleId}/permissions/{permissionId}: - delete: - consumes: - - application/json - description: 역할에서 권한을 제거합니다. - parameters: - - description: 역할 타입 ('platform' or 'workspace') - in: path - name: roleType - required: true - type: string - - description: 역할 ID - in: path - name: roleId - required: true - type: integer - - description: 권한 ID - in: path - name: permissionId - required: true - type: string - produces: - - application/json - responses: - "204": - description: No Content - summary: 역할에서 권한 제거 - tags: - - permissions - post: + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List menu roles + tags: + - roles + /api/roles/name/{roleName}: + get: consumes: - application/json - description: 역할에 권한을 할당합니다. + description: Retrieve role details by role name. + operationId: getRoleByRoleName parameters: - - description: 역할 타입 ('platform' or 'workspace') - in: path - name: roleType - required: true - type: string - - description: 역할 ID - in: path - name: roleId - required: true - type: integer - - description: 권한 ID + - description: Role name in: path - name: permissionId + name: name required: true type: string produces: - application/json responses: - "204": - description: No Content - summary: 역할에 권한 할당 + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMaster' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get role by Name tags: - - permissions - /auth/login: + - roles + /api/roles/platform-roles: post: consumes: - application/json - description: 사용자 ID와 비밀번호로 로그인하여 JWT 토큰을 발급받습니다. + description: Create a new menu role + operationId: createPlatformRole parameters: - - description: 로그인 정보 (Id, Password) + - description: Menu Role Creation Info in: body - name: login + name: request required: true schema: - $ref: '#/definitions/idp.UserLogin' + $ref: '#/definitions/model.CreateRoleRequest' produces: - application/json responses: "200": - description: 로그인 성공 및 토큰 정보 (gocloak.JWT 구조체와 유사) + description: OK schema: - additionalProperties: true + additionalProperties: + type: string type: object "400": - description: 'error: 잘못된 요청 형식' + description: Bad Request schema: additionalProperties: type: string type: object - "401": - description: 'error: 인증 실패 (자격 증명 오류)' + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object - "403": - description: 'error: 계정이 비활성화되었거나 승인 대기 중입니다' + security: + - BearerAuth: [] + summary: Create menu role + tags: + - roles + /api/roles/platform-roles/id/{roleId}: + delete: + consumes: + - application/json + description: Delete a platform role + operationId: deletePlatformRole + parameters: + - description: Platform Role ID + in: path + name: roleId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "404": + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류 (Keycloak 통신, DB 동기화 등)' + description: Internal Server Error schema: additionalProperties: type: string type: object - summary: 로그인 + security: + - BearerAuth: [] + summary: Delete platform role tags: - - auth - /mcmp-apis: + - roles get: consumes: - application/json - description: Retrieves all MCMP API service and action definitions currently - stored in the database. + description: Get platform role details by ID + operationId: getPlatformRoleByID parameters: - - description: Filter by service name - in: query - name: serviceName - type: string - - description: Filter by action name (operationId) - in: query - name: actionName + - description: Platform Role ID + in: path + name: id + required: true type: string produces: - application/json responses: "200": - description: Successfully retrieved API definitions + description: OK schema: - $ref: '#/definitions/mcmpapi.McmpApiDefinitions' + $ref: '#/definitions/model.RoleMaster' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object "500": - description: 'message: Failed to retrieve API definitions' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: Get All Stored MCMP API Definitions + summary: Get platform role by ID tags: - - McmpAPI - /mcmp-apis/{serviceName}: - put: + - roles + /api/roles/platform-roles/name/{roleName}: + get: consumes: - application/json - description: Updates specific fields (e.g., BaseURL, Auth info) of an MCMP API - service definition identified by its name. Cannot update name or version. + description: Get menu role details by Name + operationId: getPlatformRoleByName parameters: - - description: Service Name to update + - description: Menu Role Name in: path - name: serviceName + name: name required: true type: string - - description: Fields to update (e.g., {\ - in: body - name: updates - required: true - schema: - type: object produces: - application/json responses: "200": - description: 'message: Service updated successfully" // Or return updated - service?' - schema: - additionalProperties: - type: string - type: object - "400": - description: 'error: Invalid service name or request body' + description: OK schema: - additionalProperties: - type: string - type: object + $ref: '#/definitions/model.RoleMaster' "404": - description: 'error: Service not found' + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: Failed to update service' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: Update MCMP API Service Definition + summary: Get menu role by Name tags: - - McmpAPI - /mcmp-apis/{serviceName}/versions/{version}/activate: - put: + - roles + /api/roles/unassign/csp-roles: + delete: consumes: - application/json - description: Sets the specified version of an MCMP API service as the active - one. + description: Delete a mapping between workspace role and CSP role + operationId: removeCspRoleMappings parameters: - - description: Service Name - in: path - name: serviceName - required: true - type: string - - description: Version to activate - in: path - name: version + - description: Mapping Info + in: body + name: mapping required: true - type: string + schema: + $ref: '#/definitions/model.RoleMasterCspRoleMappingRequest' produces: - application/json responses: "204": description: No Content "400": - description: 'error: Invalid service name or version' - schema: - additionalProperties: - type: string - type: object - "404": - description: 'error: Service or version not found' + description: Bad Request schema: additionalProperties: type: string type: object "500": - description: 'error: Failed to set active version' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: Set Active Version for a Service + summary: Delete workspace role-CSP role mapping tags: - - McmpAPI - /mcmp-apis/call: - post: + - roles + /api/roles/unassign/platform-role: + delete: consumes: - application/json - description: Executes a defined MCMP API action with parameters structured in - McmpApiCallRequest. + description: Remove a platform role from a user + operationId: removePlatformRole parameters: - - description: API Call Request + - description: Platform Role Removal Info in: body - name: callRequest + name: request required: true schema: - $ref: '#/definitions/mcmpapi.McmpApiCallRequest' + $ref: '#/definitions/model.AssignRoleRequest' produces: - application/json responses: "200": - description: External API Response (structure depends on the called API) - schema: - type: object - "400": - description: 'error: Invalid request body or parameters' + description: OK schema: additionalProperties: type: string type: object - "404": - description: 'error: Service or action not found' + "400": + description: Bad Request schema: additionalProperties: type: string type: object "500": - description: 'error: Internal server error or failed to call external API' - schema: - additionalProperties: - type: string - type: object - "503": - description: 'error: External API unavailable' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: Call an external MCMP API action (Structured Request) + summary: Remove platform role tags: - - McmpAPI - /mcmp-apis/sync: - post: + - roles + /api/roles/unassign/workspace-role: + delete: consumes: - application/json - description: Triggers the synchronization of MCMP API definitions from the configured - YAML URL to the database. + description: Remove a workspace role from a user + operationId: removeWorkspaceRole + parameters: + - description: Workspace Role Removal Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AssignRoleRequest' produces: - application/json responses: "200": - description: 'message: Successfully triggered MCMP API sync" // Updated - message' + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request schema: additionalProperties: type: string type: object "500": - description: 'message: Failed to trigger MCMP API sync" // Updated message' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: Sync MCMP API Definitions + summary: Remove workspace role tags: - - McmpAPI // Updated tag - /mcmp-apis/test/mc-infra-manager/getallns: - get: - description: Calls the GetAllNs action of the mc-infra-manager service via the - CallApi service. + - roles + /api/roles/workspace-roles: + post: + consumes: + - application/json + description: Create a new workspace role + operationId: createWorkspaceRole + parameters: + - description: Workspace Role Creation Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.CreateRoleRequest' produces: - application/json responses: "200": - description: Response from mc-infra-manager GetAllNs + description: OK schema: + additionalProperties: + type: string type: object "400": - description: 'error: Bad Request (e.g., invalid parameters)' + description: Bad Request schema: additionalProperties: type: string type: object - "404": - description: 'error: Service or Action Not Found' + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object - "500": - description: 'error: Internal Server Error' + security: + - BearerAuth: [] + summary: Create workspace role + tags: + - roles + /api/roles/workspace-roles/id/{roleId}: + delete: + consumes: + - application/json + description: Delete a workspace role + operationId: deleteWorkspaceRole + parameters: + - description: Workspace Role ID + in: path + name: roleId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "404": + description: Not Found schema: additionalProperties: type: string type: object - "503": - description: 'error: External API Service Unavailable' + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: Test Call to mc-infra-manager GetAllNs + summary: Delete workspace role tags: - - McmpAPI - - Test - /menus: + - roles get: consumes: - application/json - description: 현재 로그인한 사용자의 Platform Role에 따라 접근 가능한 메뉴 목록을 트리 구조로 조회합니다. + description: Get workspace role details by ID + operationId: getWorkspaceRoleByID + parameters: + - description: Workspace Role ID + in: path + name: id + required: true + type: string produces: - application/json responses: "200": description: OK schema: - items: - $ref: '#/definitions/model.MenuTreeNode' - type: array - "401": - description: 'error: Unauthorized' + $ref: '#/definitions/model.RoleMaster' + "404": + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 현재 사용자의 메뉴 트리 조회 - tags: - - menus - post: - consumes: - - application/json - description: 새로운 메뉴를 생성합니다 - parameters: - - description: Menu Info - in: body - name: menu - required: true - schema: - $ref: '#/definitions/model.Menu' + summary: Get workspace role by ID + tags: + - roles + /api/roles/workspace-roles/list: + post: + consumes: + - application/json + description: Get a list of all workspace roles + operationId: listWorkspaceRoles produces: - application/json responses: - "201": - description: Created + "200": + description: OK schema: - $ref: '#/definitions/model.Menu' + items: + $ref: '#/definitions/model.RoleMaster' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object security: - BearerAuth: [] - summary: 새 메뉴 생성 + summary: List workspace roles tags: - - menus - /menus/{id}: - delete: + - roles + /api/roles/workspace-roles/name/{roleName}: + get: consumes: - application/json - description: 메뉴를 삭제합니다 + description: Get workspace role details by Name + operationId: getWorkspaceRoleByName parameters: - - description: Menu ID + - description: Workspace Role Name in: path - name: id + name: name required: true type: string produces: - application/json responses: - "204": - description: No Content + "200": + description: OK + schema: + $ref: '#/definitions/model.RoleMaster' + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object security: - BearerAuth: [] - summary: 메뉴 삭제 + summary: Get workspace role by Name tags: - - menus + - roles + /api/setup/check-user-roles: get: consumes: - application/json - description: 특정 메뉴를 ID로 조회합니다 + description: Check all roles assigned to a user. 특정 유저가 가진 role 목록을 조회합니다. + operationId: checkUserRoles parameters: - - description: Menu ID - in: path - name: id + - description: Username to check roles + in: query + name: username required: true type: string produces: @@ -1024,401 +4020,457 @@ paths: "200": description: OK schema: - $ref: '#/definitions/model.Menu' - security: - - BearerAuth: [] - summary: 메뉴 ID로 조회 + $ref: '#/definitions/model.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/model.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/model.Response' + summary: Check user roles tags: - - menus - put: + - admin + /api/setup/initial-role-menu-permission: + get: consumes: - application/json - description: 메뉴 정보를 업데이트합니다 + description: CSV 파일을 읽어서 메뉴 권한을 초기화합니다 + operationId: initializeMenuPermissions parameters: - - description: Menu ID - in: path - name: id - required: true + - description: CSV file path (optional, uses default if not provided) + in: query + name: filePath type: string - - description: Menu Info - in: body - name: menu - required: true - schema: - $ref: '#/definitions/model.Menu' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/model.Menu' + $ref: '#/definitions/model.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/model.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/model.Response' security: - BearerAuth: [] - summary: 메뉴 정보 업데이트 + summary: Initialize menu permissions from CSV tags: - - menus - /menus/all: - get: + - admin + /api/setup/sync-projects: + post: consumes: - application/json - description: 모든 메뉴 목록을 트리 구조로 조회합니다. 관리자 권한이 필요합니다. + description: mc-infra-manager의 네임스페이스 목록을 조회하여 로컬 DB에 없는 프로젝트를 추가합니다. + operationId: syncProjects produces: - application/json responses: "200": - description: OK - schema: - items: - $ref: '#/definitions/model.MenuTreeNode' - type: array - "401": - description: 'error: Unauthorized' - schema: - additionalProperties: - type: string - type: object - "403": - description: 'error: Forbidden' + description: 'message: Project synchronization successful' schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: 'error: 서버 내부 오류 또는 동기화 실패' schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 모든 메뉴 트리 조회 (관리자용) + summary: mc-infra-manager와 프로젝트 동기화 tags: - - menus - /menus/register-from-body: + - projects + /api/users: post: consumes: - - text/plain - description: 요청 본문에 포함된 YAML 텍스트를 파싱하여 메뉴를 데이터베이스에 등록하거나 업데이트합니다. Content-Type은 - text/plain, text/yaml, application/yaml 등을 권장합니다. + - application/json + description: Create a new user with the specified information. + operationId: createUser parameters: - - description: Menu definitions in YAML format (must contain 'menus:' root key) - example: '"menus:\n - id: new-item\n parentid: dashboard\n displayname: - New Menu Item\n restype: menu\n isaction: false\n priority: 10\n menunumber: - 9999"' + - description: User Info in: body - name: yaml + name: user required: true schema: - type: string + $ref: '#/definitions/model.User' produces: - application/json responses: - "200": - description: 'message: Successfully registered menus from request body' + "201": + description: Created schema: - additionalProperties: - type: string - type: object + $ref: '#/definitions/model.User' "400": - description: 'error: 잘못된 요청 본문 또는 YAML 형식 오류' + description: Bad Request schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 요청 본문의 YAML 내용으로 메뉴 등록/업데이트 + summary: Create new user tags: - - menus - /menus/register-from-yaml: - post: + - users + /api/users/{id}: + delete: consumes: - application/json - description: filePath 쿼리 파라미터로 지정된 로컬 YAML 파일 또는 파라미터가 없을 경우 .env 파일의 MCWEBCONSOLE_MENUYAML - URL에서 메뉴를 가져와 데이터베이스에 등록/업데이트합니다. URL에서 가져올 경우 asset/menu/menu.yaml에 저장됩니다. + description: Delete a user by their ID. + operationId: deleteUser parameters: - - description: YAML 파일 경로 (선택 사항, 없으면 .env의 URL 또는 기본 로컬 경로 사용) - in: query - name: filePath + - description: User ID + in: path + name: id + required: true type: string produces: - application/json responses: - "200": - description: 'message: Successfully registered menus from YAML' + "204": + description: No Content + "404": + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 실패 메시지' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: YAML 파일 또는 URL에서 메뉴 등록/업데이트 + summary: Delete user tags: - - menus - /projects: - get: + - users + put: consumes: - application/json - description: 모든 프로젝트 목록을 조회합니다 (연결된 워크스페이스 정보 포함). + description: Update the details of an existing user. + operationId: updateUser + parameters: + - description: User ID + in: path + name: id + required: true + type: string + - description: User Info + in: body + name: user + required: true + schema: + $ref: '#/definitions/model.User' produces: - application/json responses: "200": description: OK schema: - items: - $ref: '#/definitions/model.Project' - type: array + $ref: '#/definitions/model.User' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 모든 프로젝트 조회 + summary: Update user tags: - - projects - post: + - users + /api/users/id/{userId}: + get: consumes: - application/json - description: 새로운 프로젝트를 생성합니다. + description: Retrieve user details by user ID. + operationId: getUserByID parameters: - - description: 프로젝트 정보 (ID, CreatedAt, UpdatedAt, Workspaces 제외) - in: body - name: project + - description: User ID + in: path + name: userId required: true - schema: - $ref: '#/definitions/model.Project' + type: string produces: - application/json responses: - "201": - description: Created + "200": + description: OK schema: - $ref: '#/definitions/model.Project' - "400": - description: 'error: 잘못된 요청 형식' + $ref: '#/definitions/model.User' + "404": + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 프로젝트 생성 + summary: Get user by ID tags: - - projects - /projects/{id}: - delete: + - users + /api/users/id/{userId}/status: + post: consumes: - application/json - description: 프로젝트를 삭제합니다. 연결된 워크스페이스와의 관계도 해제됩니다. + description: Update user status (active/inactive) + operationId: updateUserStatus parameters: - - description: 프로젝트 ID + - description: User ID in: path name: id required: true - type: integer + type: string + - description: User Status + in: body + name: status + required: true + schema: + $ref: '#/definitions/model.UserStatusRequest' produces: - application/json responses: - "204": - description: No Content + "200": + description: OK + schema: + $ref: '#/definitions/model.User' "400": - description: 'error: 잘못된 프로젝트 ID' + description: Bad Request schema: additionalProperties: type: string type: object "404": - description: 'error: 프로젝트를 찾을 수 없습니다' + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 프로젝트 삭제 + summary: Update user status tags: - - projects + - users + /api/users/id/{userId}/workspaces/id/{workspaceId}/roles/list: get: consumes: - application/json - description: ID로 특정 프로젝트를 조회합니다 (연결된 워크스페이스 정보 포함). + description: Get workspaces and roles for a specific user and workspace + operationId: getUserWorkspaceAndWorkspaceRolesByUserIDAndWorkspaceID parameters: - - description: 프로젝트 ID + - description: User ID in: path - name: id + name: userId required: true - type: integer + type: string + - description: Workspace ID + in: path + name: workspaceId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/model.Project' - "400": - description: 'error: 잘못된 프로젝트 ID' + items: + $ref: '#/definitions/model.UserWorkspaceRole' + type: array + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object - "404": - description: 'error: 프로젝트를 찾을 수 없습니다' + security: + - BearerAuth: [] + summary: Get user workspace and workspace roles by user ID and workspace ID + tags: + - users + /api/users/id/{userId}/workspaces/list: + get: + consumes: + - application/json + description: Get workspaces for a specific user + operationId: getUserWorkspacesByUserID + parameters: + - description: User ID + in: path + name: userId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Workspace' + type: array + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object + security: + - BearerAuth: [] + summary: Get user workspaces by user ID + tags: + - users + /api/users/id/{userId}/workspaces/roles/list: + get: + consumes: + - application/json + description: Get workspaces and roles for a specific user + operationId: getUserWorkspaceAndWorkspaceRolesByUserID + parameters: + - description: User ID + in: path + name: userId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.UserWorkspaceRole' + type: array "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: ID로 프로젝트 조회 + summary: Get user workspace and workspace roles by user ID tags: - - projects - put: + - users + /api/users/kc/{kcUserId}: + get: consumes: - application/json - description: 기존 프로젝트 정보를 부분적으로 수정합니다. + description: Get user details by KcID + operationId: getUserByKcID parameters: - - description: 프로젝트 ID - in: path - name: id - required: true - type: integer - - description: '수정할 필드와 값 (예: {\' - in: body - name: updates + - description: User KcID + in: path + name: kcUserId required: true - schema: - type: object + type: string produces: - application/json responses: "200": - description: 업데이트된 프로젝트 정보 - schema: - $ref: '#/definitions/model.Project' - "400": - description: 'error: 잘못된 요청 형식 또는 ID' + description: OK schema: - additionalProperties: - type: string - type: object + $ref: '#/definitions/model.User' "404": - description: 'error: 프로젝트를 찾을 수 없습니다' + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 프로젝트 수정 + summary: Get user by KcID tags: - - projects - /projects/{id}/workspaces/{workspaceId}: - delete: + - users + /api/users/list: + post: consumes: - application/json - description: 특정 프로젝트에서 워크스페이스 연결을 해제합니다. - parameters: - - description: 프로젝트 ID - in: path - name: id - required: true - type: integer - - description: 워크스페이스 ID - in: path - name: workspaceId - required: true - type: integer + description: Retrieve a list of all users. + operationId: listUsers produces: - application/json responses: - "204": - description: No Content - "400": - description: 'error: 잘못된 ID 형식' + "200": + description: OK schema: - additionalProperties: - type: string - type: object + items: + $ref: '#/definitions/model.User' + type: array "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 프로젝트에서 워크스페이스 연결 해제 + summary: List all users tags: - - projects + - users + /api/users/menus-tree/list: post: consumes: - application/json - description: 특정 프로젝트에 워크스페이스를 연결합니다. - parameters: - - description: 프로젝트 ID - in: path - name: id - required: true - type: integer - - description: 워크스페이스 ID - in: path - name: workspaceId - required: true - type: integer + description: Get the menu tree accessible to the current user's platform role. + operationId: listUserMenuTree produces: - application/json responses: - "204": - description: No Content - "400": - description: 'error: 잘못된 ID 형식' + "200": + description: OK schema: - additionalProperties: - type: string - type: object - "404": - description: 'error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다' + items: + $ref: '#/definitions/model.MenuTreeNode' + type: array + "401": + description: 'error: Unauthorized' schema: additionalProperties: type: string @@ -1431,16 +4483,35 @@ paths: type: object security: - BearerAuth: [] - summary: 프로젝트에 워크스페이스 연결 + summary: Get current user's menu tree tags: - - projects - /projects/name/{name}: + - menus + /api/users/menus/list: + post: + consumes: + - application/json + description: Get the menu list accessible to the current user's platform role. + operationId: listUserMenu + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Menu' + type: array + summary: Get current user's menu list + tags: + - menus + /api/users/name/{username}: get: consumes: - application/json - description: 이름으로 특정 프로젝트를 조회합니다 (연결된 워크스페이스 정보 포함). + description: Get user details by username + operationId: getUserByUsername parameters: - - description: 프로젝트 이름 + - description: Username in: path name: name required: true @@ -1451,282 +4522,343 @@ paths: "200": description: OK schema: - $ref: '#/definitions/model.Project' + $ref: '#/definitions/model.User' "404": - description: 'error: 프로젝트를 찾을 수 없습니다' + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 이름으로 프로젝트 조회 + summary: Get user by username tags: - - projects - /readyz: + - users + /api/users/workspaces/id/{workspaceId}/projects/list: get: - description: 애플리케이션의 준비 상태를 확인합니다. status=detail 쿼리 파라미터로 상세 상태를 확인할 수 있습니다. + consumes: + - application/json + description: List projects for the current user + operationId: listUserProjectsByWorkspace parameters: - - description: 상세 상태 확인 여부 ('detail') - in: query - name: status + - description: Workspace ID + in: path + name: workspaceId + required: true type: string produces: - application/json responses: "200": - description: 상세 상태 정보 (status=detail) + description: OK schema: - $ref: '#/definitions/service.HealthStatus' - "503": - description: 상세 상태 확인 중 오류 발생 시 + items: + $ref: '#/definitions/model.Project' + type: array + "500": + description: Internal Server Error schema: - $ref: '#/definitions/service.HealthStatus' - summary: 애플리케이션 준비 상태 확인 + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List user projects by workspace tags: - - Health - /user/workspaces: - get: - description: 현재 로그인한 사용자가 접근 가능한 워크스페이스 및 각 워크스페이스에서의 역할 목록을 조회합니다. + - users + /api/users/workspaces/list: + post: + consumes: + - application/json + description: List workspaces for the current user + operationId: listUserWorkspaces produces: - application/json responses: "200": - description: 성공 시 워크스페이스 및 역할 정보 목록 반환 + description: OK schema: items: - $ref: '#/definitions/service.WorkspaceRoleInfo' + $ref: '#/definitions/model.Workspace' type: array - "401": - description: 'error: Unauthorized' - schema: - additionalProperties: - type: string - type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 내 워크스페이스 및 역할 목록 조회 + summary: List user workspaces tags: - users - - workspaces - - roles - - me - /users: - get: - description: 모든 사용자 목록을 조회합니다. 'admin' 또는 'platformAdmin' 역할이 필요합니다. + /api/users/workspaces/roles/list: + post: + consumes: + - application/json + description: List workspaces and roles for the current user + operationId: listUserWorkspaceAndWorkspaceRoles produces: - application/json responses: "200": - description: 성공 시 사용자 목록 반환 + description: OK schema: items: - $ref: '#/definitions/model.User' + $ref: '#/definitions/model.RoleMaster' type: array - "401": - description: 'error: Unauthorized' + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object - "403": - description: 'error: Forbidden (권한 부족)' + security: + - BearerAuth: [] + summary: List user workspace and roles + tags: + - users + /api/workspaces: + post: + consumes: + - application/json + description: Create a new workspace with the specified information. Optionally + assign existing projects to the workspace. + operationId: createWorkspace + parameters: + - description: Workspace Info + in: body + name: workspace + required: true + schema: + $ref: '#/definitions/model.Workspace' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.Workspace' + "400": + description: Bad Request schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 사용자 목록 조회 (관리자용) + summary: Create new workspace tags: - - users - post: - responses: {} - security: - - BearerAuth: [] - /users/{id}: - put: - parameters: - - description: User DB ID - in: path - name: id - required: true - type: integer - responses: {} - security: - - BearerAuth: [] - /users/{id}/approve: + - workspaces + /api/workspaces/{id}/users: post: consumes: - application/json - description: 지정된 사용자를 활성화하고 시스템 사용을 승인합니다. 'admin' 또는 'platformadmin' 역할이 필요합니다. + description: Add a user to a workspace + operationId: addUserToWorkspace parameters: - - description: 사용자 Keycloak ID + - description: Workspace ID in: path name: id required: true type: string + - description: User Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AssignRoleRequest' produces: - application/json responses: - "204": - description: No Content - "400": - description: 'error: 잘못된 사용자 ID' + "200": + description: OK schema: additionalProperties: type: string type: object - "401": - description: 'error: Unauthorized' + "400": + description: Bad Request schema: additionalProperties: type: string type: object - "403": - description: 'error: Forbidden (권한 부족)' + "404": + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 사용자 승인 (관리자용) + summary: Add user to workspace tags: - - users - /workspaces: - get: + - workspaces + /api/workspaces/{id}/users/{userId}: + delete: consumes: - application/json - description: 모든 워크스페이스 목록을 조회합니다 (연결된 프로젝트 정보 포함). + description: Remove a user from a workspace + operationId: removeUserFromWorkspace + parameters: + - description: Workspace ID + in: path + name: id + required: true + type: string + - description: User ID + in: path + name: userId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - items: - $ref: '#/definitions/model.Workspace' - type: array + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 모든 워크스페이스 조회 + summary: Remove user from workspace tags: - workspaces + /api/workspaces/assign/projects: post: consumes: - application/json - description: 새로운 워크스페이스를 생성합니다. + description: Add a project to a workspace + operationId: addProjectToWorkspace parameters: - - description: 워크스페이스 정보 (ID, CreatedAt, UpdatedAt, Projects 제외) - in: body - name: workspace + - description: Workspace ID + in: path + name: id required: true - schema: - $ref: '#/definitions/model.Workspace' + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string produces: - application/json responses: - "201": - description: Created + "200": + description: OK schema: $ref: '#/definitions/model.Workspace' "400": - description: 'error: 잘못된 요청 형식' + description: 'error: Invalid request' schema: additionalProperties: type: string type: object - "500": - description: 'error: 서버 내부 오류' + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Workspace or Project not found' schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스 생성 + summary: Add project to workspace tags: - workspaces - /workspaces/{id}: + /api/workspaces/id/{workspaceId}: delete: consumes: - application/json - description: 워크스페이스를 삭제합니다. 연결된 프로젝트와의 관계도 해제됩니다. + description: Delete a workspace by its ID. + operationId: deleteWorkspace parameters: - - description: 워크스페이스 ID + - description: Workspace ID in: path name: id required: true - type: integer + type: string produces: - application/json - responses: - "204": - description: No Content - "400": - description: 'error: 잘못된 워크스페이스 ID' - schema: - additionalProperties: - type: string - type: object + responses: + "204": + description: No Content "404": - description: 'error: 워크스페이스를 찾을 수 없습니다' + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스 삭제 + summary: Delete workspace tags: - workspaces get: consumes: - application/json - description: ID로 특정 워크스페이스를 조회합니다 (연결된 프로젝트 정보 포함). + description: Retrieve workspace details by workspace ID. + operationId: getWorkspaceByID parameters: - - description: 워크스페이스 ID + - description: Workspace ID in: path - name: id + name: workspaceId required: true - type: integer + type: string produces: - application/json responses: @@ -1734,393 +4866,462 @@ paths: description: OK schema: $ref: '#/definitions/model.Workspace' - "400": - description: 'error: 잘못된 워크스페이스 ID' - schema: - additionalProperties: - type: string - type: object "404": - description: 'error: 워크스페이스를 찾을 수 없습니다' + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: ID로 워크스페이스 조회 + summary: Get workspace by ID tags: - workspaces put: consumes: - application/json - description: 기존 워크스페이스 정보를 부분적으로 수정합니다. + description: Update the details of an existing workspace. + operationId: updateWorkspace parameters: - - description: 워크스페이스 ID + - description: Workspace ID in: path name: id required: true - type: integer - - description: '수정할 필드와 값 (예: {\' + type: string + - description: Workspace Info in: body - name: updates + name: workspace required: true schema: - type: object + $ref: '#/definitions/model.Workspace' produces: - application/json responses: "200": - description: 업데이트된 워크스페이스 정보 + description: OK schema: $ref: '#/definitions/model.Workspace' "400": - description: 'error: 잘못된 요청 형식 또는 ID' + description: Bad Request schema: additionalProperties: type: string type: object "404": - description: 'error: 워크스페이스를 찾을 수 없습니다' + description: Not Found schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스 수정 + summary: Update workspace tags: - workspaces - /workspaces/{id}/projects: + /api/workspaces/id/{workspaceId}/projects/list: get: consumes: - application/json - description: 특정 워크스페이스 ID에 연결된 모든 프로젝트 목록을 조회합니다. + description: Retrieve project list belonging to specific workspace + operationId: getWorkspaceProjectsByWorkspaceId parameters: - - description: 워크스페이스 ID + - description: Workspace ID in: path - name: id + name: workspaceId required: true - type: integer + type: string produces: - application/json responses: "200": - description: 성공 시 프로젝트 목록 반환 + description: OK schema: items: $ref: '#/definitions/model.Project' type: array - "400": - description: 'error: 잘못된 워크스페이스 ID' + "401": + description: 'error: Unauthorized' schema: additionalProperties: type: string type: object - "404": - description: 'error: 워크스페이스를 찾을 수 없습니다' + "403": + description: 'error: Forbidden' schema: additionalProperties: type: string type: object - "500": - description: 'error: 서버 내부 오류' + "404": + description: 'error: Workspace not found' schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스에 연결된 프로젝트 목록 조회 + summary: List workspace projects tags: - workspaces - /workspaces/{id}/projects/{projectId}: - delete: + /api/workspaces/id/{workspaceId}/users/id/{userId}: + get: consumes: - application/json - description: 특정 워크스페이스에서 프로젝트 연결을 해제합니다. + description: Get roles assigned to a user in a workspace + operationId: getUserWorkspaceRoles parameters: - - description: 워크스페이스 ID + - description: User ID in: path - name: id + name: userId required: true - type: integer - - description: 프로젝트 ID + type: string + - description: Workspace ID in: path - name: projectId + name: workspaceId required: true - type: integer + type: string produces: - application/json responses: - "204": - description: No Content + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.RoleMaster' + type: array "400": - description: 'error: 잘못된 ID 형식' + description: Bad Request schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스에서 프로젝트 연결 해제 + summary: Get user workspace roles tags: - - workspaces + - roles + /api/workspaces/id/{workspaceId}/users/list: post: consumes: - application/json - description: 특정 워크스페이스에 프로젝트를 연결합니다. + description: Retrieve users and roles list belonging to workspace + operationId: listUsersAndRolesByWorkspace parameters: - - description: 워크스페이스 ID - in: path - name: id - required: true - type: integer - - description: 프로젝트 ID + - description: Workspace ID in: path - name: projectId + name: workspaceId required: true type: integer produces: - application/json responses: - "204": - description: No Content + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.UserWorkspaceRole' + type: array "400": - description: 'error: 잘못된 ID 형식' + description: 'error: Invalid workspace ID' schema: additionalProperties: type: string type: object "404": - description: 'error: 워크스페이스 또는 프로젝트를 찾을 수 없습니다' + description: 'error: Workspace not found' schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: 'error: Internal server error' + schema: + additionalProperties: + type: string + type: object + summary: List users and roles by workspace + tags: + - workspaces + /api/workspaces/list: + post: + consumes: + - application/json + description: Retrieve a list of all workspaces. + operationId: listWorkspaces + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Workspace' + type: array + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스에 프로젝트 연결 + summary: List all workspaces tags: - workspaces - /workspaces/{id}/users: + /api/workspaces/name/{workspaceName}: get: consumes: - application/json - description: 특정 워크스페이스에 속한 모든 사용자와 각 사용자의 역할을 조회합니다. + description: Retrieve specific workspace by name + operationId: getWorkspaceByName parameters: - - description: 워크스페이스 ID + - description: Workspace Name in: path - name: id + name: workspaceName required: true - type: integer + type: string produces: - application/json responses: "200": - description: 성공 시 사용자 및 역할 목록 반환 + description: OK schema: - items: - $ref: '#/definitions/service.UserWithRoles' - type: array - "400": - description: 'error: 잘못된 워크스페이스 ID' + $ref: '#/definitions/model.Workspace' + "401": + description: 'error: Unauthorized' schema: additionalProperties: type: string type: object - "404": - description: 'error: 워크스페이스를 찾을 수 없습니다' + "403": + description: 'error: Forbidden' schema: additionalProperties: type: string type: object - "500": - description: 'error: 서버 내부 오류' + "404": + description: 'error: Workspace not found' schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스 사용자 및 역할 목록 조회 + summary: Get workspace by name tags: - workspaces - - users - - roles - /workspaces/{workspaceId}/users/{userId}/roles/{roleId}: - delete: + /api/workspaces/projects/list: + post: consumes: - application/json - description: 특정 워크스페이스 내의 사용자에게서 특정 워크스페이스 역할을 제거합니다. + description: Retrieve project list belonging to specific workspace + operationId: listWorkspaceProjects parameters: - - description: 워크스페이스 ID + - description: Workspace ID in: path name: workspaceId required: true - type: integer - - description: 사용자 DB ID (db_id) - in: path - name: userId - required: true - type: integer - - description: 워크스페이스 역할 ID - in: path - name: roleId - required: true - type: integer + type: string produces: - application/json responses: - "204": - description: No Content - "400": - description: 'error: 잘못된 ID 형식' + "200": + description: OK schema: - additionalProperties: - type: string - type: object - "404": - description: 'error: 역할 또는 워크스페이스를 찾을 수 없습니다" // User existence check is - optional here' + items: + $ref: '#/definitions/model.Project' + type: array + "401": + description: 'error: Unauthorized' schema: additionalProperties: type: string type: object - "409": - description: 'error: 역할이 해당 워크스페이스에 속하지 않음' + "403": + description: 'error: Forbidden' schema: additionalProperties: type: string type: object - "500": - description: 'error: 서버 내부 오류' + "404": + description: 'error: Workspace not found' schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스 사용자 역할 제거 + summary: List workspace projects tags: - workspaces - - roles - - users + /api/workspaces/temporary-credentials: post: consumes: - application/json - description: 특정 워크스페이스 내의 사용자에게 특정 워크스페이스 역할을 할당합니다. + description: Get temporary credentials for CSP + operationId: mciamGetTemporaryCredentials + produces: + - application/json + responses: {} + security: + - BearerAuth: [] + summary: Get temporary credentials + tags: + - csp-credentials + /api/workspaces/unassign/projects: + delete: + consumes: + - application/json + description: Remove a project from a workspace + operationId: removeProjectFromWorkspace parameters: - - description: 워크스페이스 ID - in: path - name: workspaceId - required: true - type: integer - - description: 사용자 DB ID (db_id) - in: path - name: userId - required: true - type: integer - - description: 워크스페이스 역할 ID + - description: Workspace ID in: path - name: roleId + name: id required: true - type: integer + type: string + produces: + - application/json + responses: {} + summary: Remove project from workspace + tags: + - workspaces + /api/workspaces/users-roles/list: + post: + consumes: + - application/json + description: Retrieve the list of users and roles assigned to the workspace. + operationId: listAllWorkspaceUsersAndRoles produces: - application/json responses: - "204": - description: No Content - "400": - description: 'error: 잘못된 ID 형식' + "200": + description: OK schema: - additionalProperties: - type: string - type: object - "404": - description: 'error: 사용자, 역할 또는 워크스페이스를 찾을 수 없습니다' + items: + $ref: '#/definitions/model.WorkspaceWithUsersAndRoles' + type: array + "401": + description: 'error: Unauthorized' schema: additionalProperties: type: string type: object - "409": - description: 'error: 역할이 해당 워크스페이스에 속하지 않음' + "403": + description: 'error: Forbidden' schema: additionalProperties: type: string type: object "500": - description: 'error: 서버 내부 오류' + description: 'error: Internal server error' schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 워크스페이스 사용자에게 역할 할당 + summary: List users and roles in workspace tags: - workspaces - - roles - - users - /workspaces/name/{name}: - get: + /api/workspaces/users/list: + post: consumes: - application/json - description: 이름으로 특정 워크스페이스를 조회합니다 (연결된 프로젝트 정보 포함). - parameters: - - description: 워크스페이스 이름 - in: path - name: name - required: true - type: string + description: List users by workspace criteria + operationId: listWorkspaceUsers produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/model.Workspace' - "404": - description: 'error: 워크스페이스를 찾을 수 없습니다' + items: + $ref: '#/definitions/model.WorkspaceWithUsersAndRoles' + type: array + "401": + description: 'error: Unauthorized' schema: additionalProperties: type: string type: object - "500": - description: 'error: 서버 내부 오류' + "403": + description: 'error: Forbidden' schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: 이름으로 워크스페이스 조회 + summary: List workspace users tags: - workspaces + /api/workspaces/workspace-ticket: + post: + consumes: + - application/json + description: Set workspace ticket + operationId: mciamWorkspaceTicket + produces: + - application/json + responses: + "200": + description: 'message: Workspace ticket set successfully' + schema: + additionalProperties: + type: string + type: object + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Set workspace ticket + tags: + - auth + /readyz: + get: + consumes: + - application/json + description: Check the health status of the service. + operationId: mciamCheckHealth + parameters: + - description: Detail check components (nginx,db,keycloak,all) + in: query + name: detail + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + summary: Health check + tags: + - health securityDefinitions: BearerAuth: description: Type "Bearer" followed by a space and JWT token. diff --git a/src/docs/docs.go b/src/docs/docs.go index bc98c2c1..d982074a 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -127,7 +127,44 @@ const docTemplate = `{ ], "summary": "Refresh access token", "operationId": "mciamRefreshToken", - "responses": {} + "parameters": [ + { + "description": "Refresh token", + "name": "refresh_token", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "New token information", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "error: Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } } }, "/api/auth/temp-credential-csps": { @@ -174,6 +211,17 @@ const docTemplate = `{ ], "summary": "Validate access token", "operationId": "mciamValidateToken", + "parameters": [ + { + "description": "Refresh token", + "name": "refresh_token", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "Token validation result with new token if refreshed", @@ -182,6 +230,15 @@ const docTemplate = `{ "additionalProperties": true } }, + "400": { + "description": "error: Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "401": { "description": "error: Unauthorized", "schema": { @@ -1952,7 +2009,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Create a new project with the specified information.", + "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", "consumes": [ "application/json" ], @@ -1971,7 +2028,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.CreateProjectRequest" } } ], @@ -1991,6 +2048,15 @@ const docTemplate = `{ } } }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -2003,6 +2069,74 @@ const docTemplate = `{ } } }, + "/api/projects/id/{projectId}/workspaces": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve list of workspaces that the project is assigned to", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Get workspaces assigned to project", + "operationId": "getProjectWorkspaces", + "parameters": [ + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "400": { + "description": "error: Invalid project ID", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Project not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/list": { "post": { "security": [ @@ -5938,7 +6072,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Create a new workspace with the specified information.", + "description": "Create a new workspace with the specified information. Optionally assign existing projects to the workspace.", "consumes": [ "application/json" ], @@ -6984,6 +7118,14 @@ const docTemplate = `{ ], "summary": "Health check", "operationId": "mciamCheckHealth", + "parameters": [ + { + "type": "string", + "description": "Detail check components (nginx,db,keycloak,all)", + "name": "detail", + "in": "query" + } + ], "responses": { "200": { "description": "OK", @@ -7305,6 +7447,24 @@ const docTemplate = `{ } } }, + "model.CreateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "workspaceId": { + "description": "optional workspace to assign project to", + "type": "string" + } + } + }, "model.CreateRoleRequest": { "type": "object", "required": [ diff --git a/src/docs/swagger.json b/src/docs/swagger.json index f17fc3c6..23b1d808 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -121,7 +121,44 @@ ], "summary": "Refresh access token", "operationId": "mciamRefreshToken", - "responses": {} + "parameters": [ + { + "description": "Refresh token", + "name": "refresh_token", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "New token information", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "error: Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } } }, "/api/auth/temp-credential-csps": { @@ -168,6 +205,17 @@ ], "summary": "Validate access token", "operationId": "mciamValidateToken", + "parameters": [ + { + "description": "Refresh token", + "name": "refresh_token", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "Token validation result with new token if refreshed", @@ -176,6 +224,15 @@ "additionalProperties": true } }, + "400": { + "description": "error: Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "401": { "description": "error: Unauthorized", "schema": { @@ -1946,7 +2003,7 @@ "BearerAuth": [] } ], - "description": "Create a new project with the specified information.", + "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", "consumes": [ "application/json" ], @@ -1965,7 +2022,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.CreateProjectRequest" } } ], @@ -1985,6 +2042,15 @@ } } }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -1997,6 +2063,74 @@ } } }, + "/api/projects/id/{projectId}/workspaces": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve list of workspaces that the project is assigned to", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Get workspaces assigned to project", + "operationId": "getProjectWorkspaces", + "parameters": [ + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "400": { + "description": "error: Invalid project ID", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Project not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/list": { "post": { "security": [ @@ -5932,7 +6066,7 @@ "BearerAuth": [] } ], - "description": "Create a new workspace with the specified information.", + "description": "Create a new workspace with the specified information. Optionally assign existing projects to the workspace.", "consumes": [ "application/json" ], @@ -6978,6 +7112,14 @@ ], "summary": "Health check", "operationId": "mciamCheckHealth", + "parameters": [ + { + "type": "string", + "description": "Detail check components (nginx,db,keycloak,all)", + "name": "detail", + "in": "query" + } + ], "responses": { "200": { "description": "OK", @@ -7299,6 +7441,24 @@ } } }, + "model.CreateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "workspaceId": { + "description": "optional workspace to assign project to", + "type": "string" + } + } + }, "model.CreateRoleRequest": { "type": "object", "required": [ diff --git a/src/handler/project_handler.go b/src/handler/project_handler.go index 54d6d9f6..a4e76d92 100644 --- a/src/handler/project_handler.go +++ b/src/handler/project_handler.go @@ -41,28 +41,45 @@ func NewProjectHandler(db *gorm.DB) *ProjectHandler { // CreateProject godoc // @Summary Create new project -// @Description Create a new project with the specified information. +// @Description Create a new project with the specified information. Optionally specify a workspace to assign the project to. // @Tags projects // @Accept json // @Produce json -// @Param project body model.Project true "Project Info" +// @Param project body model.CreateProjectRequest true "Project Info" // @Success 201 {object} model.Project // @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/projects [post] // @Id createProject func (h *ProjectHandler) CreateProject(c echo.Context) error { - var project model.Project - if err := c.Bind(&project); err != nil { + var req model.CreateProjectRequest + if err := c.Bind(&req); err != nil { return c.JSON(http.StatusBadRequest, map[string]string{"error": "잘못된 요청 형식입니다"}) } - project.ID = 0 // Ensure ID is not set by client - // project.NsId will be set by the service after calling the external API - // Call the service Create method, passing the context - if err := h.projectService.Create(c.Request().Context(), &project); err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("프로젝트 생성 실패 (DB 저장 오류): %v", err)}) + project := &model.Project{ + Name: req.Name, + Description: req.Description, + } + + // Parse optional workspaceId + var workspaceID uint + if req.WorkspaceID != "" { + workspaceIDInt, err := util.StringToUint(req.WorkspaceID) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "잘못된 워크스페이스 ID 형식입니다"}) + } + workspaceID = workspaceIDInt + } + + // Call the service Create method with optional workspaceID + if err := h.projectService.Create(c.Request().Context(), project, workspaceID); err != nil { + if err.Error() == "workspace not found" { + return c.JSON(http.StatusNotFound, map[string]string{"error": "워크스페이스를 찾을 수 없습니다"}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("프로젝트 생성 실패: %v", err)}) } log.Printf("Successfully created project '%s' (ID: %d, NsId: %s)", project.Name, project.ID, project.NsId) @@ -231,6 +248,44 @@ func (h *ProjectHandler) DeleteProject(c echo.Context) error { return c.NoContent(http.StatusNoContent) } +// GetProjectWorkspaces godoc +// @Summary Get workspaces assigned to project +// @Description Retrieve list of workspaces that the project is assigned to +// @Tags projects +// @Accept json +// @Produce json +// @Param projectId path string true "Project ID" +// @Success 200 {array} model.Workspace +// @Failure 400 {object} map[string]string "error: Invalid project ID" +// @Failure 404 {object} map[string]string "error: Project not found" +// @Failure 500 {object} map[string]string "error: Internal server error" +// @Security BearerAuth +// @Router /api/projects/id/{projectId}/workspaces [get] +// @Id getProjectWorkspaces +func (h *ProjectHandler) GetProjectWorkspaces(c echo.Context) error { + projectIDInt, err := util.StringToUint(c.Param("projectId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid project ID"}) + } + + workspaces, err := h.projectService.GetProjectWorkspaces(projectIDInt) + if err != nil { + if err.Error() == "project not found" { + return c.JSON(http.StatusNotFound, map[string]string{"error": "Project not found"}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{ + "error": fmt.Sprintf("Failed to retrieve workspace list: %v", err), + }) + } + + // 빈 배열 처리 + if workspaces == nil { + workspaces = []*model.Workspace{} + } + + return c.JSON(http.StatusOK, workspaces) +} + // SyncProjects godoc // @Summary mc-infra-manager와 프로젝트 동기화 // @Description mc-infra-manager의 네임스페이스 목록을 조회하여 로컬 DB에 없는 프로젝트를 추가합니다. diff --git a/src/handler/workspace_handler.go b/src/handler/workspace_handler.go index 803973fc..bceec20f 100644 --- a/src/handler/workspace_handler.go +++ b/src/handler/workspace_handler.go @@ -144,7 +144,7 @@ func (h *WorkspaceHandler) GetWorkspaceByID(c echo.Context) error { // CreateWorkspace godoc // @Summary Create new workspace -// @Description Create a new workspace with the specified information. +// @Description Create a new workspace with the specified information. Optionally assign existing projects to the workspace. // @Tags workspaces // @Accept json // @Produce json @@ -163,12 +163,35 @@ func (h *WorkspaceHandler) CreateWorkspace(c echo.Context) error { }) } + // Save projects array temporarily and clear it to avoid duplicate assignment + projectsToAssign := workspace.Projects + workspace.Projects = nil + + // Create workspace if err := h.workspaceService.CreateWorkspace(&workspace); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to create workspace", }) } + // If projects array is provided, assign them to the new workspace + if len(projectsToAssign) > 0 { + for _, project := range projectsToAssign { + // AddProjectToWorkspace will remove project from any existing workspace + // and assign it to the new workspace (enforcing 1:N relationship) + if err := h.workspaceService.AddProjectToWorkspace(workspace.ID, project.ID); err != nil { + log.Printf("Failed to assign project %d to workspace %d: %v", project.ID, workspace.ID, err) + // Continue with other projects even if one fails + } + } + + // Fetch updated workspace with assigned projects + updatedWorkspace, err := h.workspaceService.GetWorkspaceProjectsByWorkspaceId(workspace.ID) + if err == nil && updatedWorkspace != nil { + return c.JSON(http.StatusCreated, updatedWorkspace) + } + } + return c.JSON(http.StatusCreated, workspace) } diff --git a/src/main.go b/src/main.go index c669323f..7abf05e9 100755 --- a/src/main.go +++ b/src/main.go @@ -231,6 +231,8 @@ func main() { projects.PUT("/id/:projectId", projectHandler.UpdateProject, middleware.PlatformRoleMiddleware(middleware.Manage)) projects.DELETE("/id/:projectId", projectHandler.DeleteProject, middleware.PlatformRoleMiddleware(middleware.Manage)) + projects.GET("/id/:projectId/workspaces", projectHandler.GetProjectWorkspaces) // Get workspaces assigned to project + projects.POST("/assign/workspaces", projectHandler.AddWorkspaceToProject, middleware.PlatformAdminMiddleware) projects.DELETE("/unassign/workspaces", projectHandler.RemoveWorkspaceFromProject, middleware.PlatformAdminMiddleware) } diff --git a/src/model/request.go b/src/model/request.go index 392b889a..28f0afcd 100644 --- a/src/model/request.go +++ b/src/model/request.go @@ -27,6 +27,7 @@ type ProjectFilterRequest struct { type CreateProjectRequest struct { Name string `json:"name" validate:"required"` Description string `json:"description"` + WorkspaceID string `json:"workspaceId,omitempty"` // optional workspace to assign project to } // McmpApiRequestParams defines the structure for parameters needed in an API call. diff --git a/src/repository/workspace_repository.go b/src/repository/workspace_repository.go index 7a183da0..accf7250 100644 --- a/src/repository/workspace_repository.go +++ b/src/repository/workspace_repository.go @@ -182,24 +182,23 @@ func (r *WorkspaceRepository) FindWorkspaceProjectsByWorkspaceID(id uint) (*mode // AddProjectAssociation add project association to workspace func (r *WorkspaceRepository) AddProjectAssociation(workspaceID, projectID uint) error { + // Remove all existing workspace associations for this project (1:N relationship enforcement) + // This ensures a project can only belong to one workspace at a time + result := r.db.Where("project_id = ?", projectID). + Delete(&model.WorkspaceProject{}) + if result.Error != nil { + return result.Error + } + + // Add new workspace association workspaceProject := &model.WorkspaceProject{ WorkspaceID: workspaceID, ProjectID: projectID, } - // If project was stored in default workspace, remove it from default workspace - if workspaceID != 1 { - result := r.db.Where("workspace_id = ? AND project_id = ?", 1, projectID). - Delete(&model.WorkspaceProject{}) - if result.Error != nil { - return result.Error - } - } - // Save directly to mcmp_workspace_projects table err := r.db.Save(workspaceProject).Error if err != nil { - return err } return nil diff --git a/src/service/project_service.go b/src/service/project_service.go index bda1fb78..248b3ef7 100644 --- a/src/service/project_service.go +++ b/src/service/project_service.go @@ -45,7 +45,8 @@ func NewProjectService(db *gorm.DB) *ProjectService { } // Create 프로젝트 생성 (mc-infra-manager 호출 및 DB 저장) -func (s *ProjectService) Create(ctx context.Context, project *model.Project) error { +// workspaceID is optional: if 0, assigns to default workspace; otherwise assigns to specified workspace +func (s *ProjectService) Create(ctx context.Context, project *model.Project, workspaceID ...uint) error { // 이름 중복 체크 existingProject, err := s.projectRepo.FindProjectByProjectName(project.Name) if err == nil && existingProject != nil { @@ -68,38 +69,31 @@ func (s *ProjectService) Create(ctx context.Context, project *model.Project) err "name": project.Name, "description": project.Description, } - // bodyBytes, err := json.Marshal(nsRequestBody) // Don't marshal here - // if err != nil { - // log.Printf("Error marshalling request body for PostNs: %v", err) - // return fmt.Errorf("failed to marshal request body for PostNs: %w", err) - // } callReq := &model.McmpApiCallRequest{ ServiceName: "mc-infra-manager", - ActionName: "Postns", // Corrected action name based on previous analysis + ActionName: "Postns", RequestParams: model.McmpApiRequestParams{ - Body: nsRequestBody, // Pass the original map directly + Body: nsRequestBody, }, } log.Printf("About to call mcmpApiService.McmpApiCall with service: %+v", s.mcmpApiService) - statusCode, respBody, serviceVersion, calledURL, err := s.mcmpApiService.McmpApiCall(ctx, callReq) // Get new return values + statusCode, respBody, serviceVersion, calledURL, err := s.mcmpApiService.McmpApiCall(ctx, callReq) if err != nil { - // Include version and URL in the error message log.Printf("Error calling %s(v%s) %s (URL: %s): %v (status code: %d)", callReq.ServiceName, serviceVersion, callReq.ActionName, calledURL, err, statusCode) return fmt.Errorf("failed to call %s(v%s) %s (URL: %s): %w (status code: %d)", callReq.ServiceName, serviceVersion, callReq.ActionName, calledURL, err, statusCode) } if statusCode < 200 || statusCode >= 300 { - // Include version and URL in the error message log.Printf("%s(v%s) %s call failed (URL: %s): status code %d, response: %s", callReq.ServiceName, serviceVersion, callReq.ActionName, calledURL, statusCode, string(respBody)) var errorResp map[string]interface{} - errMsg := fmt.Sprintf("%s(v%s) %s call failed with status code %d (URL: %s)", callReq.ServiceName, serviceVersion, callReq.ActionName, statusCode, calledURL) // Base error message + errMsg := fmt.Sprintf("%s(v%s) %s call failed with status code %d (URL: %s)", callReq.ServiceName, serviceVersion, callReq.ActionName, statusCode, calledURL) if json.Unmarshal(respBody, &errorResp) == nil { if msg, ok := errorResp["message"].(string); ok { - errMsg = fmt.Sprintf("%s(v%s) error: %s (URL: %s, Status: %d)", callReq.ServiceName, serviceVersion, msg, calledURL, statusCode) // More specific message if possible + errMsg = fmt.Sprintf("%s(v%s) error: %s (URL: %s, Status: %d)", callReq.ServiceName, serviceVersion, msg, calledURL, statusCode) } } - return errors.New(errMsg) // Return as a simple error for now, or wrap if needed + return errors.New(errMsg) } // Extract NsId from response @@ -124,44 +118,61 @@ func (s *ProjectService) Create(ctx context.Context, project *model.Project) err // 2. Create project in local DB if err := s.projectRepo.CreateProject(project); err != nil { - return err // Return DB creation error + return err } - // 3. Assign to default workspace - defaultWsName := os.Getenv("DEFAULT_WORKSPACE_NAME") - if defaultWsName == "" { - defaultWsName = "default" - log.Printf("DEFAULT_WORKSPACE_NAME not set in environment, using default value: %s", defaultWsName) - } - log.Printf("Using workspace name: %s", defaultWsName) - defaultWs, err := s.workspaceRepo.FindWorkspaceByName(defaultWsName) - if err != nil { - if err.Error() == "workspace not found" { - // Default workspace doesn't exist, create it - log.Printf("Default workspace '%s' not found. Creating it...", defaultWsName) - newWorkspace := &model.Workspace{ - Name: defaultWsName, - Description: "Default workspace for automatically synced projects", - } - if err := s.workspaceRepo.CreateWorkspace(newWorkspace); err != nil { - log.Printf("Error creating default workspace '%s': %v", defaultWsName, err) - return fmt.Errorf("failed to create default workspace: %w", err) + // 3. Determine target workspace (specified or default) + var targetWorkspace *model.Workspace + var targetWorkspaceID uint + + if len(workspaceID) > 0 && workspaceID[0] != 0 { + // Workspace specified, verify it exists + targetWorkspaceID = workspaceID[0] + targetWorkspace, err = s.workspaceRepo.FindWorkspaceByID(targetWorkspaceID) + if err != nil || targetWorkspace == nil { + log.Printf("Error finding specified workspace %d: %v", targetWorkspaceID, err) + return fmt.Errorf("workspace not found") + } + log.Printf("Assigning project to specified workspace: %s (ID: %d)", targetWorkspace.Name, targetWorkspace.ID) + } else { + // No workspace specified, use default + defaultWsName := os.Getenv("DEFAULT_WORKSPACE_NAME") + if defaultWsName == "" { + defaultWsName = "default" + log.Printf("DEFAULT_WORKSPACE_NAME not set in environment, using default value: %s", defaultWsName) + } + log.Printf("Using default workspace name: %s", defaultWsName) + targetWorkspace, err = s.workspaceRepo.FindWorkspaceByName(defaultWsName) + if err != nil { + if err.Error() == "workspace not found" { + // Default workspace doesn't exist, create it + log.Printf("Default workspace '%s' not found. Creating it...", defaultWsName) + newWorkspace := &model.Workspace{ + Name: defaultWsName, + Description: "Default workspace for automatically synced projects", + } + if err := s.workspaceRepo.CreateWorkspace(newWorkspace); err != nil { + log.Printf("Error creating default workspace '%s': %v", defaultWsName, err) + return fmt.Errorf("failed to create default workspace: %w", err) + } + log.Printf("Successfully created default workspace '%s'", defaultWsName) + targetWorkspace = newWorkspace + } else { + log.Printf("Error finding default workspace '%s': %v. Cannot assign projects.", defaultWsName, err) + return fmt.Errorf("failed to find or create default workspace: %w", err) } - log.Printf("Successfully created default workspace '%s'", defaultWsName) - defaultWs = newWorkspace - } else { - log.Printf("Error finding default workspace '%s': %v. Cannot assign projects.", defaultWsName, err) - return fmt.Errorf("failed to find or create default workspace: %w", err) } + targetWorkspaceID = targetWorkspace.ID } - if err := s.projectRepo.AddProjectWorkspaceAssociation(project.ID, defaultWs.ID); err != nil { - log.Printf("Error assigning project %d to default workspace %d: %v", project.ID, defaultWs.ID, err) - // Log a warning, but the project creation was successful. - return nil // Or return fmt.Errorf("failed to assign project to default workspace: %w", err) + + // 4. Assign project to target workspace + if err := s.projectRepo.AddProjectWorkspaceAssociation(project.ID, targetWorkspaceID); err != nil { + log.Printf("Error assigning project %d to workspace %d: %v", project.ID, targetWorkspaceID, err) + return nil // Project created but assignment failed } - log.Printf("Successfully assigned project %d to default workspace %d", project.ID, defaultWs.ID) + log.Printf("Successfully assigned project %d to workspace %d (%s)", project.ID, targetWorkspaceID, targetWorkspace.Name) - return nil // Project created and assigned (or assignment failed but logged) + return nil } // List 모든 프로젝트 조회 @@ -169,6 +180,18 @@ func (s *ProjectService) ListProjects(req *model.ProjectFilterRequest) ([]*model return s.projectRepo.FindProjects(req) } +// GetProjectWorkspaces 프로젝트에 할당된 workspace 목록 조회 +func (s *ProjectService) GetProjectWorkspaces(projectID uint) ([]*model.Workspace, error) { + // 프로젝트 존재 여부 확인 + _, err := s.projectRepo.FindProjectByProjectID(projectID) + if err != nil { + return nil, err + } + + // 할당된 workspace 목록 조회 + return s.projectRepo.FindAssignedWorkspaces(projectID) +} + // GetByID ID로 프로젝트 조회 func (s *ProjectService) GetProjectByID(id uint) (*model.Project, error) { return s.projectRepo.FindProjectByProjectID(id) From 232b82b5f385f92ffeb1e1b7b111a104148797e0 Mon Sep 17 00:00:00 2001 From: dogfootman Date: Fri, 14 Nov 2025 13:11:05 +0900 Subject: [PATCH 04/14] Add docker-compose configuration for development environment and enhance API documentation for project and workspace management. Introduced new endpoints for workspace retrieval and improved error handling in existing API definitions. --- docker-compose-dev.yaml | 84 +++++++++++++++++++++++++++++ src/docs/swagger.yaml | 114 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 docker-compose-dev.yaml diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml new file mode 100644 index 00000000..b2bc4a6c --- /dev/null +++ b/docker-compose-dev.yaml @@ -0,0 +1,84 @@ +version: '3.8' + +# Release 브랜치에서 이미 생성된 네트워크를 외부 네트워크로 참조 +# 네트워크 이름이 다를 경우 'docker network ls | grep mc-iam-manager'로 확인 후 수정 +networks: + mc-iam-manager-network: + external: true + name: mc-iam-manager_mc-iam-manager-network + mc-infra-manager-network: + external: true + name: mc-iam-manager_mc-infra-manager-network + mc-web-console-network: + external: true + name: mc-iam-manager_mc-web-console-network + +services: + # Dev 환경용 mc-iam-manager 소스 컨테이너 + mc-iam-manager-dev: + container_name: mc-iam-manager-dev + build: + context: . + dockerfile: Dockerfile.mciammanager + image: mc-iam-manager:dev + platform: linux/amd64 + restart: unless-stopped + networks: + - mc-iam-manager-network + - mc-infra-manager-network + - mc-web-console-network + ports: + - '5006:5006' // Dev 환경 전용 포트 + environment: + # Release 브랜치의 로컬 Docker 컨테이너를 참조하도록 override + # .env 파일의 원격 서버 설정을 로컬 Docker 컨테이너로 변경 + MC_IAM_MANAGER_DATABASE_HOST: mc-iam-manager-db + MC_IAM_MANAGER_DATABASE_NAME: ${MC_IAM_MANAGER_DATABASE_NAME} + MC_IAM_MANAGER_DATABASE_USER: ${MC_IAM_MANAGER_DATABASE_USER} + MC_IAM_MANAGER_DATABASE_PASSWORD: ${MC_IAM_MANAGER_DATABASE_PASSWORD} + MC_IAM_MANAGER_DATABASE_PORT: 5432 + MC_IAM_MANAGER_DATABASE_SSLMODE: disable + DATABASE_URL: postgres://${MC_IAM_MANAGER_DATABASE_USER}:${MC_IAM_MANAGER_DATABASE_PASSWORD}@mc-iam-manager-db:5432/${MC_IAM_MANAGER_DATABASE_NAME}?sslmode=disable + + # Keycloak 설정 - Release 브랜치의 로컬 Keycloak 컨테이너 사용 + MC_IAM_MANAGER_KEYCLOAK_HOST: http://mc-iam-manager-kc:8080 + MC_IAM_MANAGER_KEYCLOAK_DOMAIN: mc-iam-manager-kc + MC_IAM_MANAGER_KEYCLOAK_PORT: 8080 + MC_IAM_MANAGER_KEYCLOAK_REALM: ${MC_IAM_MANAGER_KEYCLOAK_REALM} + MC_IAM_MANAGER_KEYCLOAK_CLIENT_PATH: mc-iam-manager-kc/realms/${MC_IAM_MANAGER_KEYCLOAK_REALM} + MC_IAM_MANAGER_KEYCLOAK_ADMIN: ${MC_IAM_MANAGER_KEYCLOAK_ADMIN} + MC_IAM_MANAGER_KEYCLOAK_ADMIN_PASSWORD: ${MC_IAM_MANAGER_KEYCLOAK_ADMIN_PASSWORD} + MC_IAM_MANAGER_KEYCLOAK_DATABASE_NAME: ${MC_IAM_MANAGER_KEYCLOAK_DATABASE_NAME} + + # 애플리케이션 포트 설정 + PORT: 5006 + MC_IAM_MANAGER_PORT: 5006 + MC_IAM_MANAGER_DOMAIN: localhost + MC_IAM_MANAGER_HOST: http://localhost:5006 + + # 기타 필요한 환경 변수들 + DEFAULT_LANGUAGE: ${DEFAULT_LANGUAGE} + MODE: ${MODE} + USE_TICKET_VALID: ${USE_TICKET_VALID} + MC_IAM_MANAGER_PLATFORMADMIN_ID: ${MC_IAM_MANAGER_PLATFORMADMIN_ID} + MC_IAM_MANAGER_PLATFORMADMIN_PASSWORD: ${MC_IAM_MANAGER_PLATFORMADMIN_PASSWORD} + MC_IAM_MANAGER_PLATFORMADMIN_FIRSTNAME: ${MC_IAM_MANAGER_PLATFORMADMIN_FIRSTNAME} + MC_IAM_MANAGER_PLATFORMADMIN_LASTNAME: ${MC_IAM_MANAGER_PLATFORMADMIN_LASTNAME} + MC_IAM_MANAGER_PLATFORMADMIN_EMAIL: ${MC_IAM_MANAGER_PLATFORMADMIN_EMAIL} + + # MC-INFRA-MANAGER 설정 + MCINFRAMANAGER: ${MCINFRAMANAGER} + MCINFRAMANAGER_APIUSERNAME: ${MCINFRAMANAGER_APIUSERNAME} + MCINFRAMANAGER_APIPASSWORD: ${MCINFRAMANAGER_APIPASSWORD} + + env_file: + - ./.env + volumes: + - ./tool/mcc:/app/tool/mcc + healthcheck: + test: [ 'CMD', '/app/tool/mcc', 'rest', 'get', 'http://localhost:5006/readyz' ] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 3e08a9b2..573635a8 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -209,6 +209,18 @@ definitions: - menuIds - roleId type: object + model.CreateProjectRequest: + properties: + description: + type: string + name: + type: string + workspaceId: + description: optional workspace to assign project to + type: string + required: + - name + type: object model.CreateRoleRequest: properties: cspRoles: @@ -763,9 +775,33 @@ paths: - application/json description: Refresh JWT access token using a valid refresh token. operationId: mciamRefreshToken + parameters: + - description: Refresh token + in: body + name: refresh_token + required: true + schema: + type: string produces: - application/json - responses: {} + responses: + "200": + description: New token information + schema: + additionalProperties: true + type: object + "400": + description: 'error: Bad Request' + schema: + additionalProperties: + type: string + type: object + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object summary: Refresh access token tags: - auth @@ -792,6 +828,13 @@ paths: - application/json description: Validate the current access token and refresh if expired operationId: mciamValidateToken + parameters: + - description: Refresh token + in: body + name: refresh_token + required: true + schema: + type: string produces: - application/json responses: @@ -800,6 +843,12 @@ paths: schema: additionalProperties: true type: object + "400": + description: 'error: Bad Request' + schema: + additionalProperties: + type: string + type: object "401": description: 'error: Unauthorized' schema: @@ -1961,7 +2010,8 @@ paths: post: consumes: - application/json - description: Create a new project with the specified information. + description: Create a new project with the specified information. Optionally + specify a workspace to assign the project to. operationId: createProject parameters: - description: Project Info @@ -1969,7 +2019,7 @@ paths: name: project required: true schema: - $ref: '#/definitions/model.Project' + $ref: '#/definitions/model.CreateProjectRequest' produces: - application/json responses: @@ -1983,6 +2033,12 @@ paths: additionalProperties: type: string type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object "500": description: Internal Server Error schema: @@ -2155,6 +2211,50 @@ paths: summary: 프로젝트에 워크스페이스 연결 tags: - projects + /api/projects/id/{projectId}/workspaces: + get: + consumes: + - application/json + description: Retrieve list of workspaces that the project is assigned to + operationId: getProjectWorkspaces + parameters: + - description: Project ID + in: path + name: projectId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Workspace' + type: array + "400": + description: 'error: Invalid project ID' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Project not found' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Internal server error' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get workspaces assigned to project + tags: + - projects /api/projects/list: post: consumes: @@ -4528,7 +4628,8 @@ paths: post: consumes: - application/json - description: Create a new workspace with the specified information. + description: Create a new workspace with the specified information. Optionally + assign existing projects to the workspace. operationId: createWorkspace parameters: - description: Workspace Info @@ -5204,6 +5305,11 @@ paths: - application/json description: Check the health status of the service. operationId: mciamCheckHealth + parameters: + - description: Detail check components (nginx,db,keycloak,all) + in: query + name: detail + type: string produces: - application/json responses: From d7334601d83fdb7ec82d69a9c30a2e74298cc669 Mon Sep 17 00:00:00 2001 From: dogfootman Date: Fri, 14 Nov 2025 13:17:46 +0900 Subject: [PATCH 05/14] =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml index b2bc4a6c..56e50ed7 100644 --- a/docker-compose-dev.yaml +++ b/docker-compose-dev.yaml @@ -28,7 +28,7 @@ services: - mc-infra-manager-network - mc-web-console-network ports: - - '5006:5006' // Dev 환경 전용 포트 + - '5006:5006' # Dev 환경 전용 포트 environment: # Release 브랜치의 로컬 Docker 컨테이너를 참조하도록 override # .env 파일의 원격 서버 설정을 로컬 Docker 컨테이너로 변경 From ac8767b5d45e694c9937f88acea105efb02e832b Mon Sep 17 00:00:00 2001 From: dogfootman Date: Fri, 14 Nov 2025 13:30:11 +0900 Subject: [PATCH 06/14] =?UTF-8?q?docker=20dev=EC=9D=98=20=EC=99=B8?= =?UTF-8?q?=EB=B6=80=20network=20=EC=B0=B8=EC=A1=B0=20=EB=B3=80=EA=B2=BD.?= =?UTF-8?q?=20=20mcc=5Fxxx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose-dev.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml index 56e50ed7..2c8c4173 100644 --- a/docker-compose-dev.yaml +++ b/docker-compose-dev.yaml @@ -5,13 +5,10 @@ version: '3.8' networks: mc-iam-manager-network: external: true - name: mc-iam-manager_mc-iam-manager-network + name: mcc_mc-iam-manager-network mc-infra-manager-network: external: true - name: mc-iam-manager_mc-infra-manager-network - mc-web-console-network: - external: true - name: mc-iam-manager_mc-web-console-network + name: mcc_mc-infra-manager-network services: # Dev 환경용 mc-iam-manager 소스 컨테이너 @@ -26,7 +23,6 @@ services: networks: - mc-iam-manager-network - mc-infra-manager-network - - mc-web-console-network ports: - '5006:5006' # Dev 환경 전용 포트 environment: From f616b81ec2f28f39420f4971bb00ead1e4f55f26 Mon Sep 17 00:00:00 2001 From: dogfootman Date: Sun, 16 Nov 2025 16:40:06 +0900 Subject: [PATCH 07/14] Enhance Docker deployment documentation for mc-iam-manager service, including local build instructions, service dependencies, and image management commands. Update project and workspace services to utilize environment variable for default workspace name, improve error handling, and streamline project assignment logic. --- readme.md | 67 ++++++++++++ readme_kr.md | 67 ++++++++++++ src/handler/admin_handler.go | 8 +- src/repository/workspace_repository.go | 23 +--- src/service/project_service.go | 144 ++++++++++++++++--------- src/service/workspace_service.go | 65 +++-------- 6 files changed, 255 insertions(+), 119 deletions(-) diff --git a/readme.md b/readme.md index 2f8928d1..c5536134 100644 --- a/readme.md +++ b/readme.md @@ -127,6 +127,73 @@ cd ./src go run main.go ``` +### Docker Deployment with Local Build + +The `mc-iam-manager` service is configured to use the local `Dockerfile.mciammanager` for building the container image. + +#### Build Configuration + +In `docker-compose.yaml`, the service is configured as: + +```yaml +mc-iam-manager: + build: + context: . + dockerfile: Dockerfile.mciammanager + image: cloudbaristaorg/mc-iam-manager:edge +``` + +#### Deployment Options + +**1. Build and Run mc-iam-manager:** +```bash +# Build from local Dockerfile and start +docker-compose up --build mc-iam-manager + +# Run in background +docker-compose up --build -d mc-iam-manager +``` + +**2. Run All Services:** +```bash +# Build and start all services +docker-compose up --build -d +``` + +**3. Rebuild from Scratch:** +```bash +# Force rebuild without cache +docker-compose build --no-cache mc-iam-manager +docker-compose up -d mc-iam-manager +``` + +**4. Run with Dependencies Only:** +```bash +# Start mc-iam-manager with required services +docker-compose up -d mc-iam-manager-db mc-iam-manager-kc mc-iam-manager +``` + +#### Service Dependencies + +The `mc-iam-manager` service requires: +- `mc-iam-manager-db` (PostgreSQL database) +- `mc-iam-manager-kc` (Keycloak for authentication) + +These dependencies are automatically started when you run `mc-iam-manager`. + +#### Image Management + +```bash +# Pull latest images (if using pre-built images) +docker-compose pull + +# List Docker images +docker images | grep mc-iam-manager + +# Remove old images +docker rmi cloudbaristaorg/mc-iam-manager:edge +``` + #### Step 5: Operation Verification ```bash diff --git a/readme_kr.md b/readme_kr.md index 439c61d1..f9932b2e 100644 --- a/readme_kr.md +++ b/readme_kr.md @@ -128,6 +128,73 @@ cd ./src go run main.go ``` +### Docker 로컬 빌드 배포 + +`mc-iam-manager` 서비스는 로컬의 `Dockerfile.mciammanager`를 사용하여 컨테이너 이미지를 빌드하도록 구성되어 있습니다. + +#### 빌드 설정 + +`docker-compose.yaml`에서 다음과 같이 설정되어 있습니다: + +```yaml +mc-iam-manager: + build: + context: . + dockerfile: Dockerfile.mciammanager + image: cloudbaristaorg/mc-iam-manager:edge +``` + +#### 배포 방법 + +**1. mc-iam-manager 빌드 및 실행:** +```bash +# 로컬 Dockerfile로 빌드하고 시작 +docker-compose up --build mc-iam-manager + +# 백그라운드로 실행 +docker-compose up --build -d mc-iam-manager +``` + +**2. 전체 서비스 실행:** +```bash +# 모든 서비스 빌드 및 시작 +docker-compose up --build -d +``` + +**3. 완전 재빌드:** +```bash +# 캐시 없이 강제 재빌드 +docker-compose build --no-cache mc-iam-manager +docker-compose up -d mc-iam-manager +``` + +**4. 의존성 서비스와 함께 실행:** +```bash +# 필수 서비스와 함께 mc-iam-manager 시작 +docker-compose up -d mc-iam-manager-db mc-iam-manager-kc mc-iam-manager +``` + +#### 서비스 의존성 + +`mc-iam-manager` 서비스는 다음 서비스가 필요합니다: +- `mc-iam-manager-db` (PostgreSQL 데이터베이스) +- `mc-iam-manager-kc` (인증을 위한 Keycloak) + +`mc-iam-manager`를 실행하면 의존성 서비스가 자동으로 시작됩니다. + +#### 이미지 관리 + +```bash +# 최신 이미지 가져오기 (사전 빌드된 이미지 사용 시) +docker-compose pull + +# Docker 이미지 목록 확인 +docker images | grep mc-iam-manager + +# 이전 이미지 제거 +docker rmi cloudbaristaorg/mc-iam-manager:edge +``` + #### 5단계: 가동 확인 ```bash diff --git a/src/handler/admin_handler.go b/src/handler/admin_handler.go index 8db97988..947dd48e 100644 --- a/src/handler/admin_handler.go +++ b/src/handler/admin_handler.go @@ -144,8 +144,14 @@ func (h *AdminHandler) SetupInitialAdmin(c echo.Context) error { } // 기본 workspace 생성 + defaultWsName := os.Getenv("DEFAULT_WORKSPACE_NAME") + if defaultWsName == "" { + defaultWsName = "ws01" // fallback + log.Printf("[INFO] DEFAULT_WORKSPACE_NAME not set, using default: %s", defaultWsName) + } + err = h.workspaceService.CreateWorkspace(&model.Workspace{ - Name: "ws01", + Name: defaultWsName, Description: "Default Workspace", }) if err != nil { diff --git a/src/repository/workspace_repository.go b/src/repository/workspace_repository.go index accf7250..44446a77 100644 --- a/src/repository/workspace_repository.go +++ b/src/repository/workspace_repository.go @@ -206,9 +206,8 @@ func (r *WorkspaceRepository) AddProjectAssociation(workspaceID, projectID uint) // RemoveProjectAssociation remove project association from workspace func (r *WorkspaceRepository) RemoveProjectAssociation(workspaceID, projectID uint) error { - // Cannot remove from default workspace, and when removing connection from other workspaces, assign to default workspace - // Delete directly from mcmp_workspace_projects table + // 기본 workspace 포함 모든 workspace에서 제거 가능 result := r.db.Where("workspace_id = ? AND project_id = ?", workspaceID, projectID). Delete(&model.WorkspaceProject{}) @@ -216,24 +215,8 @@ func (r *WorkspaceRepository) RemoveProjectAssociation(workspaceID, projectID ui return result.Error } - workspaceProject := &model.WorkspaceProject{ - WorkspaceID: 1, // Default workspace ID - ProjectID: projectID, - } - - err := r.db.Save(workspaceProject).Error - if err != nil { - - return err - } - - // mcmp_workspace_projects 테이블에서 직접 삭제 - // result := r.db.Where("workspace_id = ? AND project_id = ?", workspaceID, projectID). - // Delete(&model.WorkspaceProject{}) - - // if result.Error != nil { - // return result.Error - // } + // 기본 workspace로 재할당하지 않음 + // 프로젝트가 미할당 상태가 될 수 있음 // Do not treat as error even if no records were deleted (relationship may not have existed) return nil diff --git a/src/service/project_service.go b/src/service/project_service.go index 248b3ef7..7ac39c26 100644 --- a/src/service/project_service.go +++ b/src/service/project_service.go @@ -6,9 +6,10 @@ import ( "errors" // "errors" // Remove unused import - "fmt" // Add fmt import for errors - "log" // Add log import - "os" // Import os package to read environment variables + "fmt" // Add fmt import for errors + "log" // Add log import + "os" // Import os package to read environment variables + "strings" // Add strings import for string operations // "net/http" // Remove unused import @@ -56,6 +57,51 @@ func (s *ProjectService) Create(ctx context.Context, project *model.Project, wor return err } + // Step 0: Determine target workspace (specified or default) and validate BEFORE calling mc-infra-manager + var targetWorkspace *model.Workspace + var targetWorkspaceID uint + + if len(workspaceID) > 0 && workspaceID[0] != 0 { + // Workspace specified, verify it exists + targetWorkspaceID = workspaceID[0] + targetWorkspace, err = s.workspaceRepo.FindWorkspaceByID(targetWorkspaceID) + if err != nil || targetWorkspace == nil { + log.Printf("Error finding specified workspace %d: %v", targetWorkspaceID, err) + return fmt.Errorf("workspace not found") + } + log.Printf("Validated specified workspace: %s (ID: %d)", targetWorkspace.Name, targetWorkspace.ID) + } else { + // No workspace specified, use default + defaultWsName := os.Getenv("DEFAULT_WORKSPACE_NAME") + if defaultWsName == "" { + defaultWsName = "default" + log.Printf("DEFAULT_WORKSPACE_NAME not set in environment, using default value: %s", defaultWsName) + } + log.Printf("Using default workspace name: %s", defaultWsName) + targetWorkspace, err = s.workspaceRepo.FindWorkspaceByName(defaultWsName) + if err != nil { + if err.Error() == "workspace not found" { + // Default workspace doesn't exist, create it + log.Printf("Default workspace '%s' not found. Creating it...", defaultWsName) + newWorkspace := &model.Workspace{ + Name: defaultWsName, + Description: "Default workspace for automatically synced projects", + } + if err := s.workspaceRepo.CreateWorkspace(newWorkspace); err != nil { + log.Printf("Error creating default workspace '%s': %v", defaultWsName, err) + return fmt.Errorf("failed to create default workspace: %w", err) + } + log.Printf("Successfully created default workspace '%s'", defaultWsName) + targetWorkspace = newWorkspace + } else { + log.Printf("Error finding default workspace '%s': %v. Cannot assign projects.", defaultWsName, err) + return fmt.Errorf("failed to find or create default workspace: %w", err) + } + } + targetWorkspaceID = targetWorkspace.ID + } + log.Printf("Workspace validation complete. Will assign project to workspace: %s (ID: %d)", targetWorkspace.Name, targetWorkspaceID) + log.Printf("Attempting to create namespace in mc-infra-manager for project: %s", project.Name) // Check if mcmpApiService is properly initialized @@ -121,51 +167,7 @@ func (s *ProjectService) Create(ctx context.Context, project *model.Project, wor return err } - // 3. Determine target workspace (specified or default) - var targetWorkspace *model.Workspace - var targetWorkspaceID uint - - if len(workspaceID) > 0 && workspaceID[0] != 0 { - // Workspace specified, verify it exists - targetWorkspaceID = workspaceID[0] - targetWorkspace, err = s.workspaceRepo.FindWorkspaceByID(targetWorkspaceID) - if err != nil || targetWorkspace == nil { - log.Printf("Error finding specified workspace %d: %v", targetWorkspaceID, err) - return fmt.Errorf("workspace not found") - } - log.Printf("Assigning project to specified workspace: %s (ID: %d)", targetWorkspace.Name, targetWorkspace.ID) - } else { - // No workspace specified, use default - defaultWsName := os.Getenv("DEFAULT_WORKSPACE_NAME") - if defaultWsName == "" { - defaultWsName = "default" - log.Printf("DEFAULT_WORKSPACE_NAME not set in environment, using default value: %s", defaultWsName) - } - log.Printf("Using default workspace name: %s", defaultWsName) - targetWorkspace, err = s.workspaceRepo.FindWorkspaceByName(defaultWsName) - if err != nil { - if err.Error() == "workspace not found" { - // Default workspace doesn't exist, create it - log.Printf("Default workspace '%s' not found. Creating it...", defaultWsName) - newWorkspace := &model.Workspace{ - Name: defaultWsName, - Description: "Default workspace for automatically synced projects", - } - if err := s.workspaceRepo.CreateWorkspace(newWorkspace); err != nil { - log.Printf("Error creating default workspace '%s': %v", defaultWsName, err) - return fmt.Errorf("failed to create default workspace: %w", err) - } - log.Printf("Successfully created default workspace '%s'", defaultWsName) - targetWorkspace = newWorkspace - } else { - log.Printf("Error finding default workspace '%s': %v. Cannot assign projects.", defaultWsName, err) - return fmt.Errorf("failed to find or create default workspace: %w", err) - } - } - targetWorkspaceID = targetWorkspace.ID - } - - // 4. Assign project to target workspace + // 3. Assign project to target workspace (already validated above) if err := s.projectRepo.AddProjectWorkspaceAssociation(project.ID, targetWorkspaceID); err != nil { log.Printf("Error assigning project %d to workspace %d: %v", project.ID, targetWorkspaceID, err) return nil // Project created but assignment failed @@ -214,10 +216,54 @@ func (s *ProjectService) UpdateProject(id uint, updates map[string]interface{}) // Delete 프로젝트 삭제 func (s *ProjectService) DeleteProject(id uint) error { - _, err := s.projectRepo.FindProjectByProjectID(id) + // 1. 프로젝트 존재 여부 확인 + project, err := s.projectRepo.FindProjectByProjectID(id) if err != nil { return err } + + // 2. 워크스페이스 할당 확인 + workspaces, err := s.projectRepo.FindAssignedWorkspaces(id) + if err != nil { + return fmt.Errorf("워크스페이스 할당 확인 실패: %v", err) + } + + // 3. 할당된 워크스페이스가 있으면 삭제 불가 + if len(workspaces) > 0 { + workspaceNames := make([]string, len(workspaces)) + for i, ws := range workspaces { + workspaceNames[i] = ws.Name + } + return fmt.Errorf("프로젝트가 워크스페이스에 할당되어 있습니다: %s. 먼저 모든 워크스페이스에서 할당을 해제하세요", + strings.Join(workspaceNames, ", ")) + } + + // 4. mc-infra-manager namespace 삭제 + if project.NsId != "" { + ctx := context.Background() + callReq := &model.McmpApiCallRequest{ + ServiceName: "mc-infra-manager", + ActionName: "DeleteNs", + RequestParams: model.McmpApiRequestParams{ + PathParams: map[string]string{ + "nsId": project.NsId, + }, + }, + } + + statusCode, respBody, _, _, err := s.mcmpApiService.McmpApiCall(ctx, callReq) + if err != nil { + log.Printf("Warning: failed to delete namespace %s from mc-infra-manager: %v", project.NsId, err) + // 계속 진행 (DB 정리는 수행) + } else if statusCode < 200 || statusCode >= 300 { + log.Printf("Warning: mc-infra-manager DeleteNs failed (status %d): %s", statusCode, string(respBody)) + // 계속 진행 (DB 정리는 수행) + } else { + log.Printf("Successfully deleted namespace %s from mc-infra-manager", project.NsId) + } + } + + // 5. DB에서 프로젝트 삭제 return s.projectRepo.DeleteProject(id) } diff --git a/src/service/workspace_service.go b/src/service/workspace_service.go index 78f63f24..c600f835 100644 --- a/src/service/workspace_service.go +++ b/src/service/workspace_service.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "os" + "strings" "github.com/m-cmp/mc-iam-manager/constants" "github.com/m-cmp/mc-iam-manager/model" @@ -73,17 +73,23 @@ func (s *WorkspaceService) UpdateWorkspace(workspace *model.Workspace) error { // Delete 워크스페이스 삭제 func (s *WorkspaceService) DeleteWorkspace(workspaceID uint) error { - // Check if workspace exists - workspace, err := s.workspaceRepo.FindWorkspaceProjectsByWorkspaceID(workspaceID) // 실제 존재하는 workspace인지 확인 + // 1. 워크스페이스 존재 확인 + workspace, err := s.workspaceRepo.FindWorkspaceProjectsByWorkspaceID(workspaceID) if err != nil { return err } - // 워크스페이스에 연결된 프로젝트가 있는지 확인 + // 2. 할당된 프로젝트 확인 if len(workspace.Projects) > 0 { - return errors.New("워크스페이스에 연결된 프로젝트가 있습니다") + projectNames := make([]string, len(workspace.Projects)) + for i, p := range workspace.Projects { + projectNames[i] = p.Name + } + return fmt.Errorf("워크스페이스에 연결된 프로젝트가 있습니다: %s. 먼저 모든 프로젝트를 제거하세요", + strings.Join(projectNames, ", ")) } + // 3. 삭제 실행 return s.workspaceRepo.DeleteWorkspace(workspaceID) } @@ -181,50 +187,11 @@ func (s *WorkspaceService) AddProjectToWorkspace(workspaceID, projectID uint) er // RemoveProjectFromWorkspace 워크스페이스에서 프로젝트 제거 func (s *WorkspaceService) RemoveProjectFromWorkspace(workspaceID, projectID uint) error { - // 프로젝트가 다른 워크스페이스에 할당되어 있는지 확인 - assignedWorkspaces, err := s.projectRepo.FindAssignedWorkspaces(projectID) - if err != nil { - return fmt.Errorf("워크스페이스 할당 정보를 가져오는데 실패했습니다: %v", err) - } - - // 현재 워크스페이스에서만 할당되어 있는 경우에만 기본 워크스페이스에 할당 - if len(assignedWorkspaces) == 1 && assignedWorkspaces[0].ID == workspaceID { - // 기본 워크스페이스 조회 - defaultWsName := os.Getenv("DEFAULT_WORKSPACE_NAME") - if defaultWsName == "" { - defaultWsName = "default" - } - defaultWs, err := s.workspaceRepo.FindWorkspaceByName(defaultWsName) - if err != nil { - if err.Error() == "workspace not found" { - // 기본 워크스페이스가 없으면 생성 - newWorkspace := &model.Workspace{ - Name: defaultWsName, - Description: "Default workspace for automatically synced projects", - } - if err := s.workspaceRepo.CreateWorkspace(newWorkspace); err != nil { - return fmt.Errorf("기본 워크스페이스 생성에 실패했습니다: %v", err) - } - defaultWs = newWorkspace - } else { - return fmt.Errorf("기본 워크스페이스를 찾는데 실패했습니다: %v", err) - } - } - - // 기존 워크스페이스에서 제거 - if err := s.workspaceRepo.RemoveProjectAssociation(workspaceID, projectID); err != nil { - return fmt.Errorf("워크스페이스 연결 제거에 실패했습니다: %v", err) - } - - // 기본 워크스페이스에 할당 - if err := s.workspaceRepo.AddProjectAssociation(defaultWs.ID, projectID); err != nil { - return fmt.Errorf("기본 워크스페이스 할당에 실패했습니다: %v", err) - } - } else { - // 다른 워크스페이스에도 할당되어 있는 경우 단순히 현재 워크스페이스에서만 제거 - if err := s.workspaceRepo.RemoveProjectAssociation(workspaceID, projectID); err != nil { - return fmt.Errorf("워크스페이스 연결 제거에 실패했습니다: %v", err) - } + // 워크스페이스에서 프로젝트 제거 + // 기본 workspace 포함 모든 workspace에서 제거 가능 + // 프로젝트는 미할당 상태가 될 수 있음 + if err := s.workspaceRepo.RemoveProjectAssociation(workspaceID, projectID); err != nil { + return fmt.Errorf("워크스페이스 연결 제거에 실패했습니다: %v", err) } return nil From 42f7d2359c59fbc18f226e3aca1ba7cc343d3cbf Mon Sep 17 00:00:00 2001 From: dogfootman Date: Mon, 17 Nov 2025 07:59:05 +0900 Subject: [PATCH 08/14] Refactor API documentation and handlers for permission-action mappings and project management. Removed deprecated endpoints, added new endpoints for workspace assignment and unassignment, and updated parameter definitions for clarity. Enhanced error handling and improved Swagger definitions for better API usability. --- docker-compose-dev.yaml | 11 +- src/docs/docs.go | 430 +++++++----------- src/docs/swagger.json | 430 +++++++----------- src/docs/swagger.yaml | 210 ++++----- ...mpapi_permission_action_mapping_handler.go | 64 +-- src/handler/project_handler.go | 12 +- src/handler/workspace_handler.go | 132 ------ 7 files changed, 439 insertions(+), 850 deletions(-) diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml index 2c8c4173..6bdc965a 100644 --- a/docker-compose-dev.yaml +++ b/docker-compose-dev.yaml @@ -5,10 +5,10 @@ version: '3.8' networks: mc-iam-manager-network: external: true - name: mcc_mc-iam-manager-network + name: mc-iam-manager_mc-iam-manager-network mc-infra-manager-network: external: true - name: mcc_mc-infra-manager-network + name: mc-iam-manager_mc-infra-manager-network services: # Dev 환경용 mc-iam-manager 소스 컨테이너 @@ -34,6 +34,7 @@ services: MC_IAM_MANAGER_DATABASE_PASSWORD: ${MC_IAM_MANAGER_DATABASE_PASSWORD} MC_IAM_MANAGER_DATABASE_PORT: 5432 MC_IAM_MANAGER_DATABASE_SSLMODE: disable + MC_IAM_MANAGER_DATABASE_URL: postgres://${MC_IAM_MANAGER_DATABASE_USER}:${MC_IAM_MANAGER_DATABASE_PASSWORD}@mc-iam-manager-db:5432/${MC_IAM_MANAGER_DATABASE_NAME}?sslmode=disable DATABASE_URL: postgres://${MC_IAM_MANAGER_DATABASE_USER}:${MC_IAM_MANAGER_DATABASE_PASSWORD}@mc-iam-manager-db:5432/${MC_IAM_MANAGER_DATABASE_NAME}?sslmode=disable # Keycloak 설정 - Release 브랜치의 로컬 Keycloak 컨테이너 사용 @@ -49,8 +50,8 @@ services: # 애플리케이션 포트 설정 PORT: 5006 MC_IAM_MANAGER_PORT: 5006 - MC_IAM_MANAGER_DOMAIN: localhost - MC_IAM_MANAGER_HOST: http://localhost:5006 + MC_IAM_MANAGER_DOMAIN: mciambase.onecloudcon.com + MC_IAM_MANAGER_HOST: http://mciambase.onecloudcon.com:5006 # 기타 필요한 환경 변수들 DEFAULT_LANGUAGE: ${DEFAULT_LANGUAGE} @@ -72,7 +73,7 @@ services: volumes: - ./tool/mcc:/app/tool/mcc healthcheck: - test: [ 'CMD', '/app/tool/mcc', 'rest', 'get', 'http://localhost:5006/readyz' ] + test: [ 'CMD', '/app/tool/mcc', 'rest', 'get', 'http://mciambase.onecloudcon.com:5006/readyz' ] interval: 30s timeout: 10s retries: 5 diff --git a/src/docs/docs.go b/src/docs/docs.go index d982074a..7009f4f4 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -695,42 +695,6 @@ const docTemplate = `{ } } }, - "/api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions": { - "get": { - "description": "Returns all platform actions mapped to a specific permission", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "mcmp-api-permission-action-mappings" - ], - "summary": "Get platform actions by permission ID", - "operationId": "getPlatformActionsByPermissionID", - "parameters": [ - { - "type": "string", - "description": "Permission ID", - "name": "permissionId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/mcmpapi.McmpApiAction" - } - } - } - } - } - }, "/api/mcmp-apis/list": { "post": { "security": [ @@ -1005,6 +969,42 @@ const docTemplate = `{ } } }, + "/api/mcmp-apis/permission-action-mappings/id/{id}": { + "get": { + "description": "Returns all platform actions mapped to a specific permission", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mcmp-api-permission-action-mappings" + ], + "summary": "Get platform actions by permission ID", + "operationId": "getPlatformActionsByPermissionID", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" + } + } + } + } + } + }, "/api/mcmp-apis/syncMcmpAPIs": { "post": { "security": [ @@ -2069,6 +2069,70 @@ const docTemplate = `{ } } }, + "/api/projects/assign/workspaces": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "프로젝트에 워크스페이스를 연결합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: 잘못된 ID 형식", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/id/{projectId}/workspaces": { "get": { "security": [ @@ -2234,6 +2298,61 @@ const docTemplate = `{ } } }, + "/api/projects/unassign/workspaces": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a workspace from a project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Remove workspace from project", + "operationId": "removeWorkspaceFromProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: Invalid request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/{id}": { "get": { "security": [ @@ -2413,75 +2532,6 @@ const docTemplate = `{ } } }, - "/api/projects/{id}/workspaces/{workspaceId}": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "프로젝트에 워크스페이스를 연결합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "operationId": "addWorkspaceToProject", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/api/resource-types/cloud-resources": { "post": { "security": [ @@ -6952,158 +7002,6 @@ const docTemplate = `{ } } }, - "/api/workspaces/{id}/users": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Add a user to a workspace", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "workspaces" - ], - "summary": "Add user to workspace", - "operationId": "addUserToWorkspace", - "parameters": [ - { - "type": "string", - "description": "Workspace ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "User Info", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.AssignRoleRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/workspaces/{id}/users/{userId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Remove a user from a workspace", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "workspaces" - ], - "summary": "Remove user from workspace", - "operationId": "removeUserFromWorkspace", - "parameters": [ - { - "type": "string", - "description": "Workspace ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/readyz": { "get": { "description": "Check the health status of the service.", @@ -8146,6 +8044,24 @@ const docTemplate = `{ } } }, + "model.WorkspaceProjectMappingRequest": { + "type": "object", + "required": [ + "projectIds", + "workspaceId" + ], + "properties": { + "projectIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "workspaceId": { + "type": "string" + } + } + }, "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 23b1d808..6d890308 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -689,42 +689,6 @@ } } }, - "/api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions": { - "get": { - "description": "Returns all platform actions mapped to a specific permission", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "mcmp-api-permission-action-mappings" - ], - "summary": "Get platform actions by permission ID", - "operationId": "getPlatformActionsByPermissionID", - "parameters": [ - { - "type": "string", - "description": "Permission ID", - "name": "permissionId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/mcmpapi.McmpApiAction" - } - } - } - } - } - }, "/api/mcmp-apis/list": { "post": { "security": [ @@ -999,6 +963,42 @@ } } }, + "/api/mcmp-apis/permission-action-mappings/id/{id}": { + "get": { + "description": "Returns all platform actions mapped to a specific permission", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mcmp-api-permission-action-mappings" + ], + "summary": "Get platform actions by permission ID", + "operationId": "getPlatformActionsByPermissionID", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" + } + } + } + } + } + }, "/api/mcmp-apis/syncMcmpAPIs": { "post": { "security": [ @@ -2063,6 +2063,70 @@ } } }, + "/api/projects/assign/workspaces": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "프로젝트에 워크스페이스를 연결합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: 잘못된 ID 형식", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/id/{projectId}/workspaces": { "get": { "security": [ @@ -2228,6 +2292,61 @@ } } }, + "/api/projects/unassign/workspaces": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a workspace from a project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Remove workspace from project", + "operationId": "removeWorkspaceFromProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: Invalid request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/{id}": { "get": { "security": [ @@ -2407,75 +2526,6 @@ } } }, - "/api/projects/{id}/workspaces/{workspaceId}": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "프로젝트에 워크스페이스를 연결합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "operationId": "addWorkspaceToProject", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/api/resource-types/cloud-resources": { "post": { "security": [ @@ -6946,158 +6996,6 @@ } } }, - "/api/workspaces/{id}/users": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Add a user to a workspace", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "workspaces" - ], - "summary": "Add user to workspace", - "operationId": "addUserToWorkspace", - "parameters": [ - { - "type": "string", - "description": "Workspace ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "User Info", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.AssignRoleRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/workspaces/{id}/users/{userId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Remove a user from a workspace", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "workspaces" - ], - "summary": "Remove user from workspace", - "operationId": "removeUserFromWorkspace", - "parameters": [ - { - "type": "string", - "description": "Workspace ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/readyz": { "get": { "description": "Check the health status of the service.", @@ -8140,6 +8038,24 @@ } } }, + "model.WorkspaceProjectMappingRequest": { + "type": "object", + "required": [ + "projectIds", + "workspaceId" + ], + "properties": { + "projectIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "workspaceId": { + "type": "string" + } + } + }, "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 573635a8..4c6f0c4b 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -678,6 +678,18 @@ definitions: updated_at: type: string type: object + model.WorkspaceProjectMappingRequest: + properties: + projectIds: + items: + type: string + type: array + workspaceId: + type: string + required: + - projectIds + - workspaceId + type: object model.WorkspaceWithUsersAndRoles: properties: created_at: @@ -1153,30 +1165,6 @@ paths: summary: Update permission-action mapping tags: - mcmp-api-permission-action-mappings - /api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions: - get: - consumes: - - application/json - description: Returns all platform actions mapped to a specific permission - operationId: getPlatformActionsByPermissionID - parameters: - - description: Permission ID - in: path - name: permissionId - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/mcmpapi.McmpApiAction' - type: array - summary: Get platform actions by permission ID - tags: - - mcmp-api-permission-action-mappings /api/mcmp-apis/list: post: consumes: @@ -1359,6 +1347,30 @@ paths: summary: Set Active Version for a Service tags: - McmpAPI + /api/mcmp-apis/permission-action-mappings/id/{id}: + get: + consumes: + - application/json + description: Returns all platform actions mapped to a specific permission + operationId: getPlatformActionsByPermissionID + parameters: + - description: Permission ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/mcmpapi.McmpApiAction' + type: array + summary: Get platform actions by permission ID + tags: + - mcmp-api-permission-action-mappings /api/mcmp-apis/syncMcmpAPIs: post: consumes: @@ -2166,23 +2178,19 @@ paths: summary: Update project tags: - projects - /api/projects/{id}/workspaces/{workspaceId}: + /api/projects/assign/workspaces: post: consumes: - application/json description: 프로젝트에 워크스페이스를 연결합니다. operationId: addWorkspaceToProject parameters: - - description: 프로젝트 ID - in: path - name: id - required: true - type: integer - - description: 워크스페이스 ID - in: path - name: workspaceId + - description: Workspace and Project IDs + in: body + name: request required: true - type: integer + schema: + $ref: '#/definitions/model.WorkspaceProjectMappingRequest' produces: - application/json responses: @@ -2317,6 +2325,41 @@ paths: summary: Get project by name tags: - projects + /api/projects/unassign/workspaces: + delete: + consumes: + - application/json + description: Remove a workspace from a project + operationId: removeWorkspaceFromProject + parameters: + - description: Workspace and Project IDs + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.WorkspaceProjectMappingRequest' + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: 'error: Invalid request' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Internal server error' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Remove workspace from project + tags: + - projects /api/resource-types/cloud-resources: post: consumes: @@ -4662,105 +4705,6 @@ paths: summary: Create new workspace tags: - workspaces - /api/workspaces/{id}/users: - post: - consumes: - - application/json - description: Add a user to a workspace - operationId: addUserToWorkspace - parameters: - - description: Workspace ID - in: path - name: id - required: true - type: string - - description: User Info - in: body - name: request - required: true - schema: - $ref: '#/definitions/model.AssignRoleRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - additionalProperties: - type: string - type: object - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Add user to workspace - tags: - - workspaces - /api/workspaces/{id}/users/{userId}: - delete: - consumes: - - application/json - description: Remove a user from a workspace - operationId: removeUserFromWorkspace - parameters: - - description: Workspace ID - in: path - name: id - required: true - type: string - - description: User ID - in: path - name: userId - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - additionalProperties: - type: string - type: object - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Remove user from workspace - tags: - - workspaces /api/workspaces/assign/projects: post: consumes: diff --git a/src/handler/mcmpapi_permission_action_mapping_handler.go b/src/handler/mcmpapi_permission_action_mapping_handler.go index 6d59d09e..ef86254f 100644 --- a/src/handler/mcmpapi_permission_action_mapping_handler.go +++ b/src/handler/mcmpapi_permission_action_mapping_handler.go @@ -46,9 +46,9 @@ func (h *McmpApiPermissionActionMappingHandler) ListPlatformActions(c echo.Conte // @Tags mcmp-api-permission-action-mappings // @Accept json // @Produce json -// @Param permissionId path string true "Permission ID" +// @Param id path string true "Permission ID" // @Success 200 {array} mcmpapi.McmpApiAction -// @Router /api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions [get] +// @Router /api/mcmp-apis/permission-action-mappings/id/{id} [get] // @Id getPlatformActionsByPermissionID func (h *McmpApiPermissionActionMappingHandler) GetPlatformActionsByPermissionID(c echo.Context) error { permissionID := c.Param("permission_id") @@ -226,63 +226,3 @@ func (h *McmpApiPermissionActionMappingHandler) UpdateMapping(c echo.Context) er return c.JSON(http.StatusOK, map[string]string{"message": "mapping updated successfully"}) } - -// ListMappings godoc ListPlatformActions -// @Summary MCMP API 권한-액션 매핑 목록 조회 -// @Description 모든 MCMP API 권한-액션 매핑 목록을 조회합니다 -// @Tags mcmp-api-permission-action-mappings -// @Accept json -// @Produce json -// @Success 200 {array} mcmpapi.MCMPAPIPermissionActionMapping -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Security BearerAuth -// @Router /api/mcmp-api-permission-action-mappings [get] -// @Id listMappings - -// GetMappingByID godoc -// @Summary MCMP API 권한-액션 매핑 ID로 조회 -// @Description 특정 MCMP API 권한-액션 매핑을 ID로 조회합니다 -// @Tags mcmp-api-permission-action-mappings -// @Accept json -// @Produce json -// @Param id path string true "Mapping ID" -// @Success 200 {object} mcmpapi.MCMPAPIPermissionActionMapping -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Failure 404 {object} map[string]string "error: Mapping not found" -// @Security BearerAuth -// @Router /api/mcmp-api-permission-action-mappings/{id} [get] -// @Id getMappingByID - -// UpdateMapping godoc UpdateMapping -// @Summary MCMP API 권한-액션 매핑 업데이트 -// @Description MCMP API 권한-액션 매핑 정보를 업데이트합니다 -// @Tags mcmp-api-permission-action-mappings -// @Accept json -// @Produce json -// @Param id path string true "Mapping ID" -// @Param mapping body mcmpapi.MCMPAPIPermissionActionMapping true "Mapping Info" -// @Success 200 {object} mcmpapi.MCMPAPIPermissionActionMapping -// @Failure 400 {object} map[string]string "error: Invalid request" -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Failure 404 {object} map[string]string "error: Mapping not found" -// @Security BearerAuth -// @Router /api/mcmp-api-permission-action-mappings/{id} [put] -// @Id updateMapping - -// DeleteMapping godoc -// @Summary MCMP API 권한-액션 매핑 삭제 -// @Description MCMP API 권한-액션 매핑을 삭제합니다 -// @Tags mcmp-api-permission-action-mappings -// @Accept json -// @Produce json -// @Param id path string true "Mapping ID" -// @Success 204 "No Content" -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Failure 404 {object} map[string]string "error: Mapping not found" -// @Security BearerAuth -// @Router /api/mcmp-api-permission-action-mappings/{id} [delete] -// @Id deleteMapping diff --git a/src/handler/project_handler.go b/src/handler/project_handler.go index a4e76d92..29108b27 100644 --- a/src/handler/project_handler.go +++ b/src/handler/project_handler.go @@ -313,14 +313,13 @@ func (h *ProjectHandler) SyncProjects(c echo.Context) error { // @Tags projects // @Accept json // @Produce json -// @Param id path int true "프로젝트 ID" -// @Param workspaceId path int true "워크스페이스 ID" +// @Param request body model.WorkspaceProjectMappingRequest true "Workspace and Project IDs" // @Success 204 "No Content" // @Failure 400 {object} map[string]string "error: 잘못된 ID 형식" // @Failure 404 {object} map[string]string "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다" // @Failure 500 {object} map[string]string "error: 서버 내부 오류" // @Security BearerAuth -// @Router /api/projects/{id}/workspaces/{workspaceId} [post] +// @Router /api/projects/assign/workspaces [post] // @Id addWorkspaceToProject func (h *ProjectHandler) AddWorkspaceToProject(c echo.Context) error { var req model.WorkspaceProjectMappingRequest @@ -364,7 +363,12 @@ func (h *ProjectHandler) AddWorkspaceToProject(c echo.Context) error { // @Tags projects // @Accept json // @Produce json -// @Param id path string true "Project ID" +// @Param request body model.WorkspaceProjectMappingRequest true "Workspace and Project IDs" +// @Success 204 "No Content" +// @Failure 400 {object} map[string]string "error: Invalid request" +// @Failure 500 {object} map[string]string "error: Internal server error" +// @Security BearerAuth +// @Router /api/projects/unassign/workspaces [delete] // @Id removeWorkspaceFromProject func (h *ProjectHandler) RemoveWorkspaceFromProject(c echo.Context) error { var req model.WorkspaceProjectMappingRequest diff --git a/src/handler/workspace_handler.go b/src/handler/workspace_handler.go index bceec20f..d2dbfbad 100644 --- a/src/handler/workspace_handler.go +++ b/src/handler/workspace_handler.go @@ -573,138 +573,6 @@ func (h *WorkspaceHandler) ListWorkspaceUsersAndRoles(c echo.Context) error { return c.JSON(http.StatusOK, workspaceUsersRoles) } -// GetWorkspaceRoles 워크스페이스의 역할 목록 조회 -func (h *WorkspaceHandler) ListWorkspaceRoles(c echo.Context) error { - var req model.RoleFilterRequest - if err := c.Bind(&req); err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) - } - - // workspace 역할만 조회 - if req.RoleTypes == nil { - req.RoleTypes = []constants.IAMRoleType{constants.RoleTypeWorkspace} - } - - roles, err := h.roleService.ListWorkspaceRoles(&req) - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) - } - return c.JSON(http.StatusOK, roles) -} - -// ListWorkspaceUsers godoc -// @Summary Get workspace users -// @Description Get users in a workspace -// @Tags workspaces -// @Accept json -// @Produce json -// @Param id path string true "Workspace ID" -// @Success 200 {array} model.User -// @Failure 404 {object} map[string]string -// @Failure 500 {object} map[string]string -// @Security BearerAuth -// @Router /api/workspaces/{id}/users [get] -// @Id ListWorkspaceUsers -// func (h *WorkspaceHandler) ListWorkspaceUsers(c echo.Context) error { -// var req model.WorkspaceFilterRequest -// if err := c.Bind(&req); err != nil { -// return c.JSON(http.StatusBadRequest, map[string]string{"error": "잘못된 요청 형식입니다"}) -// } - -// workspaceUsers, err := h.workspaceService.ListWorkspaces(&req) -// if err != nil { -// if err.Error() == "workspace not found" { -// return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) -// } -// return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("사용자 목록 조회 실패: %v", err)}) -// } - -// return c.JSON(http.StatusOK, workspaceUsers) -// } - -// AddUserToWorkspace godoc -// @Summary Add user to workspace -// @Description Add a user to a workspace -// @Tags workspaces -// @Accept json -// @Produce json -// @Param id path string true "Workspace ID" -// @Param request body model.AssignRoleRequest true "User Info" -// @Success 200 {object} map[string]string -// @Failure 400 {object} map[string]string -// @Failure 404 {object} map[string]string -// @Failure 500 {object} map[string]string -// @Security BearerAuth -// @Router /api/workspaces/{id}/users [post] -// @Id addUserToWorkspace -func (h *WorkspaceHandler) AddUserToWorkspace(c echo.Context) error { - var req model.AssignRoleRequest - if err := c.Bind(&req); err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) - } - if req.WorkspaceID == "" || req.UserID == "" { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Workspace ID and User ID are required"}) - } - - // 워크스페이스 역할 할당 - var workspaceID uint - var userID uint - var err error - workspaceID, err = util.StringToUint(req.WorkspaceID) - if err != nil { - log.Printf("Workspace ID conversion error: %v", err) - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid workspace ID format"}) - } - if req.UserID != "" { - userID, err = util.StringToUint(req.UserID) - if err != nil { - log.Printf("User ID conversion error: %v", err) - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid user ID format"}) - } - } - - if err := h.workspaceService.AddUserToWorkspace(workspaceID, userID); err != nil { - if err.Error() == "workspace not found" || err.Error() == "user not found" { - return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) - } - return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to add user: %v", err)}) - } - return c.NoContent(http.StatusNoContent) -} - -// RemoveUserFromWorkspace godoc -// @Summary Remove user from workspace -// @Description Remove a user from a workspace -// @Tags workspaces -// @Accept json -// @Produce json -// @Param id path string true "Workspace ID" -// @Param userId path string true "User ID" -// @Success 200 {object} map[string]string -// @Failure 400 {object} map[string]string -// @Failure 404 {object} map[string]string -// @Failure 500 {object} map[string]string -// @Security BearerAuth -// @Router /api/workspaces/{id}/users/{userId} [delete] -// @Id removeUserFromWorkspace -func (h *WorkspaceHandler) RemoveUserFromWorkspace(c echo.Context) error { - workspaceID, err := strconv.ParseUint(c.Param("id"), 10, 32) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid workspace ID"}) - } - - userID, err := strconv.ParseUint(c.Param("userId"), 10, 32) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid user ID"}) - } - - if err := h.workspaceService.RemoveUserFromWorkspace(uint(workspaceID), uint(userID)); err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) - } - - return c.NoContent(http.StatusNoContent) -} - // Helper function to get user DB ID and Platform Roles from context // TODO: Move this to a shared location or middleware func getUserDbIdAndPlatformRoles(ctx context.Context, c echo.Context, userService *service.UserService) (uint, []*model.RoleMaster, error) { From e3f09da57757ad0392b59490447f0ac58708d445 Mon Sep 17 00:00:00 2001 From: MZC-CSC <78469943+MZC-CSC@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:22:24 +0900 Subject: [PATCH 09/14] Update continuous-delivery.yaml locally mzccsta --- .github/workflows/continuous-delivery.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-delivery.yaml b/.github/workflows/continuous-delivery.yaml index f3bfdbf4..980fefb8 100644 --- a/.github/workflows/continuous-delivery.yaml +++ b/.github/workflows/continuous-delivery.yaml @@ -25,7 +25,7 @@ on: - "CODEOWNERS" env: - DOCKER_REGISTRY_NAME: cloudbaristaorg + DOCKER_REGISTRY_NAME: mzccsta GHCR_REGISTRY_NAME: ${{ github.repository_owner }} IMAGE_NAME: ${{ github.event.repository.name }} From 77092af6913a89b386045af7b1c19496610dcd6d Mon Sep 17 00:00:00 2001 From: dogfootman Date: Wed, 19 Nov 2025 10:32:26 +0900 Subject: [PATCH 10/14] Revert "Update continuous-delivery.yaml locally mzccsta" This reverts commit e3f09da57757ad0392b59490447f0ac58708d445. --- .github/workflows/continuous-delivery.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-delivery.yaml b/.github/workflows/continuous-delivery.yaml index 980fefb8..f3bfdbf4 100644 --- a/.github/workflows/continuous-delivery.yaml +++ b/.github/workflows/continuous-delivery.yaml @@ -25,7 +25,7 @@ on: - "CODEOWNERS" env: - DOCKER_REGISTRY_NAME: mzccsta + DOCKER_REGISTRY_NAME: cloudbaristaorg GHCR_REGISTRY_NAME: ${{ github.repository_owner }} IMAGE_NAME: ${{ github.event.repository.name }} From fa847e2d906b1035c82be7510b6453068afda99c Mon Sep 17 00:00:00 2001 From: dogfootman Date: Mon, 12 Jan 2026 15:01:59 +0900 Subject: [PATCH 11/14] add features csp account, idp config management --- asset/mcmpapi/frameworks.yaml | 84 + asset/mcmpapi/service-actions.yaml | 1121 +++ conf/mc-iam-manager/frameworks.yaml | 84 + conf/mc-iam-manager/menu.yaml | 8 + conf/mc-iam-manager/service-actions.yaml | 1121 +++ docs/docs.go | 7156 ++++++++++++++--- docs/swagger.json | 101 +- docs/swagger.yaml | 70 +- src/csp/interface.go | 106 + src/docs/docs.go | 3096 ++++++- src/docs/swagger.json | 3096 ++++++- src/docs/swagger.yaml | 1865 ++++- src/go.sum | 23 +- src/handler/auth_handler.go | 26 - src/handler/csp_account_handler.go | 274 + src/handler/csp_idp_config_handler.go | 281 + src/handler/csp_policy_handler.go | 356 + src/handler/mcmpapi_handler.go | 164 +- ...mpapi_permission_action_mapping_handler.go | 5 +- src/handler/role_handler.go | 2 +- src/handler/workspace_handler.go | 140 +- src/main.go | 55 + src/model/csp_account.go | 110 + src/model/csp_idp_config.go | 274 + src/model/csp_policy.go | 152 + src/model/csp_role.go | 18 +- src/model/mcmpapi/mcmpapi_service_meta.go | 18 + src/model/request.go | 31 + src/repository/csp_account_repository.go | 150 + src/repository/csp_idp_config_repository.go | 180 + src/repository/csp_policy_repository.go | 262 + src/repository/mcmpapi_repository.go | 48 + src/service/csp_account_service.go | 257 + src/service/csp_idp_config_service.go | 505 ++ src/service/csp_policy_service.go | 470 ++ src/service/mcmpapi_service.go | 423 +- 36 files changed, 19460 insertions(+), 2672 deletions(-) create mode 100644 asset/mcmpapi/frameworks.yaml create mode 100644 asset/mcmpapi/service-actions.yaml create mode 100644 conf/mc-iam-manager/frameworks.yaml create mode 100644 conf/mc-iam-manager/service-actions.yaml create mode 100644 src/handler/csp_account_handler.go create mode 100644 src/handler/csp_idp_config_handler.go create mode 100644 src/handler/csp_policy_handler.go create mode 100644 src/model/csp_account.go create mode 100644 src/model/csp_idp_config.go create mode 100644 src/model/csp_policy.go create mode 100644 src/model/mcmpapi/mcmpapi_service_meta.go create mode 100644 src/repository/csp_account_repository.go create mode 100644 src/repository/csp_idp_config_repository.go create mode 100644 src/repository/csp_policy_repository.go create mode 100644 src/service/csp_account_service.go create mode 100644 src/service/csp_idp_config_service.go create mode 100644 src/service/csp_policy_service.go diff --git a/asset/mcmpapi/frameworks.yaml b/asset/mcmpapi/frameworks.yaml new file mode 100644 index 00000000..32d2c645 --- /dev/null +++ b/asset/mcmpapi/frameworks.yaml @@ -0,0 +1,84 @@ +# Swagger-to-Actions Framework Configuration +# This file defines multiple frameworks and their Swagger specifications +# to be aggregated into a single serviceActions YAML file. +# +# Version Management: +# - Each framework has a 'version' field to track the active API version +# - The output file includes '_meta' with version, repository, and generatedAt +# - mc-iam-manager can load this file at init and manage version tracking in DB + +# Output file path (relative to this config file) +output: ./service-actions.yaml + +# HTTP timeout for fetching remote Swagger specs (in seconds) +timeout: 30 + +# Enable verbose output +verbose: false + +# List of frameworks to process +frameworks: + # MC-IAM-Manager - Identity and Access Management + - name: mc-iam-manager + version: "0.3.0" # Active version (local development) + repository: https://github.com/m-cmp/mc-iam-manager + swagger: ../../docs/swagger.yaml # Local path relative to this file + + # MC-Observability - Monitoring and Observability + # Note: Swagger file path needs to be verified - currently not found in v0.5.0 + # - name: mc-observability + # version: "0.5.0" # Latest release: v0.5.0 (Nov 3, 2025) + # repository: https://github.com/m-cmp/mc-observability + # swagger: https://raw.githubusercontent.com/m-cmp/mc-observability/v0.5.0/swagger/swagger.yaml + + # MC-Application-Manager - Application Deployment Management + - name: mc-application-manager + version: "0.5.0" # Latest release: v0.5.0 + repository: https://github.com/m-cmp/mc-application-manager + swagger: https://raw.githubusercontent.com/m-cmp/mc-application-manager/v0.5.0/swagger.json + + # MC-Cost-Optimizer - Cost Optimization + - name: mc-cost-optimizer + version: "0.5.0" # Latest release: v0.5.0 + repository: https://github.com/m-cmp/mc-cost-optimizer + swagger: https://raw.githubusercontent.com/m-cmp/mc-cost-optimizer/v0.5.0/swagger.yaml + + # MC-Workflow-Manager - Workflow Orchestration + - name: mc-workflow-manager + version: "0.5.0" # Latest release: v0.5.0 + repository: https://github.com/m-cmp/mc-workflow-manager + swagger: https://raw.githubusercontent.com/m-cmp/mc-workflow-manager/v0.5.0/swagger.json + + # MC-Infra-Manager - Multi-Cloud Infrastructure Management + # Note: No releases found - using main branch + # - name: mc-infra-manager + # version: "main" # No releases, using main branch + # repository: https://github.com/m-cmp/mc-infra-manager + # swagger: https://raw.githubusercontent.com/m-cmp/mc-infra-manager/main/swagger.yaml + + # MC-Infra-Connector - Cloud Infrastructure Connection + # Note: No releases found - using main branch + # - name: mc-infra-connector + # version: "main" # No releases, using main branch + # repository: https://github.com/m-cmp/mc-infra-connector + # swagger: https://raw.githubusercontent.com/m-cmp/mc-infra-connector/main/swagger.yaml + + # MC-Data-Manager - Data Management + # Note: No releases found - using main branch + # - name: mc-data-manager + # version: "main" # No releases, using main branch + # repository: https://github.com/m-cmp/mc-data-manager + # swagger: https://raw.githubusercontent.com/m-cmp/mc-data-manager/main/swagger.yaml + + # MC-Across-Service-Manager - Cross-Service Management + # Note: Latest release is v0.1.0, not v0.5.0 - Swagger file path needs verification + # - name: mc-across-service-manager + # version: "0.1.0" # Latest release: v0.1.0 (not v0.5.0) + # repository: https://github.com/m-cmp/mc-across-service-manager + # swagger: https://raw.githubusercontent.com/m-cmp/mc-across-service-manager/v0.1.0/swagger.yaml + + # MC-Web-Console - Web Console Interface + # - name: mc-web-console + # version: "0.1.0" + # repository: https://github.com/m-cmp/mc-web-console + # swagger: /path/to/swagger.yaml diff --git a/asset/mcmpapi/service-actions.yaml b/asset/mcmpapi/service-actions.yaml new file mode 100644 index 00000000..ee2c0966 --- /dev/null +++ b/asset/mcmpapi/service-actions.yaml @@ -0,0 +1,1121 @@ +serviceActions: + mc-application-manager: + _meta: + version: 0.5.0 + repository: https://github.com/m-cmp/mc-application-manager + generatedAt: "2026-01-06T07:17:34Z" + checkConnectionUsingPOST: + method: post + resourcePath: /oss/connection-check + description: checkConnection + createCatalogRefUsingPOST: + method: post + resourcePath: /catalog/software/ref/{catalogIdx} + description: software catalog 관련정보 등록(webpage, workflow 등) + createCatalogUsingPOST: + method: post + resourcePath: /catalog/software/ + description: software catalog 등록 + createComponentByTextUsingPOST: + method: post + resourcePath: /oss/v1/components/{module}/create/{name}/text + description: createComponentByText + createComponentUsingPOST: + method: post + resourcePath: /oss/v1/components/{module}/create/{name} + description: createComponent + createManifestUsingPOST: + method: post + resourcePath: /manifest/ + description: createManifest + createRepositoryUsingPOST: + method: post + resourcePath: /oss/v1/repositories/{module}/create + description: createRepository + createRepositoryUsingPOST_1: + method: post + resourcePath: /repository/ + description: createRepository + deleteCatalogRefWorkflowUsingDELETE: + method: delete + resourcePath: /catalog/software/ref/{catalogIdx}/{catalogRefIdx} + description: deleteCatalogRefWorkflow + deleteCatalogUsingDELETE: + method: delete + resourcePath: /catalog/software/{catalogIdx} + description: software catalog 삭제 + deleteComponentUsingDELETE: + method: delete + resourcePath: /oss/v1/components/{module}/delete/{id} + description: deleteComponent + deleteOssTypeUsingDELETE: + method: delete + resourcePath: /ossType/{ossTypeIdx} + description: deleteOssType + deleteOssUsingDELETE: + method: delete + resourcePath: /oss/{ossIdx} + description: deleteOss + deleteRepositoryFileUsingDELETE: + method: delete + resourcePath: /repository/file/{filename} + description: deleteRepositoryFile + deleteRepositoryUsingDELETE: + method: delete + resourcePath: /oss/v1/repositories/{module}/delete/{name} + description: deleteRepository + deleteRepositoryUsingDELETE_1: + method: delete + resourcePath: /repository/ + description: deleteRepository + detailOssTypeUsingGET: + method: get + resourcePath: /ossType/{ossTypeIdx} + description: detailOssType + detailOssUsingGET: + method: get + resourcePath: /oss/{ossIdx} + description: detailOss + errorHtmlUsingDELETE: + method: delete + resourcePath: /error + description: errorHtml + errorHtmlUsingGET: + method: get + resourcePath: /error + description: errorHtml + errorHtmlUsingHEAD: + method: head + resourcePath: /error + description: errorHtml + errorHtmlUsingOPTIONS: + method: options + resourcePath: /error + description: errorHtml + errorHtmlUsingPATCH: + method: patch + resourcePath: /error + description: errorHtml + errorHtmlUsingPOST: + method: post + resourcePath: /error + description: errorHtml + errorHtmlUsingPUT: + method: put + resourcePath: /error + description: errorHtml + execWorkflowUsingPOST: + method: post + resourcePath: /catalog/software/ref/workflow + description: execWorkflow + generateConfigmapYamlUsingPOST: + method: post + resourcePath: /yaml/configmap + description: generateConfigmapYaml + generateDeploymentYamlUsingPOST: + method: post + resourcePath: /yaml/deployment + description: generateDeploymentYaml + generateHPAYamlUsingPOST: + method: post + resourcePath: /yaml/hpa + description: generateHPAYaml + generatePodYamlUsingPOST: + method: post + resourcePath: /yaml/pod + description: generatePodYaml + generateServiceYamlUsingPOST: + method: post + resourcePath: /yaml/service + description: generateServiceYaml + getArtifactHubListUsingGET: + method: get + resourcePath: /search/artifacthub/{keyword} + description: getArtifactHubList + getCatalogDetailUsingGET: + method: get + resourcePath: /catalog/software/{catalogIdx} + description: software catalog 내용 확인(연결된 정보들까지) + getCatalogListUsingGET: + method: get + resourcePath: /catalog/software/ + description: software catalog 리스트 불러오기 + getCatalogReferenceUsingGET: + method: get + resourcePath: /catalog/software/ref/{catalogIdx} + description: getCatalogReference + getComponentDetailByNameUsingGET: + method: get + resourcePath: /oss/v1/components/{module}/detail/{id} + description: getComponentDetailByName + getComponentListUsingGET: + method: get + resourcePath: /oss/v1/components/{module}/list/{name} + description: getComponentList + getDockerHubListUsingGET: + method: get + resourcePath: /search/dockerhub/{keyword} + description: getDockerHubList + getManifestDetailTxtUsingGET: + method: get + resourcePath: /manifest/{manifestIdx}/txt + description: getManifestDetailTxt + getManifestDetailUsingGET: + method: get + resourcePath: /manifest/{manifestIdx} + description: getManifestDetail + getManifestUsingGET: + method: get + resourcePath: /manifest/ + description: getManifest + getOssListUsingGET: + method: get + resourcePath: /oss/list/{ossTypeName} + description: getOssList + getOssListUsingGET_1: + method: get + resourcePath: /oss/list + description: getOssList + getOssTypeListUsingGET: + method: get + resourcePath: /ossType/list + description: getOssTypeList + getRepositoryDetailByNameUsingGET: + method: get + resourcePath: /oss/v1/repositories/{module}/detail/{name} + description: getRepositoryDetailByName + getRepositoryFileUsingGET: + method: get + resourcePath: /repository/file/{filename} + description: getRepositoryFile + getRepositoryListUsingGET: + method: get + resourcePath: /oss/v1/repositories/{module}/list + description: getRepositoryList + getRepositoryListUsingGET_1: + method: get + resourcePath: /repository/ + description: getRepositoryList + getRepositoryUsingGET: + method: get + resourcePath: /repository/{repositoryName} + description: getRepository + insertRepositoryUsingPOST: + method: post + resourcePath: /repository/{repositoryName} + description: insertRepository + isOssInfoDuplicatedUsingGET: + method: get + resourcePath: /oss/duplicate + description: isOssInfoDuplicated + openapiJsonUsingGET: + method: get + resourcePath: /v3/api-docs + description: openapiJson + openapiJsonUsingGET_1: + method: get + resourcePath: /v3/api-docs/swagger-config + description: openapiJson + openapiYamlUsingGET: + method: get + resourcePath: /v3/api-docs.yaml + description: openapiYaml + redirectToUiUsingGET: + method: get + resourcePath: /swagger-ui.html + description: redirectToUi + registOssTypeUsingPOST: + method: post + resourcePath: /ossType + description: registOssType + registOssUsingPOST: + method: post + resourcePath: /oss + description: registOss + saveManifestUsingGET: + method: get + resourcePath: /manifest/download/{manifestIdx} + description: saveManifest + updateCatalogUsingPUT: + method: put + resourcePath: /catalog/software/ + description: software catalog 수정 + updateManifestUsingDELETE: + method: delete + resourcePath: /manifest/{manifestIdx} + description: updateManifest + updateManifestUsingPUT: + method: put + resourcePath: /manifest/ + description: updateManifest + updateOssTypeUsingPATCH: + method: patch + resourcePath: /ossType/{ossTypeIdx} + description: updateOssType + updateOssUsingPATCH: + method: patch + resourcePath: /oss/{ossIdx} + description: updateOss + updateRepositoryUsingPUT: + method: put + resourcePath: /oss/v1/repositories/{module}/update + description: updateRepository + updateRepositoryUsingPUT_1: + method: put + resourcePath: /repository/ + description: updateRepository + uploadFilesUsingPOST: + method: post + resourcePath: /repository/file/ + description: file upload + mc-cost-optimizer: + _meta: + version: 0.5.0 + repository: https://github.com/m-cmp/mc-cost-optimizer + generatedAt: "2026-01-06T07:17:34Z" + getAbrnormalRcmd: + method: post + resourcePath: /api/costopti/be/opti/abnormalRcmd + description: 최근 24시간동안 과금이 발생한 서비스들의 이상 비용 여부를 확인한다. + getAlarmHistory: + method: post + resourcePath: /api/costopti/be/alarm/history + description: 최근 7일간 발생한 최적화 알람을 조회한다. + getBillAsset: + method: post + resourcePath: /api/costopti/be/getBillAsset + description: 이번달 사용한 서비스(VM, DB 등) 단위의 비용을 확인합니다. + getBillingBaseInfo: + method: post + resourcePath: /api/costopti/be/invoice/getBillingBaseInfo + description: 이번달 CSP별 요약된 빌링 인보이스를 확인한다. + getCurMonthBill: + method: post + resourcePath: /api/costopti/be/getCurMonthBill + description: 지난달 대비 이번달 비용을 확인합니다. + getInstOptiSizeRcmd: + method: post + resourcePath: /api/costopti/be/opti/instOptiSizeRcmd + description: 사용중인 인스턴스의 추천 사이즈를 확인한다. + getInvoice: + method: post + resourcePath: /api/costopti/be/invoice/getInvoice + description: 이번달 빌링 인보이스 내역을 확인한다. + getProjects: + method: get + resourcePath: /api/costopti/be/getProjects + description: 워크스페이스에 속한 프로젝트 목록을 조회합니다. + getReadyz: + method: get + resourcePath: /api/costopti/be/readyz + description: 어플리케이션의 상태를 조회합니다. + getSummary: + method: post + resourcePath: /api/costopti/be/invoice/getSummary + description: CSP별 빌링 인보이스 비용을 월별로 확인한다. + getTop5Bill: + method: post + resourcePath: /api/costopti/be/getTop5Bill + description: 이번달에 사용한 비용 상위 5개의 리소스와 비용을 확인합니다. + getUnusedRcmd: + method: post + resourcePath: /api/costopti/be/opti/unusedRcmd + description: 최근 24시간동안 과금이 발생한 리소스에 대하여 미사용 자원을 추천한다. + getWorkspaces: + method: get + resourcePath: /api/costopti/be/getWorkspaces + description: 워크스페이스 목록을 조회합니다. + updateTBBRscMeta: + method: get + resourcePath: /api/costopti/be/updateRscMeta + description: "" + mc-iam-manager: + _meta: + version: 0.3.0 + repository: https://github.com/m-cmp/mc-iam-manager + generatedAt: "2026-01-06T07:17:34Z" + UpdateFrameworkService: + method: put + resourcePath: /api/mcmp-apis/name/{serviceName} + description: Updates specific fields (e.g., BaseURL, Auth info) of an MCMP API service definition identified by its name. Cannot update name or version. + addCspRoleMappings: + method: post + resourcePath: /api/roles/csp-roles + description: Create a new mapping between role and CSP role + addProjectToWorkspace: + method: post + resourcePath: /api/workspaces/assign/projects + description: Add a project to a workspace + addUserToWorkspace: + method: post + resourcePath: /api/workspaces/{id}/users + description: Add a user to a workspace + addWorkspaceToProject: + method: post + resourcePath: /api/projects/{id}/workspaces/{workspaceId} + description: 프로젝트에 워크스페이스를 연결합니다. + assignMciamPermissionToRole: + method: post + resourcePath: /api/roles/{roleType}/{roleId}/mciam-permissions/{permissionId} + description: 역할에 MC-IAM 권한을 할당합니다. + assignPlatformRole: + method: post + resourcePath: /api/roles/assign/platform-role + description: Assign a platform role to a user + assignRole: + method: post + resourcePath: /api/roles/id/{roleId}/assign + description: Assign a role to a user + assignWorkspaceRole: + method: post + resourcePath: /api/roles/assign/workspace-role + description: Assign a workspace role to a user + checkUserRoles: + method: get + resourcePath: /api/setup/check-user-roles + description: Check all roles assigned to a user. 특정 유저가 가진 role 목록을 조회합니다. + createCspRole: + method: post + resourcePath: /api/roles/csp + description: Create a new csp role + createCspRoles: + method: post + resourcePath: /api/roles/csp-roles/batch + description: Create multiple new csp roles + createMciamPermission: + method: post + resourcePath: /api/permissions/mciam + description: Create a new permission with the specified information. + createMcmpApiPermissionActionMapping: + method: post + resourcePath: /api/mcmp-api-permission-action-mappings + description: Creates a new mapping between a permission and an API action + createMenu: + method: post + resourcePath: /api/menus + description: Create a new menu + createMenusRolesMapping: + method: post + resourcePath: /api/menus/platform-roles + description: Create a new menu mapping + createPlatformRole: + method: post + resourcePath: /api/roles/platform-roles + description: Create a new menu role + createProject: + method: post + resourcePath: /api/projects + description: Create a new project with the specified information. + createResourceType: + method: post + resourcePath: /api/resource-types/cloud-resources + description: 새로운 리소스 타입을 생성합니다 + createRole: + method: post + resourcePath: /api/roles + description: Create a new role + createUser: + method: post + resourcePath: /api/users + description: Create a new user with the specified information. + createWorkspace: + method: post + resourcePath: /api/workspaces + description: Create a new workspace with the specified information. + createWorkspaceRole: + method: post + resourcePath: /api/roles/workspace-roles + description: Create a new workspace role + deleteCspRole: + method: delete + resourcePath: /api/roles/csp-roles/id/{roleId} + description: Delete a role + deleteMapping: + method: delete + resourcePath: /api/mcmp-api-permission-action-mappings/permissions/{permissionId}/actions/{actionId} + description: Deletes a mapping between a permission and an API action + deleteMciamPermission: + method: delete + resourcePath: /api/permissions/mciam/{id} + description: Delete a permission by its ID. + deleteMenu: + method: delete + resourcePath: /api/menus/id/{menuId} + description: Delete a menu + deleteMenusRolesMapping: + method: delete + resourcePath: /api/menus/platform-roles + description: Delete the mapping between a platform role and a menu. + deletePlatformRole: + method: delete + resourcePath: /api/roles/platform-roles/id/{roleId} + description: Delete a platform role + deleteProject: + method: delete + resourcePath: /api/projects/{id} + description: Delete a project by its ID. + deleteResourceType: + method: delete + resourcePath: /api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId + description: 리소스 타입을 삭제합니다 + deleteRole: + method: delete + resourcePath: /api/roles/id/{roleId} + description: Delete a role by its name. + deleteUser: + method: delete + resourcePath: /api/users/{id} + description: Delete a user by their ID. + deleteWorkspace: + method: delete + resourcePath: /api/workspaces/id/{workspaceId} + description: Delete a workspace by its ID. + deleteWorkspaceRole: + method: delete + resourcePath: /api/roles/workspace-roles/id/{roleId} + description: Delete a workspace role + getCloudResourceTypeByID: + method: get + resourcePath: /api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId + description: 특정 리소스 타입을 ID로 조회합니다 + getCspRoleByID: + method: get + resourcePath: /api/roles/csp/id/{roleId} + description: Get csp role details by ID + getCspRoleByName: + method: get + resourcePath: /api/roles/csp/name/{roleName} + description: Get csp role details by Name + getCspRoleMappingByRoleId: + method: get + resourcePath: /api/roles/csp-roles/id/:roleId + description: Get a mapping between role and CSP role + getMciamPermissionByID: + method: get + resourcePath: /api/permissions/mciam/id/{id} + description: Retrieve permission details by permission ID. + getMenuByID: + method: post + resourcePath: /api/menus/id/{menuId} + description: Get menu details by ID + getPlatformActionsByPermissionID: + method: get + resourcePath: /api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions + description: Returns all platform actions mapped to a specific permission + getPlatformRoleByID: + method: get + resourcePath: /api/roles/platform-roles/id/{roleId} + description: Get platform role details by ID + getPlatformRoleByName: + method: get + resourcePath: /api/roles/platform-roles/name/{roleName} + description: Get menu role details by Name + getProjectByID: + method: get + resourcePath: /api/projects/{id} + description: Retrieve project details by project ID. + getProjectByName: + method: get + resourcePath: /api/projects/name/{projectName} + description: Get project details by name + getRoleByRoleID: + method: get + resourcePath: /api/roles/id/{roleId} + description: Get role details by ID + getRoleByRoleName: + method: get + resourcePath: /api/roles/name/{roleName} + description: Retrieve role details by role name. + getRoleMasterMappings: + method: get + resourcePath: /api/roles/mappings/role/id/:roleId + description: Get role master mappings + getRoleMciamPermissions: + method: get + resourcePath: /api/roles/{roleType}/{roleId}/mciam-permissions + description: 특정 역할의 MC-IAM 권한 ID 목록을 조회합니다. + getUserByID: + method: get + resourcePath: /api/users/id/{userId} + description: Retrieve user details by user ID. + getUserByKcID: + method: get + resourcePath: /api/users/kc/{kcUserId} + description: Get user details by KcID + getUserByUsername: + method: get + resourcePath: /api/users/name/{username} + description: Get user details by username + getUserMenuTree: + method: get + resourcePath: /api/menus/user-menu-tree + description: Get menu tree based on user's platform roles + getUserWorkspaceAndWorkspaceRolesByUserID: + method: get + resourcePath: /api/users/id/{userId}/workspaces/roles/list + description: Get workspaces and roles for a specific user + getUserWorkspaceAndWorkspaceRolesByUserIDAndWorkspaceID: + method: get + resourcePath: /api/users/id/{userId}/workspaces/id/{workspaceId}/roles/list + description: Get workspaces and roles for a specific user and workspace + getUserWorkspaceRoles: + method: get + resourcePath: /api/workspaces/id/{workspaceId}/users/id/{userId} + description: Get roles assigned to a user in a workspace + getUserWorkspacesByUserID: + method: get + resourcePath: /api/users/id/{userId}/workspaces/list + description: Get workspaces for a specific user + getWorkspaceByID: + method: get + resourcePath: /api/workspaces/id/{workspaceId} + description: Retrieve workspace details by workspace ID. + getWorkspaceByName: + method: get + resourcePath: /api/workspaces/name/{workspaceName} + description: Retrieve specific workspace by name + getWorkspaceProjectsByWorkspaceId: + method: get + resourcePath: /api/workspaces/id/{workspaceId}/projects/list + description: Retrieve project list belonging to specific workspace + getWorkspaceRoleByID: + method: get + resourcePath: /api/roles/workspace-roles/id/{roleId} + description: Get workspace role details by ID + getWorkspaceRoleByName: + method: get + resourcePath: /api/roles/workspace-roles/name/{roleName} + description: Get workspace role details by Name + initializeMenuPermissions: + method: get + resourcePath: /api/setup/initial-role-menu-permission + description: CSV 파일을 읽어서 메뉴 권한을 초기화합니다 + listAllWorkspaceUsersAndRoles: + method: post + resourcePath: /api/workspaces/users-roles/list + description: Retrieve the list of users and roles assigned to the workspace. + listCSPRoles: + method: post + resourcePath: /api/roles/csp/list + description: Get a list of all csp roles + listCloudResourceTypes: + method: post + resourcePath: /api/resource-types/cloud-resources/list + description: 모든 리소스 타입 목록을 조회합니다 + listCspRoleMappings: + method: post + resourcePath: /api/roles/csp-roles/list + description: Get a mapping between role and CSP role + listMappedMenusByRole: + method: post + resourcePath: /api/menus/platform-roles/list + description: List menus mapped to a specific platform role. + listMciamPermissions: + method: post + resourcePath: /api/permissions/mciam/list + description: Retrieve a list of all permissions. + listMenus: + method: post + resourcePath: /api/menus/list + description: List all menus as a tree structure. Admin permission required. + listMenusTree: + method: post + resourcePath: /api/menus/tree/list + description: List all menus as a tree structure. Admin permission required. + listPermissionsByActionID: + method: get + resourcePath: /api/mcmp-api-permission-action-mappings/actions/{actionId}/permissions + description: Returns all permissions mapped to a specific API action + listPlatformActions: + method: post + resourcePath: /api/mcmp-api-permission-action-mappings/list + description: Returns all platform actions mapped to a specific permission + listPlatformRoles: + method: post + resourcePath: /api/roles/menu-roles/list + description: Get a list of all menu roles + listProjects: + method: post + resourcePath: /api/projects/list + description: Retrieve a list of all projects. + listRoleMasterMappings: + method: post + resourcePath: /api/roles/mappings/list + description: List role master mappings + listRoles: + method: post + resourcePath: /api/roles/list + description: Retrieve a list of all roles. + listServicesAndActions: + method: post + resourcePath: /api/mcmp-apis/list + description: Retrieves all MCMP API service and action definitions currently stored in the database. + listUserMenu: + method: post + resourcePath: /api/users/menus/list + description: Get the menu list accessible to the current user's platform role. + listUserMenuTree: + method: post + resourcePath: /api/users/menus-tree/list + description: Get the menu tree accessible to the current user's platform role. + listUserProjectsByWorkspace: + method: get + resourcePath: /api/users/workspaces/id/{workspaceId}/projects/list + description: List projects for the current user + listUserWorkspaceAndWorkspaceRoles: + method: post + resourcePath: /api/users/workspaces/roles/list + description: List workspaces and roles for the current user + listUserWorkspaces: + method: post + resourcePath: /api/users/workspaces/list + description: List workspaces for the current user + listUsers: + method: post + resourcePath: /api/users/list + description: Retrieve a list of all users. + listUsersAndRolesByWorkspace: + method: post + resourcePath: /api/workspaces/id/{workspaceId}/users/list + description: Retrieve users and roles list belonging to workspace + listUsersByCspRole: + method: post + resourcePath: /api/roles/mappings/csp-roles/list + description: List users by csp role + listUsersByPlatformRole: + method: post + resourcePath: /api/roles/mappings/platform-roles/users/list + description: List users by platform role + listUsersByWorkspaceRole: + method: post + resourcePath: /api/roles/mappings/workspace-roles/users/list + description: List users by workspace role + listWorkspaceActionsByPermissionID: + method: post + resourcePath: /api/mcmp-api-permission-action-mappings/actions/list + description: Returns all workspace actions mapped to a specific permission + listWorkspaceProjects: + method: post + resourcePath: /api/workspaces/projects/list + description: Retrieve project list belonging to specific workspace + listWorkspaceRoles: + method: post + resourcePath: /api/roles/workspace-roles/list + description: Get a list of all workspace roles + listWorkspaceUsers: + method: post + resourcePath: /api/workspaces/users/list + description: List users by workspace criteria + listWorkspaces: + method: post + resourcePath: /api/workspaces/list + description: Retrieve a list of all workspaces. + mciamAuthCerts: + method: get + resourcePath: /api/auth/certs + description: Retrieve authentication certificates for MC-IAM-Manager to be used in target frameworks for token validation. + mciamCheckHealth: + method: get + resourcePath: /readyz + description: Check the health status of the service. + mciamCreateCredential: + method: post + resourcePath: /api/csp-credentials + description: 새로운 CSP 인증 정보를 생성합니다 + mciamDeleteCredential: + method: delete + resourcePath: /api/csp-credentials/{id} + description: CSP 인증 정보를 삭제합니다 + mciamGetCredentialByID: + method: get + resourcePath: /api/csp-credentials/{id} + description: 특정 CSP 인증 정보를 ID로 조회합니다 + mciamGetTempCredentialProviders: + method: get + resourcePath: /api/auth/temp-credential-csps + description: Get temporary credential provider information for AWS and GCP + mciamGetTemporaryCredentials: + method: post + resourcePath: /api/workspaces/temporary-credentials + description: Get temporary credentials for CSP + mciamListCredentials: + method: get + resourcePath: /api/csp-credentials + description: 모든 CSP 인증 정보 목록을 조회합니다 + mciamLogin: + method: post + resourcePath: /api/auth/login + description: Authenticate user and issue JWT token. + mciamLogout: + method: post + resourcePath: /api/auth/logout + description: Invalidate the user's refresh token and log out. + mciamRefreshToken: + method: post + resourcePath: /api/auth/refresh + description: Refresh JWT access token using a valid refresh token. + mciamUpdateCredential: + method: put + resourcePath: /api/csp-credentials/{id} + description: CSP 인증 정보를 업데이트합니다 + mciamValidateToken: + method: post + resourcePath: /api/auth/validate + description: Validate the current access token and refresh if expired + mciamWorkspaceTicket: + method: post + resourcePath: /api/workspaces/workspace-ticket + description: Set workspace ticket + mcmpApiCall: + method: post + resourcePath: /api/mcmp-apis/mcmpApiCall + description: Executes a defined MCMP API action with parameters structured in McmpApiCallRequest. + registerMenusFromBody: + method: post + resourcePath: /api/menus/setup/initial-menus2 + description: 'Parse YAML text in the request body and register or update menus in the database. Recommended Content-Type: text/plain, text/yaml, application/yaml.' + registerMenusFromYAML: + method: post + resourcePath: /api/menus/setup/initial-menus + description: Register or update menus from a local YAML file specified by the filePath query parameter, or from the MCWEBCONSOLE_MENUYAML URL in .env if not provided. If loaded from URL, the file is saved to asset/menu/menu.yaml. + removeCspRoleMappings: + method: delete + resourcePath: /api/roles/unassign/csp-roles + description: Delete a mapping between workspace role and CSP role + removeMciamPermissionFromRole: + method: delete + resourcePath: /api/roles/{roleType}/{roleId}/mciam-permissions/{permissionId} + description: 역할에서 MC-IAM 권한을 제거합니다. + removePlatformRole: + method: delete + resourcePath: /api/roles/unassign/platform-role + description: Remove a platform role from a user + removeProjectFromWorkspace: + method: delete + resourcePath: /api/workspaces/unassign/projects + description: Remove a project from a workspace + removeRole: + method: delete + resourcePath: /api/roles/id/{roleId}/unassign + description: Remove a role from a user + removeUserFromWorkspace: + method: delete + resourcePath: /api/workspaces/{id}/users/{userId} + description: Remove a user from a workspace + removeWorkspaceRole: + method: delete + resourcePath: /api/roles/unassign/workspace-role + description: Remove a workspace role from a user + setActiveVersion: + method: put + resourcePath: /api/mcmp-apis/name/{serviceName}/versions/{version}/activate + description: Sets the specified version of an MCMP API service as the active one. + setupInitialAdmin: + method: post + resourcePath: /api/initial-admin + description: Creates the initial platform admin user with necessary permissions. platform admin 생성인데 + syncMcmpAPIs: + method: post + resourcePath: /api/mcmp-apis/syncMcmpAPIs + description: Triggers the synchronization of MCMP API definitions from the configured YAML URL to the database. + syncProjects: + method: post + resourcePath: /api/setup/sync-projects + description: mc-infra-manager의 네임스페이스 목록을 조회하여 로컬 DB에 없는 프로젝트를 추가합니다. + testCallGetAllNs: + method: get + resourcePath: /api/mcmp-apis/test/mc-infra-manager/getallns + description: Calls the GetAllNs action of the mc-infra-manager service via the CallApi service. + updateCspRole: + method: put + resourcePath: /api/roles/csp-roles/id/{roleId} + description: Update role information + updateMapping: + method: put + resourcePath: /api/mcmp-api-permission-action-mappings/permissions/{permissionId}/actions/{actionId} + description: Updates an existing mapping between a permission and an API action + updateMciamPermission: + method: put + resourcePath: /api/permissions/mciam/{id} + description: Update the details of an existing permission. + updateMenu: + method: put + resourcePath: /api/menus/id/{menuId} + description: Update menu information + updateProject: + method: put + resourcePath: /api/projects/{id} + description: Update the details of an existing project. + updateResourceType: + method: put + resourcePath: /api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId + description: 리소스 타입 정보를 업데이트합니다 + updateRole: + method: put + resourcePath: /api/roles/id/{roleId} + description: Update the details of an existing role. + updateUser: + method: put + resourcePath: /api/users/{id} + description: Update the details of an existing user. + updateUserStatus: + method: post + resourcePath: /api/users/id/{userId}/status + description: Update user status (active/inactive) + updateWorkspace: + method: put + resourcePath: /api/workspaces/id/{workspaceId} + description: Update the details of an existing workspace. + mc-workflow-manager: + _meta: + version: 0.5.0 + repository: https://github.com/m-cmp/mc-workflow-manager + generatedAt: "2026-01-06T07:17:34Z" + checkConnectionUsingGET: + method: get + resourcePath: /readyz + description: checkConnection + checkConnectionUsingPOST: + method: post + resourcePath: /oss/connection-check + description: checkConnection + deleteEventListnerUsingDELETE: + method: delete + resourcePath: /eventlistener/{eventListenerIdx} + description: deleteEventListner + deleteOssTypeUsingDELETE: + method: delete + resourcePath: /ossType/{ossTypeIdx} + description: deleteOssType + deleteOssUsingDELETE: + method: delete + resourcePath: /oss/{ossIdx} + description: deleteOss + deleteWorkflowStageTypeUsingDELETE: + method: delete + resourcePath: /workflowStageType/{workflowStageTypeIdx} + description: deleteWorkflowStageType + deleteWorkflowStageUsingDELETE: + method: delete + resourcePath: /workflowStage/{workflowStageIdx} + description: deleteWorkflowStage + deleteWorkflowUsingDELETE: + method: delete + resourcePath: /workflow/{workflowIdx} + description: deleteWorkflow + detailEventListenerUsingGET: + method: get + resourcePath: /eventlistener/{eventListenerIdx} + description: detailEventListener + detailOssTypeUsingGET: + method: get + resourcePath: /ossType/{ossTypeIdx} + description: detailOssType + detailOssUsingGET: + method: get + resourcePath: /oss/{ossIdx} + description: detailOss + detailWorkflowStageTypeUsingGET: + method: get + resourcePath: /workflowStageType/{workflowStageTypeIdx} + description: detailWorkflowStageType + detailWorkflowStageUsingGET: + method: get + resourcePath: /workflowStage/{workflowStageIdx} + description: detailWorkflowStage + errorUsingDELETE: + method: delete + resourcePath: /error + description: error + errorUsingGET: + method: get + resourcePath: /error + description: error + errorUsingHEAD: + method: head + resourcePath: /error + description: error + errorUsingOPTIONS: + method: options + resourcePath: /error + description: error + errorUsingPATCH: + method: patch + resourcePath: /error + description: error + errorUsingPOST: + method: post + resourcePath: /error + description: error + errorUsingPUT: + method: put + resourcePath: /error + description: error + getDefaultWorkflowStageUsingGET: + method: get + resourcePath: /workflowStage/default/script/{workflowStageTypeName} + description: getDefaultWorkflowStage + getEventListenerListUsingGET: + method: get + resourcePath: /eventlistener/list + description: getEventListenerList + getOssListUsingGET: + method: get + resourcePath: /oss/list/{ossTypeName} + description: getOssList + getOssListUsingGET_1: + method: get + resourcePath: /oss/list + description: getOssList + getOssTypeFilteredListUsingGET: + method: get + resourcePath: /ossType/filter/list + description: getOssTypeFilteredList + getOssTypeListUsingGET: + method: get + resourcePath: /ossType/list + description: getOssTypeList + getWorkflowDetailUsingGET: + method: get + resourcePath: /eventlistener/workflowDetail/{workflowIdx}/{evnetListenerYn} + description: getWorkflowDetail + getWorkflowHistoryListUsingGET: + method: get + resourcePath: /workflow/history/{workflowIdx} + description: getWorkflowHistoryList + getWorkflowListUsingGET: + method: get + resourcePath: /eventlistener/workflowList/{eventListenerYn} + description: getWorkflowList + getWorkflowListUsingGET_1: + method: get + resourcePath: /workflow/list + description: getWorkflowList + getWorkflowLogUsingGET: + method: get + resourcePath: /workflow/log/{workflowIdx} + description: getWorkflowLog + getWorkflowParamListUsingGET: + method: get + resourcePath: /workflow/param/list + description: getWorkflowParamList + getWorkflowRunHistoryListUsingGET: + method: get + resourcePath: /workflow/runHistory/{workflowIdx} + description: getWorkflowRunHistoryList + getWorkflowStageHistoryListUsingGET: + method: get + resourcePath: /workflow/stageHistory/{workflowIdx} + description: getWorkflowStageHistoryList + getWorkflowStageListUsingGET: + method: get + resourcePath: /workflow/workflowStageList + description: getWorkflowStageList + getWorkflowStageListUsingGET_1: + method: get + resourcePath: /workflowStage/list + description: getWorkflowStageList + getWorkflowStageListUsingGET_2: + method: get + resourcePath: /workflowStageType/list + description: getWorkflowStageList + getWorkflowTemplateUsingGET: + method: get + resourcePath: /workflow/template/{workflowName} + description: getWorkflowTemplate + getWorkflowUsingGET: + method: get + resourcePath: /workflow/{workflowIdx} + description: getWorkflow + isEventListenerDuplicatedUsingGET: + method: get + resourcePath: /eventlistener/duplicate + description: isEventListenerDuplicated + isOssInfoDuplicatedUsingGET: + method: get + resourcePath: /oss/duplicate + description: isOssInfoDuplicated + isWorkflowNameDuplicatedUsingGET: + method: get + resourcePath: /workflow/name/duplicate + description: isWorkflowNameDuplicated + isWorkflowStageNameDuplicatedUsingGET: + method: get + resourcePath: /workflowStage/duplicate + description: isWorkflowStageNameDuplicated + openapiJsonUsingGET: + method: get + resourcePath: /v3/api-docs + description: openapiJson + openapiJsonUsingGET_1: + method: get + resourcePath: /v3/api-docs/swagger-config + description: openapiJson + openapiYamlUsingGET: + method: get + resourcePath: /v3/api-docs.yaml + description: openapiYaml + redirectToUiUsingGET: + method: get + resourcePath: /swagger-ui.html + description: redirectToUi + registEventListnerUsingPOST: + method: post + resourcePath: /eventlistener + description: registEventListner + registOssTypeUsingPOST: + method: post + resourcePath: /ossType + description: registOssType + registOssUsingPOST: + method: post + resourcePath: /oss + description: registOss + registWorkflowStageUsingPOST: + method: post + resourcePath: /workflowStage + description: registWorkflowStage + registWorkflowStageUsingPOST_1: + method: post + resourcePath: /workflowStageType + description: registWorkflowStage + registWorkflowUsingPOST: + method: post + resourcePath: /workflow + description: registWorkflow + runEventListenerUsingGET: + method: get + resourcePath: /eventlistener/run/{eventListenerIdx} + description: runEventListener + runWorkflowGetUsingGET: + method: get + resourcePath: /workflow/run/{workflowIdx} + description: runWorkflowGet + runWorkflowPostUsingPOST: + method: post + resourcePath: /workflow/run + description: runWorkflowPost + updateEventListnerUsingPATCH: + method: patch + resourcePath: /eventlistener/{eventListenerIdx} + description: updateEventListner + updateOssTypeUsingPATCH: + method: patch + resourcePath: /ossType/{ossTypeIdx} + description: updateOssType + updateOssUsingPATCH: + method: patch + resourcePath: /oss/{ossIdx} + description: updateOss + updateWorkflowStageTypeUsingPATCH: + method: patch + resourcePath: /workflowStageType/{workflowStageTypeIdx} + description: updateWorkflowStageType + updateWorkflowStageUsingPATCH: + method: patch + resourcePath: /workflowStage/{workflowStageIdx} + description: updateWorkflowStage + updateWorkflowUsingPATCH: + method: patch + resourcePath: /workflow/{workflowIdx} + description: updateWorkflow diff --git a/conf/mc-iam-manager/frameworks.yaml b/conf/mc-iam-manager/frameworks.yaml new file mode 100644 index 00000000..32d2c645 --- /dev/null +++ b/conf/mc-iam-manager/frameworks.yaml @@ -0,0 +1,84 @@ +# Swagger-to-Actions Framework Configuration +# This file defines multiple frameworks and their Swagger specifications +# to be aggregated into a single serviceActions YAML file. +# +# Version Management: +# - Each framework has a 'version' field to track the active API version +# - The output file includes '_meta' with version, repository, and generatedAt +# - mc-iam-manager can load this file at init and manage version tracking in DB + +# Output file path (relative to this config file) +output: ./service-actions.yaml + +# HTTP timeout for fetching remote Swagger specs (in seconds) +timeout: 30 + +# Enable verbose output +verbose: false + +# List of frameworks to process +frameworks: + # MC-IAM-Manager - Identity and Access Management + - name: mc-iam-manager + version: "0.3.0" # Active version (local development) + repository: https://github.com/m-cmp/mc-iam-manager + swagger: ../../docs/swagger.yaml # Local path relative to this file + + # MC-Observability - Monitoring and Observability + # Note: Swagger file path needs to be verified - currently not found in v0.5.0 + # - name: mc-observability + # version: "0.5.0" # Latest release: v0.5.0 (Nov 3, 2025) + # repository: https://github.com/m-cmp/mc-observability + # swagger: https://raw.githubusercontent.com/m-cmp/mc-observability/v0.5.0/swagger/swagger.yaml + + # MC-Application-Manager - Application Deployment Management + - name: mc-application-manager + version: "0.5.0" # Latest release: v0.5.0 + repository: https://github.com/m-cmp/mc-application-manager + swagger: https://raw.githubusercontent.com/m-cmp/mc-application-manager/v0.5.0/swagger.json + + # MC-Cost-Optimizer - Cost Optimization + - name: mc-cost-optimizer + version: "0.5.0" # Latest release: v0.5.0 + repository: https://github.com/m-cmp/mc-cost-optimizer + swagger: https://raw.githubusercontent.com/m-cmp/mc-cost-optimizer/v0.5.0/swagger.yaml + + # MC-Workflow-Manager - Workflow Orchestration + - name: mc-workflow-manager + version: "0.5.0" # Latest release: v0.5.0 + repository: https://github.com/m-cmp/mc-workflow-manager + swagger: https://raw.githubusercontent.com/m-cmp/mc-workflow-manager/v0.5.0/swagger.json + + # MC-Infra-Manager - Multi-Cloud Infrastructure Management + # Note: No releases found - using main branch + # - name: mc-infra-manager + # version: "main" # No releases, using main branch + # repository: https://github.com/m-cmp/mc-infra-manager + # swagger: https://raw.githubusercontent.com/m-cmp/mc-infra-manager/main/swagger.yaml + + # MC-Infra-Connector - Cloud Infrastructure Connection + # Note: No releases found - using main branch + # - name: mc-infra-connector + # version: "main" # No releases, using main branch + # repository: https://github.com/m-cmp/mc-infra-connector + # swagger: https://raw.githubusercontent.com/m-cmp/mc-infra-connector/main/swagger.yaml + + # MC-Data-Manager - Data Management + # Note: No releases found - using main branch + # - name: mc-data-manager + # version: "main" # No releases, using main branch + # repository: https://github.com/m-cmp/mc-data-manager + # swagger: https://raw.githubusercontent.com/m-cmp/mc-data-manager/main/swagger.yaml + + # MC-Across-Service-Manager - Cross-Service Management + # Note: Latest release is v0.1.0, not v0.5.0 - Swagger file path needs verification + # - name: mc-across-service-manager + # version: "0.1.0" # Latest release: v0.1.0 (not v0.5.0) + # repository: https://github.com/m-cmp/mc-across-service-manager + # swagger: https://raw.githubusercontent.com/m-cmp/mc-across-service-manager/v0.1.0/swagger.yaml + + # MC-Web-Console - Web Console Interface + # - name: mc-web-console + # version: "0.1.0" + # repository: https://github.com/m-cmp/mc-web-console + # swagger: /path/to/swagger.yaml diff --git a/conf/mc-iam-manager/menu.yaml b/conf/mc-iam-manager/menu.yaml index 0d37b5d6..ab161459 100644 --- a/conf/mc-iam-manager/menu.yaml +++ b/conf/mc-iam-manager/menu.yaml @@ -263,6 +263,14 @@ menus: isaction: true priority: 2 menunumber: 1740 + + - id: csproles + parentid: workspaces + displayname: CSP Roles + restype: menu + isaction: true + priority: 2 + menunumber: 1741 - id: projectboard parentid: workspaces diff --git a/conf/mc-iam-manager/service-actions.yaml b/conf/mc-iam-manager/service-actions.yaml new file mode 100644 index 00000000..ee2c0966 --- /dev/null +++ b/conf/mc-iam-manager/service-actions.yaml @@ -0,0 +1,1121 @@ +serviceActions: + mc-application-manager: + _meta: + version: 0.5.0 + repository: https://github.com/m-cmp/mc-application-manager + generatedAt: "2026-01-06T07:17:34Z" + checkConnectionUsingPOST: + method: post + resourcePath: /oss/connection-check + description: checkConnection + createCatalogRefUsingPOST: + method: post + resourcePath: /catalog/software/ref/{catalogIdx} + description: software catalog 관련정보 등록(webpage, workflow 등) + createCatalogUsingPOST: + method: post + resourcePath: /catalog/software/ + description: software catalog 등록 + createComponentByTextUsingPOST: + method: post + resourcePath: /oss/v1/components/{module}/create/{name}/text + description: createComponentByText + createComponentUsingPOST: + method: post + resourcePath: /oss/v1/components/{module}/create/{name} + description: createComponent + createManifestUsingPOST: + method: post + resourcePath: /manifest/ + description: createManifest + createRepositoryUsingPOST: + method: post + resourcePath: /oss/v1/repositories/{module}/create + description: createRepository + createRepositoryUsingPOST_1: + method: post + resourcePath: /repository/ + description: createRepository + deleteCatalogRefWorkflowUsingDELETE: + method: delete + resourcePath: /catalog/software/ref/{catalogIdx}/{catalogRefIdx} + description: deleteCatalogRefWorkflow + deleteCatalogUsingDELETE: + method: delete + resourcePath: /catalog/software/{catalogIdx} + description: software catalog 삭제 + deleteComponentUsingDELETE: + method: delete + resourcePath: /oss/v1/components/{module}/delete/{id} + description: deleteComponent + deleteOssTypeUsingDELETE: + method: delete + resourcePath: /ossType/{ossTypeIdx} + description: deleteOssType + deleteOssUsingDELETE: + method: delete + resourcePath: /oss/{ossIdx} + description: deleteOss + deleteRepositoryFileUsingDELETE: + method: delete + resourcePath: /repository/file/{filename} + description: deleteRepositoryFile + deleteRepositoryUsingDELETE: + method: delete + resourcePath: /oss/v1/repositories/{module}/delete/{name} + description: deleteRepository + deleteRepositoryUsingDELETE_1: + method: delete + resourcePath: /repository/ + description: deleteRepository + detailOssTypeUsingGET: + method: get + resourcePath: /ossType/{ossTypeIdx} + description: detailOssType + detailOssUsingGET: + method: get + resourcePath: /oss/{ossIdx} + description: detailOss + errorHtmlUsingDELETE: + method: delete + resourcePath: /error + description: errorHtml + errorHtmlUsingGET: + method: get + resourcePath: /error + description: errorHtml + errorHtmlUsingHEAD: + method: head + resourcePath: /error + description: errorHtml + errorHtmlUsingOPTIONS: + method: options + resourcePath: /error + description: errorHtml + errorHtmlUsingPATCH: + method: patch + resourcePath: /error + description: errorHtml + errorHtmlUsingPOST: + method: post + resourcePath: /error + description: errorHtml + errorHtmlUsingPUT: + method: put + resourcePath: /error + description: errorHtml + execWorkflowUsingPOST: + method: post + resourcePath: /catalog/software/ref/workflow + description: execWorkflow + generateConfigmapYamlUsingPOST: + method: post + resourcePath: /yaml/configmap + description: generateConfigmapYaml + generateDeploymentYamlUsingPOST: + method: post + resourcePath: /yaml/deployment + description: generateDeploymentYaml + generateHPAYamlUsingPOST: + method: post + resourcePath: /yaml/hpa + description: generateHPAYaml + generatePodYamlUsingPOST: + method: post + resourcePath: /yaml/pod + description: generatePodYaml + generateServiceYamlUsingPOST: + method: post + resourcePath: /yaml/service + description: generateServiceYaml + getArtifactHubListUsingGET: + method: get + resourcePath: /search/artifacthub/{keyword} + description: getArtifactHubList + getCatalogDetailUsingGET: + method: get + resourcePath: /catalog/software/{catalogIdx} + description: software catalog 내용 확인(연결된 정보들까지) + getCatalogListUsingGET: + method: get + resourcePath: /catalog/software/ + description: software catalog 리스트 불러오기 + getCatalogReferenceUsingGET: + method: get + resourcePath: /catalog/software/ref/{catalogIdx} + description: getCatalogReference + getComponentDetailByNameUsingGET: + method: get + resourcePath: /oss/v1/components/{module}/detail/{id} + description: getComponentDetailByName + getComponentListUsingGET: + method: get + resourcePath: /oss/v1/components/{module}/list/{name} + description: getComponentList + getDockerHubListUsingGET: + method: get + resourcePath: /search/dockerhub/{keyword} + description: getDockerHubList + getManifestDetailTxtUsingGET: + method: get + resourcePath: /manifest/{manifestIdx}/txt + description: getManifestDetailTxt + getManifestDetailUsingGET: + method: get + resourcePath: /manifest/{manifestIdx} + description: getManifestDetail + getManifestUsingGET: + method: get + resourcePath: /manifest/ + description: getManifest + getOssListUsingGET: + method: get + resourcePath: /oss/list/{ossTypeName} + description: getOssList + getOssListUsingGET_1: + method: get + resourcePath: /oss/list + description: getOssList + getOssTypeListUsingGET: + method: get + resourcePath: /ossType/list + description: getOssTypeList + getRepositoryDetailByNameUsingGET: + method: get + resourcePath: /oss/v1/repositories/{module}/detail/{name} + description: getRepositoryDetailByName + getRepositoryFileUsingGET: + method: get + resourcePath: /repository/file/{filename} + description: getRepositoryFile + getRepositoryListUsingGET: + method: get + resourcePath: /oss/v1/repositories/{module}/list + description: getRepositoryList + getRepositoryListUsingGET_1: + method: get + resourcePath: /repository/ + description: getRepositoryList + getRepositoryUsingGET: + method: get + resourcePath: /repository/{repositoryName} + description: getRepository + insertRepositoryUsingPOST: + method: post + resourcePath: /repository/{repositoryName} + description: insertRepository + isOssInfoDuplicatedUsingGET: + method: get + resourcePath: /oss/duplicate + description: isOssInfoDuplicated + openapiJsonUsingGET: + method: get + resourcePath: /v3/api-docs + description: openapiJson + openapiJsonUsingGET_1: + method: get + resourcePath: /v3/api-docs/swagger-config + description: openapiJson + openapiYamlUsingGET: + method: get + resourcePath: /v3/api-docs.yaml + description: openapiYaml + redirectToUiUsingGET: + method: get + resourcePath: /swagger-ui.html + description: redirectToUi + registOssTypeUsingPOST: + method: post + resourcePath: /ossType + description: registOssType + registOssUsingPOST: + method: post + resourcePath: /oss + description: registOss + saveManifestUsingGET: + method: get + resourcePath: /manifest/download/{manifestIdx} + description: saveManifest + updateCatalogUsingPUT: + method: put + resourcePath: /catalog/software/ + description: software catalog 수정 + updateManifestUsingDELETE: + method: delete + resourcePath: /manifest/{manifestIdx} + description: updateManifest + updateManifestUsingPUT: + method: put + resourcePath: /manifest/ + description: updateManifest + updateOssTypeUsingPATCH: + method: patch + resourcePath: /ossType/{ossTypeIdx} + description: updateOssType + updateOssUsingPATCH: + method: patch + resourcePath: /oss/{ossIdx} + description: updateOss + updateRepositoryUsingPUT: + method: put + resourcePath: /oss/v1/repositories/{module}/update + description: updateRepository + updateRepositoryUsingPUT_1: + method: put + resourcePath: /repository/ + description: updateRepository + uploadFilesUsingPOST: + method: post + resourcePath: /repository/file/ + description: file upload + mc-cost-optimizer: + _meta: + version: 0.5.0 + repository: https://github.com/m-cmp/mc-cost-optimizer + generatedAt: "2026-01-06T07:17:34Z" + getAbrnormalRcmd: + method: post + resourcePath: /api/costopti/be/opti/abnormalRcmd + description: 최근 24시간동안 과금이 발생한 서비스들의 이상 비용 여부를 확인한다. + getAlarmHistory: + method: post + resourcePath: /api/costopti/be/alarm/history + description: 최근 7일간 발생한 최적화 알람을 조회한다. + getBillAsset: + method: post + resourcePath: /api/costopti/be/getBillAsset + description: 이번달 사용한 서비스(VM, DB 등) 단위의 비용을 확인합니다. + getBillingBaseInfo: + method: post + resourcePath: /api/costopti/be/invoice/getBillingBaseInfo + description: 이번달 CSP별 요약된 빌링 인보이스를 확인한다. + getCurMonthBill: + method: post + resourcePath: /api/costopti/be/getCurMonthBill + description: 지난달 대비 이번달 비용을 확인합니다. + getInstOptiSizeRcmd: + method: post + resourcePath: /api/costopti/be/opti/instOptiSizeRcmd + description: 사용중인 인스턴스의 추천 사이즈를 확인한다. + getInvoice: + method: post + resourcePath: /api/costopti/be/invoice/getInvoice + description: 이번달 빌링 인보이스 내역을 확인한다. + getProjects: + method: get + resourcePath: /api/costopti/be/getProjects + description: 워크스페이스에 속한 프로젝트 목록을 조회합니다. + getReadyz: + method: get + resourcePath: /api/costopti/be/readyz + description: 어플리케이션의 상태를 조회합니다. + getSummary: + method: post + resourcePath: /api/costopti/be/invoice/getSummary + description: CSP별 빌링 인보이스 비용을 월별로 확인한다. + getTop5Bill: + method: post + resourcePath: /api/costopti/be/getTop5Bill + description: 이번달에 사용한 비용 상위 5개의 리소스와 비용을 확인합니다. + getUnusedRcmd: + method: post + resourcePath: /api/costopti/be/opti/unusedRcmd + description: 최근 24시간동안 과금이 발생한 리소스에 대하여 미사용 자원을 추천한다. + getWorkspaces: + method: get + resourcePath: /api/costopti/be/getWorkspaces + description: 워크스페이스 목록을 조회합니다. + updateTBBRscMeta: + method: get + resourcePath: /api/costopti/be/updateRscMeta + description: "" + mc-iam-manager: + _meta: + version: 0.3.0 + repository: https://github.com/m-cmp/mc-iam-manager + generatedAt: "2026-01-06T07:17:34Z" + UpdateFrameworkService: + method: put + resourcePath: /api/mcmp-apis/name/{serviceName} + description: Updates specific fields (e.g., BaseURL, Auth info) of an MCMP API service definition identified by its name. Cannot update name or version. + addCspRoleMappings: + method: post + resourcePath: /api/roles/csp-roles + description: Create a new mapping between role and CSP role + addProjectToWorkspace: + method: post + resourcePath: /api/workspaces/assign/projects + description: Add a project to a workspace + addUserToWorkspace: + method: post + resourcePath: /api/workspaces/{id}/users + description: Add a user to a workspace + addWorkspaceToProject: + method: post + resourcePath: /api/projects/{id}/workspaces/{workspaceId} + description: 프로젝트에 워크스페이스를 연결합니다. + assignMciamPermissionToRole: + method: post + resourcePath: /api/roles/{roleType}/{roleId}/mciam-permissions/{permissionId} + description: 역할에 MC-IAM 권한을 할당합니다. + assignPlatformRole: + method: post + resourcePath: /api/roles/assign/platform-role + description: Assign a platform role to a user + assignRole: + method: post + resourcePath: /api/roles/id/{roleId}/assign + description: Assign a role to a user + assignWorkspaceRole: + method: post + resourcePath: /api/roles/assign/workspace-role + description: Assign a workspace role to a user + checkUserRoles: + method: get + resourcePath: /api/setup/check-user-roles + description: Check all roles assigned to a user. 특정 유저가 가진 role 목록을 조회합니다. + createCspRole: + method: post + resourcePath: /api/roles/csp + description: Create a new csp role + createCspRoles: + method: post + resourcePath: /api/roles/csp-roles/batch + description: Create multiple new csp roles + createMciamPermission: + method: post + resourcePath: /api/permissions/mciam + description: Create a new permission with the specified information. + createMcmpApiPermissionActionMapping: + method: post + resourcePath: /api/mcmp-api-permission-action-mappings + description: Creates a new mapping between a permission and an API action + createMenu: + method: post + resourcePath: /api/menus + description: Create a new menu + createMenusRolesMapping: + method: post + resourcePath: /api/menus/platform-roles + description: Create a new menu mapping + createPlatformRole: + method: post + resourcePath: /api/roles/platform-roles + description: Create a new menu role + createProject: + method: post + resourcePath: /api/projects + description: Create a new project with the specified information. + createResourceType: + method: post + resourcePath: /api/resource-types/cloud-resources + description: 새로운 리소스 타입을 생성합니다 + createRole: + method: post + resourcePath: /api/roles + description: Create a new role + createUser: + method: post + resourcePath: /api/users + description: Create a new user with the specified information. + createWorkspace: + method: post + resourcePath: /api/workspaces + description: Create a new workspace with the specified information. + createWorkspaceRole: + method: post + resourcePath: /api/roles/workspace-roles + description: Create a new workspace role + deleteCspRole: + method: delete + resourcePath: /api/roles/csp-roles/id/{roleId} + description: Delete a role + deleteMapping: + method: delete + resourcePath: /api/mcmp-api-permission-action-mappings/permissions/{permissionId}/actions/{actionId} + description: Deletes a mapping between a permission and an API action + deleteMciamPermission: + method: delete + resourcePath: /api/permissions/mciam/{id} + description: Delete a permission by its ID. + deleteMenu: + method: delete + resourcePath: /api/menus/id/{menuId} + description: Delete a menu + deleteMenusRolesMapping: + method: delete + resourcePath: /api/menus/platform-roles + description: Delete the mapping between a platform role and a menu. + deletePlatformRole: + method: delete + resourcePath: /api/roles/platform-roles/id/{roleId} + description: Delete a platform role + deleteProject: + method: delete + resourcePath: /api/projects/{id} + description: Delete a project by its ID. + deleteResourceType: + method: delete + resourcePath: /api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId + description: 리소스 타입을 삭제합니다 + deleteRole: + method: delete + resourcePath: /api/roles/id/{roleId} + description: Delete a role by its name. + deleteUser: + method: delete + resourcePath: /api/users/{id} + description: Delete a user by their ID. + deleteWorkspace: + method: delete + resourcePath: /api/workspaces/id/{workspaceId} + description: Delete a workspace by its ID. + deleteWorkspaceRole: + method: delete + resourcePath: /api/roles/workspace-roles/id/{roleId} + description: Delete a workspace role + getCloudResourceTypeByID: + method: get + resourcePath: /api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId + description: 특정 리소스 타입을 ID로 조회합니다 + getCspRoleByID: + method: get + resourcePath: /api/roles/csp/id/{roleId} + description: Get csp role details by ID + getCspRoleByName: + method: get + resourcePath: /api/roles/csp/name/{roleName} + description: Get csp role details by Name + getCspRoleMappingByRoleId: + method: get + resourcePath: /api/roles/csp-roles/id/:roleId + description: Get a mapping between role and CSP role + getMciamPermissionByID: + method: get + resourcePath: /api/permissions/mciam/id/{id} + description: Retrieve permission details by permission ID. + getMenuByID: + method: post + resourcePath: /api/menus/id/{menuId} + description: Get menu details by ID + getPlatformActionsByPermissionID: + method: get + resourcePath: /api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions + description: Returns all platform actions mapped to a specific permission + getPlatformRoleByID: + method: get + resourcePath: /api/roles/platform-roles/id/{roleId} + description: Get platform role details by ID + getPlatformRoleByName: + method: get + resourcePath: /api/roles/platform-roles/name/{roleName} + description: Get menu role details by Name + getProjectByID: + method: get + resourcePath: /api/projects/{id} + description: Retrieve project details by project ID. + getProjectByName: + method: get + resourcePath: /api/projects/name/{projectName} + description: Get project details by name + getRoleByRoleID: + method: get + resourcePath: /api/roles/id/{roleId} + description: Get role details by ID + getRoleByRoleName: + method: get + resourcePath: /api/roles/name/{roleName} + description: Retrieve role details by role name. + getRoleMasterMappings: + method: get + resourcePath: /api/roles/mappings/role/id/:roleId + description: Get role master mappings + getRoleMciamPermissions: + method: get + resourcePath: /api/roles/{roleType}/{roleId}/mciam-permissions + description: 특정 역할의 MC-IAM 권한 ID 목록을 조회합니다. + getUserByID: + method: get + resourcePath: /api/users/id/{userId} + description: Retrieve user details by user ID. + getUserByKcID: + method: get + resourcePath: /api/users/kc/{kcUserId} + description: Get user details by KcID + getUserByUsername: + method: get + resourcePath: /api/users/name/{username} + description: Get user details by username + getUserMenuTree: + method: get + resourcePath: /api/menus/user-menu-tree + description: Get menu tree based on user's platform roles + getUserWorkspaceAndWorkspaceRolesByUserID: + method: get + resourcePath: /api/users/id/{userId}/workspaces/roles/list + description: Get workspaces and roles for a specific user + getUserWorkspaceAndWorkspaceRolesByUserIDAndWorkspaceID: + method: get + resourcePath: /api/users/id/{userId}/workspaces/id/{workspaceId}/roles/list + description: Get workspaces and roles for a specific user and workspace + getUserWorkspaceRoles: + method: get + resourcePath: /api/workspaces/id/{workspaceId}/users/id/{userId} + description: Get roles assigned to a user in a workspace + getUserWorkspacesByUserID: + method: get + resourcePath: /api/users/id/{userId}/workspaces/list + description: Get workspaces for a specific user + getWorkspaceByID: + method: get + resourcePath: /api/workspaces/id/{workspaceId} + description: Retrieve workspace details by workspace ID. + getWorkspaceByName: + method: get + resourcePath: /api/workspaces/name/{workspaceName} + description: Retrieve specific workspace by name + getWorkspaceProjectsByWorkspaceId: + method: get + resourcePath: /api/workspaces/id/{workspaceId}/projects/list + description: Retrieve project list belonging to specific workspace + getWorkspaceRoleByID: + method: get + resourcePath: /api/roles/workspace-roles/id/{roleId} + description: Get workspace role details by ID + getWorkspaceRoleByName: + method: get + resourcePath: /api/roles/workspace-roles/name/{roleName} + description: Get workspace role details by Name + initializeMenuPermissions: + method: get + resourcePath: /api/setup/initial-role-menu-permission + description: CSV 파일을 읽어서 메뉴 권한을 초기화합니다 + listAllWorkspaceUsersAndRoles: + method: post + resourcePath: /api/workspaces/users-roles/list + description: Retrieve the list of users and roles assigned to the workspace. + listCSPRoles: + method: post + resourcePath: /api/roles/csp/list + description: Get a list of all csp roles + listCloudResourceTypes: + method: post + resourcePath: /api/resource-types/cloud-resources/list + description: 모든 리소스 타입 목록을 조회합니다 + listCspRoleMappings: + method: post + resourcePath: /api/roles/csp-roles/list + description: Get a mapping between role and CSP role + listMappedMenusByRole: + method: post + resourcePath: /api/menus/platform-roles/list + description: List menus mapped to a specific platform role. + listMciamPermissions: + method: post + resourcePath: /api/permissions/mciam/list + description: Retrieve a list of all permissions. + listMenus: + method: post + resourcePath: /api/menus/list + description: List all menus as a tree structure. Admin permission required. + listMenusTree: + method: post + resourcePath: /api/menus/tree/list + description: List all menus as a tree structure. Admin permission required. + listPermissionsByActionID: + method: get + resourcePath: /api/mcmp-api-permission-action-mappings/actions/{actionId}/permissions + description: Returns all permissions mapped to a specific API action + listPlatformActions: + method: post + resourcePath: /api/mcmp-api-permission-action-mappings/list + description: Returns all platform actions mapped to a specific permission + listPlatformRoles: + method: post + resourcePath: /api/roles/menu-roles/list + description: Get a list of all menu roles + listProjects: + method: post + resourcePath: /api/projects/list + description: Retrieve a list of all projects. + listRoleMasterMappings: + method: post + resourcePath: /api/roles/mappings/list + description: List role master mappings + listRoles: + method: post + resourcePath: /api/roles/list + description: Retrieve a list of all roles. + listServicesAndActions: + method: post + resourcePath: /api/mcmp-apis/list + description: Retrieves all MCMP API service and action definitions currently stored in the database. + listUserMenu: + method: post + resourcePath: /api/users/menus/list + description: Get the menu list accessible to the current user's platform role. + listUserMenuTree: + method: post + resourcePath: /api/users/menus-tree/list + description: Get the menu tree accessible to the current user's platform role. + listUserProjectsByWorkspace: + method: get + resourcePath: /api/users/workspaces/id/{workspaceId}/projects/list + description: List projects for the current user + listUserWorkspaceAndWorkspaceRoles: + method: post + resourcePath: /api/users/workspaces/roles/list + description: List workspaces and roles for the current user + listUserWorkspaces: + method: post + resourcePath: /api/users/workspaces/list + description: List workspaces for the current user + listUsers: + method: post + resourcePath: /api/users/list + description: Retrieve a list of all users. + listUsersAndRolesByWorkspace: + method: post + resourcePath: /api/workspaces/id/{workspaceId}/users/list + description: Retrieve users and roles list belonging to workspace + listUsersByCspRole: + method: post + resourcePath: /api/roles/mappings/csp-roles/list + description: List users by csp role + listUsersByPlatformRole: + method: post + resourcePath: /api/roles/mappings/platform-roles/users/list + description: List users by platform role + listUsersByWorkspaceRole: + method: post + resourcePath: /api/roles/mappings/workspace-roles/users/list + description: List users by workspace role + listWorkspaceActionsByPermissionID: + method: post + resourcePath: /api/mcmp-api-permission-action-mappings/actions/list + description: Returns all workspace actions mapped to a specific permission + listWorkspaceProjects: + method: post + resourcePath: /api/workspaces/projects/list + description: Retrieve project list belonging to specific workspace + listWorkspaceRoles: + method: post + resourcePath: /api/roles/workspace-roles/list + description: Get a list of all workspace roles + listWorkspaceUsers: + method: post + resourcePath: /api/workspaces/users/list + description: List users by workspace criteria + listWorkspaces: + method: post + resourcePath: /api/workspaces/list + description: Retrieve a list of all workspaces. + mciamAuthCerts: + method: get + resourcePath: /api/auth/certs + description: Retrieve authentication certificates for MC-IAM-Manager to be used in target frameworks for token validation. + mciamCheckHealth: + method: get + resourcePath: /readyz + description: Check the health status of the service. + mciamCreateCredential: + method: post + resourcePath: /api/csp-credentials + description: 새로운 CSP 인증 정보를 생성합니다 + mciamDeleteCredential: + method: delete + resourcePath: /api/csp-credentials/{id} + description: CSP 인증 정보를 삭제합니다 + mciamGetCredentialByID: + method: get + resourcePath: /api/csp-credentials/{id} + description: 특정 CSP 인증 정보를 ID로 조회합니다 + mciamGetTempCredentialProviders: + method: get + resourcePath: /api/auth/temp-credential-csps + description: Get temporary credential provider information for AWS and GCP + mciamGetTemporaryCredentials: + method: post + resourcePath: /api/workspaces/temporary-credentials + description: Get temporary credentials for CSP + mciamListCredentials: + method: get + resourcePath: /api/csp-credentials + description: 모든 CSP 인증 정보 목록을 조회합니다 + mciamLogin: + method: post + resourcePath: /api/auth/login + description: Authenticate user and issue JWT token. + mciamLogout: + method: post + resourcePath: /api/auth/logout + description: Invalidate the user's refresh token and log out. + mciamRefreshToken: + method: post + resourcePath: /api/auth/refresh + description: Refresh JWT access token using a valid refresh token. + mciamUpdateCredential: + method: put + resourcePath: /api/csp-credentials/{id} + description: CSP 인증 정보를 업데이트합니다 + mciamValidateToken: + method: post + resourcePath: /api/auth/validate + description: Validate the current access token and refresh if expired + mciamWorkspaceTicket: + method: post + resourcePath: /api/workspaces/workspace-ticket + description: Set workspace ticket + mcmpApiCall: + method: post + resourcePath: /api/mcmp-apis/mcmpApiCall + description: Executes a defined MCMP API action with parameters structured in McmpApiCallRequest. + registerMenusFromBody: + method: post + resourcePath: /api/menus/setup/initial-menus2 + description: 'Parse YAML text in the request body and register or update menus in the database. Recommended Content-Type: text/plain, text/yaml, application/yaml.' + registerMenusFromYAML: + method: post + resourcePath: /api/menus/setup/initial-menus + description: Register or update menus from a local YAML file specified by the filePath query parameter, or from the MCWEBCONSOLE_MENUYAML URL in .env if not provided. If loaded from URL, the file is saved to asset/menu/menu.yaml. + removeCspRoleMappings: + method: delete + resourcePath: /api/roles/unassign/csp-roles + description: Delete a mapping between workspace role and CSP role + removeMciamPermissionFromRole: + method: delete + resourcePath: /api/roles/{roleType}/{roleId}/mciam-permissions/{permissionId} + description: 역할에서 MC-IAM 권한을 제거합니다. + removePlatformRole: + method: delete + resourcePath: /api/roles/unassign/platform-role + description: Remove a platform role from a user + removeProjectFromWorkspace: + method: delete + resourcePath: /api/workspaces/unassign/projects + description: Remove a project from a workspace + removeRole: + method: delete + resourcePath: /api/roles/id/{roleId}/unassign + description: Remove a role from a user + removeUserFromWorkspace: + method: delete + resourcePath: /api/workspaces/{id}/users/{userId} + description: Remove a user from a workspace + removeWorkspaceRole: + method: delete + resourcePath: /api/roles/unassign/workspace-role + description: Remove a workspace role from a user + setActiveVersion: + method: put + resourcePath: /api/mcmp-apis/name/{serviceName}/versions/{version}/activate + description: Sets the specified version of an MCMP API service as the active one. + setupInitialAdmin: + method: post + resourcePath: /api/initial-admin + description: Creates the initial platform admin user with necessary permissions. platform admin 생성인데 + syncMcmpAPIs: + method: post + resourcePath: /api/mcmp-apis/syncMcmpAPIs + description: Triggers the synchronization of MCMP API definitions from the configured YAML URL to the database. + syncProjects: + method: post + resourcePath: /api/setup/sync-projects + description: mc-infra-manager의 네임스페이스 목록을 조회하여 로컬 DB에 없는 프로젝트를 추가합니다. + testCallGetAllNs: + method: get + resourcePath: /api/mcmp-apis/test/mc-infra-manager/getallns + description: Calls the GetAllNs action of the mc-infra-manager service via the CallApi service. + updateCspRole: + method: put + resourcePath: /api/roles/csp-roles/id/{roleId} + description: Update role information + updateMapping: + method: put + resourcePath: /api/mcmp-api-permission-action-mappings/permissions/{permissionId}/actions/{actionId} + description: Updates an existing mapping between a permission and an API action + updateMciamPermission: + method: put + resourcePath: /api/permissions/mciam/{id} + description: Update the details of an existing permission. + updateMenu: + method: put + resourcePath: /api/menus/id/{menuId} + description: Update menu information + updateProject: + method: put + resourcePath: /api/projects/{id} + description: Update the details of an existing project. + updateResourceType: + method: put + resourcePath: /api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId + description: 리소스 타입 정보를 업데이트합니다 + updateRole: + method: put + resourcePath: /api/roles/id/{roleId} + description: Update the details of an existing role. + updateUser: + method: put + resourcePath: /api/users/{id} + description: Update the details of an existing user. + updateUserStatus: + method: post + resourcePath: /api/users/id/{userId}/status + description: Update user status (active/inactive) + updateWorkspace: + method: put + resourcePath: /api/workspaces/id/{workspaceId} + description: Update the details of an existing workspace. + mc-workflow-manager: + _meta: + version: 0.5.0 + repository: https://github.com/m-cmp/mc-workflow-manager + generatedAt: "2026-01-06T07:17:34Z" + checkConnectionUsingGET: + method: get + resourcePath: /readyz + description: checkConnection + checkConnectionUsingPOST: + method: post + resourcePath: /oss/connection-check + description: checkConnection + deleteEventListnerUsingDELETE: + method: delete + resourcePath: /eventlistener/{eventListenerIdx} + description: deleteEventListner + deleteOssTypeUsingDELETE: + method: delete + resourcePath: /ossType/{ossTypeIdx} + description: deleteOssType + deleteOssUsingDELETE: + method: delete + resourcePath: /oss/{ossIdx} + description: deleteOss + deleteWorkflowStageTypeUsingDELETE: + method: delete + resourcePath: /workflowStageType/{workflowStageTypeIdx} + description: deleteWorkflowStageType + deleteWorkflowStageUsingDELETE: + method: delete + resourcePath: /workflowStage/{workflowStageIdx} + description: deleteWorkflowStage + deleteWorkflowUsingDELETE: + method: delete + resourcePath: /workflow/{workflowIdx} + description: deleteWorkflow + detailEventListenerUsingGET: + method: get + resourcePath: /eventlistener/{eventListenerIdx} + description: detailEventListener + detailOssTypeUsingGET: + method: get + resourcePath: /ossType/{ossTypeIdx} + description: detailOssType + detailOssUsingGET: + method: get + resourcePath: /oss/{ossIdx} + description: detailOss + detailWorkflowStageTypeUsingGET: + method: get + resourcePath: /workflowStageType/{workflowStageTypeIdx} + description: detailWorkflowStageType + detailWorkflowStageUsingGET: + method: get + resourcePath: /workflowStage/{workflowStageIdx} + description: detailWorkflowStage + errorUsingDELETE: + method: delete + resourcePath: /error + description: error + errorUsingGET: + method: get + resourcePath: /error + description: error + errorUsingHEAD: + method: head + resourcePath: /error + description: error + errorUsingOPTIONS: + method: options + resourcePath: /error + description: error + errorUsingPATCH: + method: patch + resourcePath: /error + description: error + errorUsingPOST: + method: post + resourcePath: /error + description: error + errorUsingPUT: + method: put + resourcePath: /error + description: error + getDefaultWorkflowStageUsingGET: + method: get + resourcePath: /workflowStage/default/script/{workflowStageTypeName} + description: getDefaultWorkflowStage + getEventListenerListUsingGET: + method: get + resourcePath: /eventlistener/list + description: getEventListenerList + getOssListUsingGET: + method: get + resourcePath: /oss/list/{ossTypeName} + description: getOssList + getOssListUsingGET_1: + method: get + resourcePath: /oss/list + description: getOssList + getOssTypeFilteredListUsingGET: + method: get + resourcePath: /ossType/filter/list + description: getOssTypeFilteredList + getOssTypeListUsingGET: + method: get + resourcePath: /ossType/list + description: getOssTypeList + getWorkflowDetailUsingGET: + method: get + resourcePath: /eventlistener/workflowDetail/{workflowIdx}/{evnetListenerYn} + description: getWorkflowDetail + getWorkflowHistoryListUsingGET: + method: get + resourcePath: /workflow/history/{workflowIdx} + description: getWorkflowHistoryList + getWorkflowListUsingGET: + method: get + resourcePath: /eventlistener/workflowList/{eventListenerYn} + description: getWorkflowList + getWorkflowListUsingGET_1: + method: get + resourcePath: /workflow/list + description: getWorkflowList + getWorkflowLogUsingGET: + method: get + resourcePath: /workflow/log/{workflowIdx} + description: getWorkflowLog + getWorkflowParamListUsingGET: + method: get + resourcePath: /workflow/param/list + description: getWorkflowParamList + getWorkflowRunHistoryListUsingGET: + method: get + resourcePath: /workflow/runHistory/{workflowIdx} + description: getWorkflowRunHistoryList + getWorkflowStageHistoryListUsingGET: + method: get + resourcePath: /workflow/stageHistory/{workflowIdx} + description: getWorkflowStageHistoryList + getWorkflowStageListUsingGET: + method: get + resourcePath: /workflow/workflowStageList + description: getWorkflowStageList + getWorkflowStageListUsingGET_1: + method: get + resourcePath: /workflowStage/list + description: getWorkflowStageList + getWorkflowStageListUsingGET_2: + method: get + resourcePath: /workflowStageType/list + description: getWorkflowStageList + getWorkflowTemplateUsingGET: + method: get + resourcePath: /workflow/template/{workflowName} + description: getWorkflowTemplate + getWorkflowUsingGET: + method: get + resourcePath: /workflow/{workflowIdx} + description: getWorkflow + isEventListenerDuplicatedUsingGET: + method: get + resourcePath: /eventlistener/duplicate + description: isEventListenerDuplicated + isOssInfoDuplicatedUsingGET: + method: get + resourcePath: /oss/duplicate + description: isOssInfoDuplicated + isWorkflowNameDuplicatedUsingGET: + method: get + resourcePath: /workflow/name/duplicate + description: isWorkflowNameDuplicated + isWorkflowStageNameDuplicatedUsingGET: + method: get + resourcePath: /workflowStage/duplicate + description: isWorkflowStageNameDuplicated + openapiJsonUsingGET: + method: get + resourcePath: /v3/api-docs + description: openapiJson + openapiJsonUsingGET_1: + method: get + resourcePath: /v3/api-docs/swagger-config + description: openapiJson + openapiYamlUsingGET: + method: get + resourcePath: /v3/api-docs.yaml + description: openapiYaml + redirectToUiUsingGET: + method: get + resourcePath: /swagger-ui.html + description: redirectToUi + registEventListnerUsingPOST: + method: post + resourcePath: /eventlistener + description: registEventListner + registOssTypeUsingPOST: + method: post + resourcePath: /ossType + description: registOssType + registOssUsingPOST: + method: post + resourcePath: /oss + description: registOss + registWorkflowStageUsingPOST: + method: post + resourcePath: /workflowStage + description: registWorkflowStage + registWorkflowStageUsingPOST_1: + method: post + resourcePath: /workflowStageType + description: registWorkflowStage + registWorkflowUsingPOST: + method: post + resourcePath: /workflow + description: registWorkflow + runEventListenerUsingGET: + method: get + resourcePath: /eventlistener/run/{eventListenerIdx} + description: runEventListener + runWorkflowGetUsingGET: + method: get + resourcePath: /workflow/run/{workflowIdx} + description: runWorkflowGet + runWorkflowPostUsingPOST: + method: post + resourcePath: /workflow/run + description: runWorkflowPost + updateEventListnerUsingPATCH: + method: patch + resourcePath: /eventlistener/{eventListenerIdx} + description: updateEventListner + updateOssTypeUsingPATCH: + method: patch + resourcePath: /ossType/{ossTypeIdx} + description: updateOssType + updateOssUsingPATCH: + method: patch + resourcePath: /oss/{ossIdx} + description: updateOss + updateWorkflowStageTypeUsingPATCH: + method: patch + resourcePath: /workflowStageType/{workflowStageTypeIdx} + description: updateWorkflowStageType + updateWorkflowStageUsingPATCH: + method: patch + resourcePath: /workflowStage/{workflowStageIdx} + description: updateWorkflowStage + updateWorkflowUsingPATCH: + method: patch + resourcePath: /workflow/{workflowIdx} + description: updateWorkflow diff --git a/docs/docs.go b/docs/docs.go index f8c60fce..f5ad29ec 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -15,14 +15,43 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/api/call": { - "post": { - "security": [ - { - "BearerAuth": [] - } + "/api/auth/certs": { + "get": { + "description": "Retrieve authentication certificates for MC-IAM-Manager to be used in target frameworks for token validation.", + "consumes": [ + "application/json" ], - "description": "Executes a defined MCMP API action with query parameters and a request body.", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get authentication certificates", + "operationId": "mciamAuthCerts", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/auth/login": { + "post": { + "description": "Authenticate user and issue JWT token.", "consumes": [ "application/json" ], @@ -30,29 +59,41 @@ const docTemplate = `{ "application/json" ], "tags": [ - "McmpAPI" + "auth" ], - "summary": "Call an external MCMP API action (Generic Request)", + "summary": "User login", + "operationId": "mciamLogin", "parameters": [ { - "description": "Generic API Call Request", - "name": "callRequest", + "description": "Login Credentials", + "name": "credentials", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/mcmpapi.ServiceApiCallRequest" + "$ref": "#/definitions/idp.UserLogin" } } ], + "responses": {} + } + }, + "/api/auth/logout": { + "post": { + "description": "Invalidate the user's refresh token and log out.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout user", + "operationId": "mciamLogout", "responses": { "200": { - "description": "External API Response (structure depends on the called API)", - "schema": { - "type": "object" - } - }, - "400": { - "description": "error: Invalid request body or parameters", + "description": "OK", "schema": { "type": "object", "additionalProperties": { @@ -60,17 +101,53 @@ const docTemplate = `{ } } }, - "404": { - "description": "error: Service or action not found", + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { "type": "string" } } + } + } + } + }, + "/api/auth/refresh": { + "post": { + "description": "Refresh JWT access token using a valid refresh token.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Refresh access token", + "operationId": "mciamRefreshToken", + "parameters": [ + { + "description": "Refresh token", + "name": "refresh_token", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "New token information", + "schema": { + "type": "object", + "additionalProperties": true + } }, - "500": { - "description": "error: Internal server error or failed to call external API", + "400": { + "description": "error: Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -78,8 +155,8 @@ const docTemplate = `{ } } }, - "503": { - "description": "error: External API unavailable", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -90,9 +167,9 @@ const docTemplate = `{ } } }, - "/api/permissions": { + "/api/auth/temp-credential-csps": { "get": { - "description": "모든 권한 목록을 조회합니다.", + "description": "Get temporary credential provider information for AWS and GCP", "consumes": [ "application/json" ], @@ -100,23 +177,29 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "auth" ], - "summary": "권한 목록 조회", + "summary": "Get temporary credential CSP information", + "operationId": "mciamGetTempCredentialProviders", "responses": { "200": { - "description": "OK", + "description": "CSP temporary credential information", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Permission" - } + "type": "object", + "additionalProperties": true } } } - }, + } + }, + "/api/auth/validate": { "post": { - "description": "새로운 권한을 생성합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Validate the current access token and refresh if expired", "consumes": [ "application/json" ], @@ -124,33 +207,58 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "auth" ], - "summary": "권한 생성", + "summary": "Validate access token", + "operationId": "mciamValidateToken", "parameters": [ { - "description": "권한 정보", - "name": "permission", + "description": "Refresh token", + "name": "refresh_token", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Permission" + "type": "string" } } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "Token validation result with new token if refreshed", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "error: Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", "schema": { - "$ref": "#/definitions/model.Permission" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } } }, - "/api/permissions/{id}": { + "/api/csp-credentials": { "get": { - "description": "ID로 특정 권한을 조회합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "모든 CSP 인증 정보 목록을 조회합니다", "consumes": [ "application/json" ], @@ -158,29 +266,80 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "csp-credentials" + ], + "summary": "CSP 인증 정보 목록 조회", + "operationId": "mciamListCredentials", + "responses": {} + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "새로운 CSP 인증 정보를 생성합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" ], - "summary": "ID로 권한 조회", + "tags": [ + "csp-credentials" + ], + "summary": "새 CSP 인증 정보 생성", + "operationId": "mciamCreateCredential", + "responses": {} + } + }, + "/api/csp-credentials/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "특정 CSP 인증 정보를 ID로 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 ID로 조회", + "operationId": "mciamGetCredentialByID", "parameters": [ { "type": "string", - "description": "권한 ID", + "description": "Credential ID", "name": "id", "in": "path", "required": true } ], "responses": { - "200": { - "description": "OK", + "404": { + "description": "error: Credential not found", "schema": { - "$ref": "#/definitions/model.Permission" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } }, "put": { - "description": "기존 권한을 수정합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 업데이트합니다", "consumes": [ "application/json" ], @@ -188,38 +347,38 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "csp-credentials" ], - "summary": "권한 수정", + "summary": "CSP 인증 정보 업데이트", + "operationId": "mciamUpdateCredential", "parameters": [ { "type": "string", - "description": "권한 ID", + "description": "Credential ID", "name": "id", "in": "path", "required": true - }, - { - "description": "권한 정보", - "name": "permission", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Permission" - } } ], "responses": { - "200": { - "description": "OK", + "404": { + "description": "error: Credential not found", "schema": { - "$ref": "#/definitions/model.Permission" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } }, "delete": { - "description": "권한을 삭제합니다.", + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 삭제합니다", "consumes": [ "application/json" ], @@ -227,13 +386,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "csp-credentials" ], - "summary": "권한 삭제", + "summary": "CSP 인증 정보 삭제", + "operationId": "mciamDeleteCredential", "parameters": [ { "type": "string", - "description": "권한 ID", + "description": "Credential ID", "name": "id", "in": "path", "required": true @@ -242,13 +402,40 @@ const docTemplate = `{ "responses": { "204": { "description": "No Content" + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } } } } }, - "/api/platform-roles": { - "get": { - "description": "모든 플랫폼 역할을 조회합니다.", + "/api/initial-admin": { + "post": { + "description": "Creates the initial platform admin user with necessary permissions. platform admin 생성인데", "consumes": [ "application/json" ], @@ -256,23 +443,34 @@ const docTemplate = `{ "application/json" ], "tags": [ - "platform-roles" + "admin" + ], + "summary": "Setup initial platform admin", + "operationId": "setupInitialAdmin", + "parameters": [ + { + "description": "Setup Initial Admin Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.SetupInitialAdminRequest" + } + } ], - "summary": "플랫폼 역할 목록 조회", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.PlatformRole" - } + "$ref": "#/definitions/model.Response" } } } - }, + } + }, + "/api/mcmp-api-permission-action-mappings": { "post": { - "description": "새로운 플랫폼 역할을 생성합니다.", + "description": "Creates a new mapping between a permission and an API action", "consumes": [ "application/json" ], @@ -280,33 +478,31 @@ const docTemplate = `{ "application/json" ], "tags": [ - "platform-roles" + "mcmp-api-permission-action-mappings" ], - "summary": "플랫폼 역할 생성", + "summary": "Create permission-action mapping", + "operationId": "createMcmpApiPermissionActionMapping", "parameters": [ { - "description": "Platform Role", - "name": "role", + "description": "Mapping to create", + "name": "mapping", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PlatformRole" + "$ref": "#/definitions/mcmpapi.McmpApiPermissionActionMapping" } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/model.PlatformRole" - } + "204": { + "description": "No Content" } } } }, - "/api/platform-roles/{id}": { - "get": { - "description": "ID로 플랫폼 역할을 조회합니다.", + "/api/mcmp-api-permission-action-mappings/actions/list": { + "post": { + "description": "Returns all workspace actions mapped to a specific permission", "consumes": [ "application/json" ], @@ -314,14 +510,15 @@ const docTemplate = `{ "application/json" ], "tags": [ - "platform-roles" + "mcmp-api-permission-action-mappings" ], - "summary": "플랫폼 역할 조회", + "summary": "Get workspace actions by permission ID", + "operationId": "listWorkspaceActionsByPermissionID", "parameters": [ { - "type": "integer", - "description": "Platform Role ID", - "name": "id", + "type": "string", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true } @@ -330,13 +527,18 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.PlatformRole" + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" + } } } } - }, - "put": { - "description": "기존 플랫폼 역할을 수정합니다.", + } + }, + "/api/mcmp-api-permission-action-mappings/actions/{actionId}/permissions": { + "get": { + "description": "Returns all permissions mapped to a specific API action", "consumes": [ "application/json" ], @@ -344,67 +546,35 @@ const docTemplate = `{ "application/json" ], "tags": [ - "platform-roles" + "mcmp-api-permission-action-mappings" ], - "summary": "플랫폼 역할 수정", + "summary": "Get permissions by action ID", + "operationId": "listPermissionsByActionID", "parameters": [ { "type": "integer", - "description": "Platform Role ID", - "name": "id", + "description": "Action ID", + "name": "actionId", "in": "path", "required": true - }, - { - "description": "Platform Role", - "name": "role", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.PlatformRole" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.PlatformRole" + "type": "array", + "items": { + "type": "string" + } } } } - }, - "delete": { - "description": "플랫폼 역할을 삭제합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "platform-roles" - ], - "summary": "플랫폼 역할 삭제", - "parameters": [ - { - "type": "integer", - "description": "Platform Role ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - } } }, - "/api/roles/{roleType}/{roleId}/permissions": { - "get": { - "description": "특정 역할의 권한 목록을 조회합니다.", + "/api/mcmp-api-permission-action-mappings/list": { + "post": { + "description": "Returns all platform actions mapped to a specific permission", "consumes": [ "application/json" ], @@ -412,21 +582,15 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "mcmp-api-permission-action-mappings" ], - "summary": "역할의 권한 목록 조회", + "summary": "List platform actions by permission ID", + "operationId": "listPlatformActions", "parameters": [ { "type": "string", - "description": "역할 타입 ('platform' or 'workspace')", - "name": "roleType", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "역할 ID", - "name": "roleId", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true } @@ -437,16 +601,16 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/model.Permission" + "$ref": "#/definitions/mcmpapi.McmpApiAction" } } } } } }, - "/api/roles/{roleType}/{roleId}/permissions/{permissionId}": { - "post": { - "description": "역할에 권한을 할당합니다.", + "/api/mcmp-api-permission-action-mappings/permissions/{permissionId}/actions/{actionId}": { + "put": { + "description": "Updates an existing mapping between a permission and an API action", "consumes": [ "application/json" ], @@ -454,40 +618,49 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "mcmp-api-permission-action-mappings" ], - "summary": "역할에 권한 할당", + "summary": "Update permission-action mapping", + "operationId": "updateMapping", "parameters": [ { "type": "string", - "description": "역할 타입 ('platform' or 'workspace')", - "name": "roleType", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true }, { "type": "integer", - "description": "역할 ID", - "name": "roleId", + "description": "Action ID", + "name": "actionId", "in": "path", "required": true }, { - "type": "string", - "description": "권한 ID", - "name": "permissionId", - "in": "path", - "required": true + "description": "Updated mapping", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/mcmpapi.McmpApiPermissionActionMapping" + } } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } } } }, "delete": { - "description": "역할에서 권한을 제거합니다.", + "description": "Deletes a mapping between a permission and an API action", "consumes": [ "application/json" ], @@ -495,28 +668,22 @@ const docTemplate = `{ "application/json" ], "tags": [ - "permissions" + "mcmp-api-permission-action-mappings" ], - "summary": "역할에서 권한 제거", + "summary": "Delete permission-action mapping", + "operationId": "deleteMapping", "parameters": [ { "type": "string", - "description": "역할 타입 ('platform' or 'workspace')", - "name": "roleType", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true }, { "type": "integer", - "description": "역할 ID", - "name": "roleId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "권한 ID", - "name": "permissionId", + "description": "Action ID", + "name": "actionId", "in": "path", "required": true } @@ -528,9 +695,9 @@ const docTemplate = `{ } } }, - "/auth/login": { - "post": { - "description": "사용자 ID와 비밀번호로 로그인하여 JWT 토큰을 발급받습니다.", + "/api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions": { + "get": { + "description": "Returns all platform actions mapped to a specific permission", "consumes": [ "application/json" ], @@ -538,69 +705,34 @@ const docTemplate = `{ "application/json" ], "tags": [ - "auth" + "mcmp-api-permission-action-mappings" ], - "summary": "로그인", + "summary": "Get platform actions by permission ID", + "operationId": "getPlatformActionsByPermissionID", "parameters": [ { - "description": "로그인 정보 (Id, Password)", - "name": "login", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/idp.UserLogin" - } + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "로그인 성공 및 토큰 정보 (gocloak.JWT 구조체와 유사)", - "schema": { - "type": "object", - "additionalProperties": true - } - }, - "400": { - "description": "error: 잘못된 요청 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "error: 인증 실패 (자격 증명 오류)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "403": { - "description": "error: 계정이 비활성화되었거나 승인 대기 중입니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류 (Keycloak 통신, DB 동기화 등)", + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" } } } } } }, - "/mcmp-apis": { - "get": { + "/api/mcmp-apis/list": { + "post": { "security": [ { "BearerAuth": [] @@ -617,6 +749,7 @@ const docTemplate = `{ "McmpAPI" ], "summary": "Get All Stored MCMP API Definitions", + "operationId": "listServicesAndActions", "parameters": [ { "type": "string", @@ -650,7 +783,7 @@ const docTemplate = `{ } } }, - "/mcmp-apis/call": { + "/api/mcmp-apis/mcmpApiCall": { "post": { "security": [ { @@ -668,6 +801,7 @@ const docTemplate = `{ "McmpAPI" ], "summary": "Call an external MCMP API action (Structured Request)", + "operationId": "mcmpApiCall", "parameters": [ { "description": "API Call Request", @@ -675,7 +809,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/mcmpapi.McmpApiCallRequest" + "$ref": "#/definitions/model.McmpApiCallRequest" } } ], @@ -725,109 +859,7 @@ const docTemplate = `{ } } }, - "/mcmp-apis/sync": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Triggers the synchronization of MCMP API definitions from the configured YAML URL to the database.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "McmpAPI // Updated tag" - ], - "summary": "Sync MCMP API Definitions", - "responses": { - "200": { - "description": "message: Successfully triggered MCMP API sync\" // Updated message", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "message: Failed to trigger MCMP API sync\" // Updated message", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/mcmp-apis/test/mc-infra-manager/getallns": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Calls the GetAllNs action of the mc-infra-manager service via the CallApi service.", - "produces": [ - "application/json" - ], - "tags": [ - "McmpAPI", - "Test" - ], - "summary": "Test Call to mc-infra-manager GetAllNs", - "responses": { - "200": { - "description": "Response from mc-infra-manager GetAllNs", - "schema": { - "type": "object" - } - }, - "400": { - "description": "error: Bad Request (e.g., invalid parameters)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: Service or Action Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "503": { - "description": "error: External API Service Unavailable", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/mcmp-apis/{serviceName}": { + "/api/mcmp-apis/name/{serviceName}": { "put": { "security": [ { @@ -845,6 +877,7 @@ const docTemplate = `{ "McmpAPI" ], "summary": "Update MCMP API Service Definition", + "operationId": "UpdateFrameworkService", "parameters": [ { "type": "string", @@ -903,7 +936,7 @@ const docTemplate = `{ } } }, - "/mcmp-apis/{serviceName}/versions/{version}/activate": { + "/api/mcmp-apis/name/{serviceName}/versions/{version}/activate": { "put": { "security": [ { @@ -921,6 +954,7 @@ const docTemplate = `{ "McmpAPI" ], "summary": "Set Active Version for a Service", + "operationId": "setActiveVersion", "parameters": [ { "type": "string", @@ -971,14 +1005,14 @@ const docTemplate = `{ } } }, - "/menus": { - "get": { + "/api/mcmp-apis/syncMcmpAPIs": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "현재 로그인한 사용자의 Platform Role에 따라 접근 가능한 메뉴 목록을 트리 구조로 조회합니다.", + "description": "Triggers the synchronization of MCMP API definitions from the configured YAML URL to the database.", "consumes": [ "application/json" ], @@ -986,21 +1020,13 @@ const docTemplate = `{ "application/json" ], "tags": [ - "menus" + "McmpAPI" ], - "summary": "현재 사용자의 메뉴 트리 조회", + "summary": "Sync MCMP API Definitions", + "operationId": "syncMcmpAPIs", "responses": { "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.MenuTreeNode" - } - } - }, - "401": { - "description": "error: Unauthorized", + "description": "message: Successfully triggered MCMP API sync", "schema": { "type": "object", "additionalProperties": { @@ -1009,7 +1035,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "message: Failed to trigger MCMP API sync", "schema": { "type": "object", "additionalProperties": { @@ -1018,75 +1044,34 @@ const docTemplate = `{ } } } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "새로운 메뉴를 생성합니다", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "menus" - ], - "summary": "새 메뉴 생성", - "parameters": [ - { - "description": "Menu Info", - "name": "menu", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Menu" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/model.Menu" - } - } - } } }, - "/menus/all": { + "/api/mcmp-apis/test/mc-infra-manager/getallns": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "모든 메뉴 목록을 트리 구조로 조회합니다. 관리자 권한이 필요합니다.", - "consumes": [ - "application/json" - ], + "description": "Calls the GetAllNs action of the mc-infra-manager service via the CallApi service.", "produces": [ "application/json" ], "tags": [ - "menus" + "McmpAPI", + "Test" ], - "summary": "모든 메뉴 트리 조회 (관리자용)", + "summary": "Test Call to mc-infra-manager GetAllNs", + "operationId": "testCallGetAllNs", "responses": { "200": { - "description": "OK", + "description": "Response from mc-infra-manager GetAllNs", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.MenuTreeNode" - } + "type": "object" } }, - "401": { - "description": "error: Unauthorized", + "400": { + "description": "error: Bad Request (e.g., invalid parameters)", "schema": { "type": "object", "additionalProperties": { @@ -1094,8 +1079,8 @@ const docTemplate = `{ } } }, - "403": { - "description": "error: Forbidden", + "404": { + "description": "error: Service or Action Not Found", "schema": { "type": "object", "additionalProperties": { @@ -1104,59 +1089,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/menus/register-from-body": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "요청 본문에 포함된 YAML 텍스트를 파싱하여 메뉴를 데이터베이스에 등록하거나 업데이트합니다. Content-Type은 text/plain, text/yaml, application/yaml 등을 권장합니다.", - "consumes": [ - "text/plain" - ], - "produces": [ - "application/json" - ], - "tags": [ - "menus" - ], - "summary": "요청 본문의 YAML 내용으로 메뉴 등록/업데이트", - "parameters": [ - { - "example": "\"menus:\\n - id: new-item\\n parentid: dashboard\\n displayname: New Menu Item\\n restype: menu\\n isaction: false\\n priority: 10\\n menunumber: 9999\"", - "description": "Menu definitions in YAML format (must contain 'menus:' root key)", - "name": "yaml", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "message: Successfully registered menus from request body", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "error: 잘못된 요청 본문 또는 YAML 형식 오류", + "description": "error: Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1164,8 +1097,8 @@ const docTemplate = `{ } } }, - "500": { - "description": "error: 서버 내부 오류", + "503": { + "description": "error: External API Service Unavailable", "schema": { "type": "object", "additionalProperties": { @@ -1176,14 +1109,14 @@ const docTemplate = `{ } } }, - "/menus/register-from-yaml": { + "/api/menus": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "filePath 쿼리 파라미터로 지정된 로컬 YAML 파일 또는 파라미터가 없을 경우 .env 파일의 MCWEBCONSOLE_MENUYAML URL에서 메뉴를 가져와 데이터베이스에 등록/업데이트합니다. URL에서 가져올 경우 asset/menu/menu.yaml에 저장됩니다.", + "description": "Create a new menu", "consumes": [ "application/json" ], @@ -1193,45 +1126,37 @@ const docTemplate = `{ "tags": [ "menus" ], - "summary": "YAML 파일 또는 URL에서 메뉴 등록/업데이트", + "summary": "Create new menu", + "operationId": "createMenu", "parameters": [ { - "type": "string", - "description": "YAML 파일 경로 (선택 사항, 없으면 .env의 URL 또는 기본 로컬 경로 사용)", - "name": "filePath", - "in": "query" + "description": "Menu Info", + "name": "menu", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Menu" + } } ], "responses": { - "200": { - "description": "message: Successfully registered menus from YAML", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 실패 메시지", + "201": { + "description": "Created", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/model.Menu" } } } } }, - "/menus/{id}": { - "get": { + "/api/menus/id/{menuId}": { + "put": { "security": [ { "BearerAuth": [] } ], - "description": "특정 메뉴를 ID로 조회합니다", + "description": "Update menu information", "consumes": [ "application/json" ], @@ -1241,7 +1166,8 @@ const docTemplate = `{ "tags": [ "menus" ], - "summary": "메뉴 ID로 조회", + "summary": "Update menu information", + "operationId": "updateMenu", "parameters": [ { "type": "string", @@ -1249,6 +1175,15 @@ const docTemplate = `{ "name": "id", "in": "path", "required": true + }, + { + "description": "Menu Info", + "name": "menu", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Menu" + } } ], "responses": { @@ -1260,13 +1195,13 @@ const docTemplate = `{ } } }, - "put": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "메뉴 정보를 업데이트합니다", + "description": "Get menu details by ID", "consumes": [ "application/json" ], @@ -1276,23 +1211,15 @@ const docTemplate = `{ "tags": [ "menus" ], - "summary": "메뉴 정보 업데이트", + "summary": "Get menu by ID", + "operationId": "getMenuByID", "parameters": [ { "type": "string", "description": "Menu ID", - "name": "id", + "name": "menuId", "in": "path", "required": true - }, - { - "description": "Menu Info", - "name": "menu", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Menu" - } } ], "responses": { @@ -1310,7 +1237,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "메뉴를 삭제합니다", + "description": "Delete a menu", "consumes": [ "application/json" ], @@ -1320,7 +1247,8 @@ const docTemplate = `{ "tags": [ "menus" ], - "summary": "메뉴 삭제", + "summary": "Delete menu", + "operationId": "deleteMenu", "parameters": [ { "type": "string", @@ -1337,14 +1265,14 @@ const docTemplate = `{ } } }, - "/projects": { - "get": { + "/api/menus/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "모든 프로젝트 목록을 조회합니다 (연결된 워크스페이스 정보 포함).", + "description": "List all menus as a tree structure. Admin permission required.", "consumes": [ "application/json" ], @@ -1352,16 +1280,35 @@ const docTemplate = `{ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "모든 프로젝트 조회", + "summary": "List all menus", + "operationId": "listMenus", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.MenuTreeNode" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" } } }, @@ -1375,14 +1322,16 @@ const docTemplate = `{ } } } - }, + } + }, + "/api/menus/platform-roles": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "새로운 프로젝트를 생성합니다.", + "description": "Create a new menu mapping", "consumes": [ "application/json" ], @@ -1390,17 +1339,18 @@ const docTemplate = `{ "application/json" ], "tags": [ - "projects" + "menu" ], - "summary": "프로젝트 생성", + "summary": "Create menu mapping", + "operationId": "createMenusRolesMapping", "parameters": [ { - "description": "프로젝트 정보 (ID, CreatedAt, UpdatedAt, Workspaces 제외)", - "name": "project", + "description": "Menu Mapping", + "name": "mapping", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.CreateMenuMappingRequest" } } ], @@ -1408,11 +1358,14 @@ const docTemplate = `{ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/model.Project" + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "400": { - "description": "error: 잘못된 요청 형식", + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -1421,7 +1374,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1430,16 +1383,14 @@ const docTemplate = `{ } } } - } - }, - "/projects/name/{name}": { - "get": { + }, + "delete": { "security": [ { "BearerAuth": [] } ], - "description": "이름으로 특정 프로젝트를 조회합니다 (연결된 워크스페이스 정보 포함).", + "description": "Delete the mapping between a platform role and a menu.", "consumes": [ "application/json" ], @@ -1447,27 +1398,36 @@ const docTemplate = `{ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "이름으로 프로젝트 조회", + "summary": "Delete platform role-menu mapping", + "operationId": "deleteMenusRolesMapping", "parameters": [ { "type": "string", - "description": "프로젝트 이름", - "name": "name", - "in": "path", - "required": true + "description": "Platform Role ID", + "name": "roleId", + "in": "query" + }, + { + "type": "string", + "description": "Menu ID", + "name": "menuId", + "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "message: Menu mapping deleted successfully", "schema": { - "$ref": "#/definitions/model.Project" + "type": "object", + "additionalProperties": { + "type": "string" + } } }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "400": { + "description": "error: platform role and menu ID are required", "schema": { "type": "object", "additionalProperties": { @@ -1487,14 +1447,14 @@ const docTemplate = `{ } } }, - "/projects/{id}": { - "get": { + "/api/menus/platform-roles/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "ID로 특정 프로젝트를 조회합니다 (연결된 워크스페이스 정보 포함).", + "description": "List menus mapped to a specific platform role.", "consumes": [ "application/json" ], @@ -1502,36 +1462,36 @@ const docTemplate = `{ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "ID로 프로젝트 조회", + "summary": "List menus mapped to platform role", + "operationId": "listMappedMenusByRole", "parameters": [ { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/model.Project" - } + "type": "string", + "description": "Platform Role ID", + "name": "roleId", + "in": "query" }, - "400": { - "description": "error: 잘못된 프로젝트 ID", + { + "type": "string", + "description": "Menu ID", + "name": "menuId", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/model.Menu" } } }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "400": { + "description": "error: platform role is required", "schema": { "type": "object", "additionalProperties": { @@ -1549,14 +1509,16 @@ const docTemplate = `{ } } } - }, - "put": { + } + }, + "/api/menus/setup/initial-menus": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "기존 프로젝트 정보를 부분적으로 수정합니다.", + "description": "Register or update menus from a local YAML file specified by the filePath query parameter, or from the MCWEBCONSOLE_MENUYAML URL in .env if not provided. If loaded from URL, the file is saved to asset/menu/menu.yaml.", "consumes": [ "application/json" ], @@ -1564,45 +1526,21 @@ const docTemplate = `{ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "프로젝트 수정", + "summary": "Register/Update menus from YAML file or URL", + "operationId": "registerMenusFromYAML", "parameters": [ { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "수정할 필드와 값 (예: {\\", - "name": "updates", - "in": "body", - "required": true, - "schema": { - "type": "object" - } + "type": "string", + "description": "YAML file path (optional, uses .env URL or default local path if not provided)", + "name": "filePath", + "in": "query" } ], "responses": { "200": { - "description": "업데이트된 프로젝트 정보", - "schema": { - "$ref": "#/definitions/model.Project" - } - }, - "400": { - "description": "error: 잘못된 요청 형식 또는 ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "description": "message: Successfully registered menus from YAML", "schema": { "type": "object", "additionalProperties": { @@ -1611,7 +1549,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "error: 실패 메시지", "schema": { "type": "object", "additionalProperties": { @@ -1620,39 +1558,42 @@ const docTemplate = `{ } } } - }, - "delete": { + } + }, + "/api/menus/setup/initial-menus2": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "프로젝트를 삭제합니다. 연결된 워크스페이스와의 관계도 해제됩니다.", + "description": "Parse YAML text in the request body and register or update menus in the database. Recommended Content-Type: text/plain, text/yaml, application/yaml.", "consumes": [ - "application/json" + "text/plain" ], "produces": [ "application/json" ], "tags": [ - "projects" + "menus" ], - "summary": "프로젝트 삭제", + "summary": "Register/Update menus from YAML in request body", + "operationId": "registerMenusFromBody", "parameters": [ { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true + "example": "\"menus:\\n - id: new-item\\n parentid: dashboard\\n displayname: New Menu Item\\n restype: menu\\n isaction: false\\n priority: 10\\n menunumber: 9999\"", + "description": "Menu definitions in YAML format (must contain 'menus:' root key)", + "name": "yaml", + "in": "body", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 프로젝트 ID", + "200": { + "description": "message: Successfully registered menus from request body", "schema": { "type": "object", "additionalProperties": { @@ -1660,8 +1601,8 @@ const docTemplate = `{ } } }, - "404": { - "description": "error: 프로젝트를 찾을 수 없습니다", + "400": { + "description": "error: 잘못된 요청 본문 또는 YAML 형식 오류", "schema": { "type": "object", "additionalProperties": { @@ -1681,14 +1622,14 @@ const docTemplate = `{ } } }, - "/projects/{id}/workspaces/{workspaceId}": { + "/api/menus/tree/list": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 프로젝트에 워크스페이스를 연결합니다.", + "description": "List all menus as a tree structure. Admin permission required.", "consumes": [ "application/json" ], @@ -1696,31 +1637,22 @@ const docTemplate = `{ "application/json" ], "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } + "menus" ], + "summary": "List all menus Tree", + "operationId": "listMenusTree", "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.MenuTreeNode" + } + } }, - "400": { - "description": "error: 잘못된 ID 형식", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -1728,8 +1660,8 @@ const docTemplate = `{ } } }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -1747,14 +1679,16 @@ const docTemplate = `{ } } } - }, - "delete": { + } + }, + "/api/menus/user-menu-tree": { + "get": { "security": [ { "BearerAuth": [] } ], - "description": "특정 프로젝트에서 워크스페이스 연결을 해제합니다.", + "description": "Get menu tree based on user's platform roles", "consumes": [ "application/json" ], @@ -1762,40 +1696,22 @@ const docTemplate = `{ "application/json" ], "tags": [ - "projects" - ], - "summary": "프로젝트에서 워크스페이스 연결 해제", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } + "menus" ], + "summary": "Get user menu tree by platform roles", + "operationId": "getUserMenuTree", "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", + "200": { + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/model.MenuTreeNode" } } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1806,70 +1722,101 @@ const docTemplate = `{ } } }, - "/readyz": { - "get": { - "description": "애플리케이션의 준비 상태를 확인합니다. status=detail 쿼리 파라미터로 상세 상태를 확인할 수 있습니다.", + "/api/permissions/mciam": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new permission with the specified information.", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Health" + "permissions" ], - "summary": "애플리케이션 준비 상태 확인", + "summary": "Create new permission", + "operationId": "createMciamPermission", "parameters": [ { - "type": "string", - "description": "상세 상태 확인 여부 ('detail')", - "name": "status", - "in": "query" + "description": "Permission Info", + "name": "permission", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.MciamPermission" + } } ], "responses": { - "200": { - "description": "상세 상태 정보 (status=detail)", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/service.HealthStatus" + "$ref": "#/definitions/model.MciamPermission" } }, - "503": { - "description": "상세 상태 확인 중 오류 발생 시", + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/service.HealthStatus" + "type": "object", + "additionalProperties": { + "type": "string" + } } } } } }, - "/user/workspaces": { + "/api/permissions/mciam/id/{id}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "현재 로그인한 사용자가 접근 가능한 워크스페이스 및 각 워크스페이스에서의 역할 목록을 조회합니다.", + "description": "Retrieve permission details by permission ID.", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "users", - "workspaces", - "roles", - "me" + "permissions" + ], + "summary": "Get permission by ID", + "operationId": "getMciamPermissionByID", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true + } ], - "summary": "내 워크스페이스 및 역할 목록 조회", "responses": { "200": { - "description": "성공 시 워크스페이스 및 역할 정보 목록 반환", + "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/service.WorkspaceRoleInfo" - } + "$ref": "#/definitions/model.MciamPermission" } }, - "401": { - "description": "error: Unauthorized", + "404": { + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -1878,7 +1825,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1889,51 +1836,37 @@ const docTemplate = `{ } } }, - "/users": { - "get": { + "/api/permissions/mciam/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "모든 사용자 목록을 조회합니다. 'admin' 또는 'platformAdmin' 역할이 필요합니다.", + "description": "Retrieve a list of all permissions.", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "users" + "permissions" ], - "summary": "사용자 목록 조회 (관리자용)", + "summary": "List all permissions", + "operationId": "listMciamPermissions", "responses": { "200": { - "description": "성공 시 사용자 목록 반환", + "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/model.User" - } - } - }, - "401": { - "description": "error: Unauthorized", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "403": { - "description": "error: Forbidden (권한 부족)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" + "$ref": "#/definitions/model.MciamPermission" } } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -1942,43 +1875,16 @@ const docTemplate = `{ } } } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "responses": {} } }, - "/users/{id}": { + "/api/permissions/mciam/{id}": { "put": { "security": [ { "BearerAuth": [] } ], - "parameters": [ - { - "type": "integer", - "description": "User DB ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": {} - } - }, - "/users/{id}/approve": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "지정된 사용자를 활성화하고 시스템 사용을 승인합니다. 'admin' 또는 'platformadmin' 역할이 필요합니다.", + "description": "Update the details of an existing permission.", "consumes": [ "application/json" ], @@ -1986,33 +1892,37 @@ const docTemplate = `{ "application/json" ], "tags": [ - "users" + "permissions" ], - "summary": "사용자 승인 (관리자용)", + "summary": "Update permission", + "operationId": "updateMciamPermission", "parameters": [ { "type": "string", - "description": "사용자 Keycloak ID", - "name": "id", + "description": "Permission ID", + "name": "permissionId", "in": "path", "required": true + }, + { + "description": "Permission Info", + "name": "permission", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.MciamPermission" + } } ], "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 사용자 ID", + "200": { + "description": "OK", "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/model.MciamPermission" } }, - "401": { - "description": "error: Unauthorized", + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2020,8 +1930,8 @@ const docTemplate = `{ } } }, - "403": { - "description": "error: Forbidden (권한 부족)", + "404": { + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2030,7 +1940,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2039,16 +1949,14 @@ const docTemplate = `{ } } } - } - }, - "/workspaces": { - "get": { + }, + "delete": { "security": [ { "BearerAuth": [] } ], - "description": "모든 워크스페이스 목록을 조회합니다 (연결된 프로젝트 정보 포함).", + "description": "Delete a permission by its ID.", "consumes": [ "application/json" ], @@ -2056,21 +1964,34 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "permissions" + ], + "summary": "Delete permission", + "operationId": "deleteMciamPermission", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true + } ], - "summary": "모든 워크스페이스 조회", "responses": { - "200": { - "description": "OK", + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Workspace" + "type": "object", + "additionalProperties": { + "type": "string" } } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2079,14 +2000,16 @@ const docTemplate = `{ } } } - }, + } + }, + "/api/projects": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "새로운 워크스페이스를 생성합니다.", + "description": "Create a new project with the specified information.", "consumes": [ "application/json" ], @@ -2094,17 +2017,18 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "워크스페이스 생성", + "summary": "Create new project", + "operationId": "createProject", "parameters": [ { - "description": "워크스페이스 정보 (ID, CreatedAt, UpdatedAt, Projects 제외)", - "name": "workspace", + "description": "Project Info", + "name": "project", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.Project" } } ], @@ -2112,11 +2036,11 @@ const docTemplate = `{ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.Project" } }, "400": { - "description": "error: 잘못된 요청 형식", + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2125,7 +2049,48 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/projects/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of all projects.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "List all projects", + "operationId": "listProjects", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2136,14 +2101,14 @@ const docTemplate = `{ } } }, - "/workspaces/name/{name}": { + "/api/projects/name/{projectName}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "이름으로 특정 워크스페이스를 조회합니다 (연결된 프로젝트 정보 포함).", + "description": "Get project details by name", "consumes": [ "application/json" ], @@ -2151,13 +2116,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "이름으로 워크스페이스 조회", + "summary": "Get project by name", + "operationId": "getProjectByName", "parameters": [ { "type": "string", - "description": "워크스페이스 이름", + "description": "Project Name", "name": "name", "in": "path", "required": true @@ -2167,11 +2133,11 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.Project" } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2180,7 +2146,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2191,14 +2157,14 @@ const docTemplate = `{ } } }, - "/workspaces/{id}": { + "/api/projects/{id}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "ID로 특정 워크스페이스를 조회합니다 (연결된 프로젝트 정보 포함).", + "description": "Retrieve project details by project ID.", "consumes": [ "application/json" ], @@ -2206,14 +2172,15 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "ID로 워크스페이스 조회", + "summary": "Get project by ID", + "operationId": "getProjectByID", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", + "type": "string", + "description": "Project ID", + "name": "projectId", "in": "path", "required": true } @@ -2222,20 +2189,11 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/model.Workspace" - } - }, - "400": { - "description": "error: 잘못된 워크스페이스 ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/model.Project" } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2244,7 +2202,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2260,7 +2218,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "기존 워크스페이스 정보를 부분적으로 수정합니다.", + "description": "Update the details of an existing project.", "consumes": [ "application/json" ], @@ -2268,36 +2226,37 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "워크스페이스 수정", + "summary": "Update project", + "operationId": "updateProject", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", + "type": "string", + "description": "Project ID", + "name": "projectId", "in": "path", "required": true }, { - "description": "수정할 필드와 값 (예: {\\", - "name": "updates", + "description": "Project Info", + "name": "project", "in": "body", "required": true, "schema": { - "type": "object" + "$ref": "#/definitions/model.Project" } } ], "responses": { "200": { - "description": "업데이트된 워크스페이스 정보", + "description": "OK", "schema": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.Project" } }, "400": { - "description": "error: 잘못된 요청 형식 또는 ID", + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2306,7 +2265,7 @@ const docTemplate = `{ } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2315,7 +2274,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2331,7 +2290,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "워크스페이스를 삭제합니다. 연결된 프로젝트와의 관계도 해제됩니다.", + "description": "Delete a project by its ID.", "consumes": [ "application/json" ], @@ -2339,14 +2298,15 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "워크스페이스 삭제", + "summary": "Delete project", + "operationId": "deleteProject", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", + "type": "string", + "description": "Project ID", + "name": "projectId", "in": "path", "required": true } @@ -2355,17 +2315,8 @@ const docTemplate = `{ "204": { "description": "No Content" }, - "400": { - "description": "error: 잘못된 워크스페이스 ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2374,7 +2325,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2385,14 +2336,14 @@ const docTemplate = `{ } } }, - "/workspaces/{id}/projects": { - "get": { + "/api/projects/{id}/workspaces/{workspaceId}": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스 ID에 연결된 모든 프로젝트 목록을 조회합니다.", + "description": "프로젝트에 워크스페이스를 연결합니다.", "consumes": [ "application/json" ], @@ -2400,30 +2351,32 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "projects" ], - "summary": "워크스페이스에 연결된 프로젝트 목록 조회", + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", "parameters": [ { "type": "integer", - "description": "워크스페이스 ID", + "description": "프로젝트 ID", "name": "id", "in": "path", "required": true + }, + { + "type": "integer", + "description": "워크스페이스 ID", + "name": "workspaceId", + "in": "path", + "required": true } ], "responses": { - "200": { - "description": "성공 시 프로젝트 목록 반환", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Project" - } - } + "204": { + "description": "No Content" }, "400": { - "description": "error: 잘못된 워크스페이스 ID", + "description": "error: 잘못된 ID 형식", "schema": { "type": "object", "additionalProperties": { @@ -2432,7 +2385,7 @@ const docTemplate = `{ } }, "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", "schema": { "type": "object", "additionalProperties": { @@ -2452,14 +2405,14 @@ const docTemplate = `{ } } }, - "/workspaces/{id}/projects/{projectId}": { + "/api/resource-types/cloud-resources": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스에 프로젝트를 연결합니다.", + "description": "새로운 리소스 타입을 생성합니다", "consumes": [ "application/json" ], @@ -2467,31 +2420,30 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "resource-types" ], - "summary": "워크스페이스에 프로젝트 연결", + "summary": "Cloud에서 관리되는 Resource(vm, nlb, k8s 등의 그룹) 새 리소스 타입 생성", + "operationId": "createResourceType", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "프로젝트 ID", - "name": "projectId", - "in": "path", - "required": true + "description": "Resource Type Info", + "name": "resourceType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.ResourceType" + } } ], "responses": { - "204": { - "description": "No Content" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.ResourceType" + } }, "400": { - "description": "error: 잘못된 ID 형식", + "description": "error: Invalid request", "schema": { "type": "object", "additionalProperties": { @@ -2499,8 +2451,8 @@ const docTemplate = `{ } } }, - "404": { - "description": "error: 워크스페이스 또는 프로젝트를 찾을 수 없습니다", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2508,8 +2460,8 @@ const docTemplate = `{ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -2518,14 +2470,16 @@ const docTemplate = `{ } } } - }, - "delete": { + } + }, + "/api/resource-types/cloud-resources/framework/:frameworkId/id/:resourceTypeId": { + "get": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스에서 프로젝트 연결을 해제합니다.", + "description": "특정 리소스 타입을 ID로 조회합니다", "consumes": [ "application/json" ], @@ -2533,31 +2487,28 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces" + "resource-types" ], - "summary": "워크스페이스에서 프로젝트 연결 해제", + "summary": "리소스 타입 ID로 조회", + "operationId": "getCloudResourceTypeByID", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", + "type": "string", + "description": "Resource Type ID", "name": "id", "in": "path", "required": true - }, - { - "type": "integer", - "description": "프로젝트 ID", - "name": "projectId", - "in": "path", - "required": true } ], "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.ResourceType" + } + }, + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2565,8 +2516,17 @@ const docTemplate = `{ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Resource Type not found", "schema": { "type": "object", "additionalProperties": { @@ -2575,16 +2535,14 @@ const docTemplate = `{ } } } - } - }, - "/workspaces/{id}/users": { - "get": { + }, + "put": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스에 속한 모든 사용자와 각 사용자의 역할을 조회합니다.", + "description": "리소스 타입 정보를 업데이트합니다", "consumes": [ "application/json" ], @@ -2592,32 +2550,37 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces", - "users", - "roles" + "resource-types" ], - "summary": "워크스페이스 사용자 및 역할 목록 조회", + "summary": "리소스 타입 업데이트", + "operationId": "updateResourceType", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", + "type": "string", + "description": "Resource Type ID", "name": "id", "in": "path", "required": true + }, + { + "description": "Resource Type Info", + "name": "resourceType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.ResourceType" + } } ], "responses": { "200": { - "description": "성공 시 사용자 및 역할 목록 반환", + "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/service.UserWithRoles" - } + "$ref": "#/definitions/model.ResourceType" } }, "400": { - "description": "error: 잘못된 워크스페이스 ID", + "description": "error: Invalid request", "schema": { "type": "object", "additionalProperties": { @@ -2625,8 +2588,8 @@ const docTemplate = `{ } } }, - "404": { - "description": "error: 워크스페이스를 찾을 수 없습니다", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2634,8 +2597,17 @@ const docTemplate = `{ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Resource Type not found", "schema": { "type": "object", "additionalProperties": { @@ -2644,16 +2616,14 @@ const docTemplate = `{ } } } - } - }, - "/workspaces/{workspaceId}/users/{userId}/roles/{roleId}": { - "post": { + }, + "delete": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스 내의 사용자에게 특정 워크스페이스 역할을 할당합니다.", + "description": "리소스 타입을 삭제합니다", "consumes": [ "application/json" ], @@ -2661,30 +2631,15 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces", - "roles", - "users" + "resource-types" ], - "summary": "워크스페이스 사용자에게 역할 할당", + "summary": "리소스 타입 삭제", + "operationId": "deleteResourceType", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "사용자 DB ID (db_id)", - "name": "userId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 역할 ID", - "name": "roleId", + "type": "string", + "description": "Resource Type ID", + "name": "id", "in": "path", "required": true } @@ -2693,8 +2648,17 @@ const docTemplate = `{ "204": { "description": "No Content" }, - "400": { - "description": "error: 잘못된 ID 형식", + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -2703,16 +2667,48 @@ const docTemplate = `{ } }, "404": { - "description": "error: 사용자, 역할 또는 워크스페이스를 찾을 수 없습니다", + "description": "error: Resource Type not found", "schema": { "type": "object", "additionalProperties": { "type": "string" } } + } + } + } + }, + "/api/resource-types/cloud-resources/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "모든 리소스 타입 목록을 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "resource-types" + ], + "summary": "리소스 타입 목록 조회", + "operationId": "listCloudResourceTypes", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.ResourceType" + } + } }, - "409": { - "description": "error: 역할이 해당 워크스페이스에 속하지 않음", + "401": { + "description": "error: Unauthorized", "schema": { "type": "object", "additionalProperties": { @@ -2720,8 +2716,8 @@ const docTemplate = `{ } } }, - "500": { - "description": "error: 서버 내부 오류", + "403": { + "description": "error: Forbidden", "schema": { "type": "object", "additionalProperties": { @@ -2730,14 +2726,16 @@ const docTemplate = `{ } } } - }, - "delete": { + } + }, + "/api/roles": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "특정 워크스페이스 내의 사용자에게서 특정 워크스페이스 역할을 제거합니다.", + "description": "Create a new role", "consumes": [ "application/json" ], @@ -2745,40 +2743,91 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspaces", - "roles", - "users" + "roles" ], - "summary": "워크스페이스 사용자 역할 제거", + "summary": "Create role", + "operationId": "createRole", "parameters": [ { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true + "description": "Role Info", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } }, - { - "type": "integer", - "description": "사용자 DB ID (db_id)", - "name": "userId", - "in": "path", - "required": true + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/assign/platform-role": { + "post": { + "security": [ { - "type": "integer", - "description": "워크스페이스 역할 ID", - "name": "roleId", - "in": "path", - "required": true + "BearerAuth": [] + } + ], + "description": "Assign a platform role to a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Assign platform role", + "operationId": "assignPlatformRole", + "parameters": [ + { + "description": "Platform Role Assignment Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } }, "400": { - "description": "error: 잘못된 ID 형식", + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2786,8 +2835,51 @@ const docTemplate = `{ } } }, - "404": { - "description": "error: 역할 또는 워크스페이스를 찾을 수 없습니다\" // User existence check is optional here", + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/assign/workspace-role": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Assign a workspace role to a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Assign workspace role", + "operationId": "assignWorkspaceRole", + "parameters": [ + { + "description": "Workspace Role Assignment Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignWorkspaceRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", "schema": { "type": "object", "additionalProperties": { @@ -2795,8 +2887,8 @@ const docTemplate = `{ } } }, - "409": { - "description": "error: 역할이 해당 워크스페이스에 속하지 않음", + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -2805,7 +2897,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: 서버 내부 오류", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2815,176 +2907,4718 @@ const docTemplate = `{ } } } - } - }, - "definitions": { - "idp.UserLogin": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "password": { - "type": "string" + }, + "/api/roles/csp": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new csp role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create csp role", + "operationId": "createCspRole", + "parameters": [ + { + "description": "CSP Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new mapping between role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create role-CSP role mapping", + "operationId": "addCspRoleMappings", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/batch": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create multiple new csp roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create multiple csp roles", + "operationId": "createCspRoles", + "parameters": [ + { + "description": "Multiple CSP Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspRolesRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspRole" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/id/:roleId": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a mapping between role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role-CSP role mapping", + "operationId": "getCspRoleMappingByRoleId", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/id/{roleId}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update role information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Update csp role", + "operationId": "updateCspRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Role Info", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete csp role", + "operationId": "deleteCspRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a mapping between role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role-CSP role mapping", + "operationId": "listCspRoleMappings", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get csp role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get csp role by ID", + "operationId": "getCspRoleByID", + "parameters": [ + { + "type": "string", + "description": "CSP Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a list of all csp roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List csp roles", + "operationId": "listCSPRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/csp/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get csp role details by Name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get csp role by Name", + "operationId": "getCspRoleByName", + "parameters": [ + { + "type": "string", + "description": "CSP Role Name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role by ID", + "operationId": "getRoleByRoleID", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the details of an existing role.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Update role", + "operationId": "updateRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Role Info", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a role by its name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete role", + "operationId": "deleteRole", + "parameters": [ + { + "type": "string", + "description": "Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/id/{roleId}/assign": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Assign a role to a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Assign role", + "operationId": "assignRole", + "parameters": [ + { + "description": "Role Assignment Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/id/{roleId}/unassign": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a role from a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Remove role", + "operationId": "removeRole", + "parameters": [ + { + "description": "Role Removal Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of all roles.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List all roles", + "operationId": "listRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/csp-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by csp role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List users by csp role", + "operationId": "listUsersByCspRole", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List role master mappings", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List role master mappings", + "operationId": "listRoleMasterMappings", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/platform-roles/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by platform role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List users by platform role", + "operationId": "listUsersByPlatformRole", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/role/id/:roleId": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get role master mappings", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role master mappings", + "operationId": "getRoleMasterMappings", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/mappings/workspace-roles/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by workspace role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List users by workspace role", + "operationId": "listUsersByWorkspaceRole", + "parameters": [ + { + "description": "Filter Role Master Mapping Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.FilterRoleMasterMappingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterMapping" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/menu-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a list of all menu roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List menu roles", + "operationId": "listPlatformRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve role details by role name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get role by Name", + "operationId": "getRoleByRoleName", + "parameters": [ + { + "type": "string", + "description": "Role name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/platform-roles": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new menu role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create menu role", + "operationId": "createPlatformRole", + "parameters": [ + { + "description": "Menu Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/platform-roles/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get platform role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get platform role by ID", + "operationId": "getPlatformRoleByID", + "parameters": [ + { + "type": "string", + "description": "Platform Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a platform role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete platform role", + "operationId": "deletePlatformRole", + "parameters": [ + { + "type": "string", + "description": "Platform Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/platform-roles/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get menu role details by Name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get menu role by Name", + "operationId": "getPlatformRoleByName", + "parameters": [ + { + "type": "string", + "description": "Menu Role Name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/unassign/csp-roles": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a mapping between workspace role and CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete workspace role-CSP role mapping", + "operationId": "removeCspRoleMappings", + "parameters": [ + { + "description": "Mapping Info", + "name": "mapping", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleMasterCspRoleMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/unassign/platform-role": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a platform role from a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Remove platform role", + "operationId": "removePlatformRole", + "parameters": [ + { + "description": "Platform Role Removal Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/unassign/workspace-role": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a workspace role from a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Remove workspace role", + "operationId": "removeWorkspaceRole", + "parameters": [ + { + "description": "Workspace Role Removal Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new workspace role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Create workspace role", + "operationId": "createWorkspaceRole", + "parameters": [ + { + "description": "Workspace Role Creation Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles/id/{roleId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspace role details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get workspace role by ID", + "operationId": "getWorkspaceRoleByID", + "parameters": [ + { + "type": "string", + "description": "Workspace Role ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a workspace role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Delete workspace role", + "operationId": "deleteWorkspaceRole", + "parameters": [ + { + "type": "string", + "description": "Workspace Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get a list of all workspace roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "List workspace roles", + "operationId": "listWorkspaceRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/workspace-roles/name/{roleName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspace role details by Name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get workspace role by Name", + "operationId": "getWorkspaceRoleByName", + "parameters": [ + { + "type": "string", + "description": "Workspace Role Name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.RoleMaster" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/{roleType}/{roleId}/mciam-permissions": { + "get": { + "description": "특정 역할의 MC-IAM 권한 ID 목록을 조회합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles", + "mciam-permissions" + ], + "summary": "역할의 MC-IAM 권한 목록 조회 - Renamed", + "operationId": "getRoleMciamPermissions", + "parameters": [ + { + "type": "string", + "description": "역할 타입 ('platform' or 'workspace')", + "name": "roleType", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "역할 ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "권한 ID 목록", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "/api/roles/{roleType}/{roleId}/mciam-permissions/{permissionId}": { + "post": { + "description": "역할에 MC-IAM 권한을 할당합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles", + "mciam-permissions" + ], + "summary": "역할에 MC-IAM 권한 할당 - Renamed", + "operationId": "assignMciamPermissionToRole", + "parameters": [ + { + "type": "string", + "description": "역할 타입 ('platform' or 'workspace')", + "name": "roleType", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "역할 ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "MC-IAM 권한 ID", + "name": "permissionId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "description": "역할에서 MC-IAM 권한을 제거합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles", + "mciam-permissions" + ], + "summary": "역할에서 MC-IAM 권한 제거 - Renamed", + "operationId": "removeMciamPermissionFromRole", + "parameters": [ + { + "type": "string", + "description": "역할 타입 ('platform' or 'workspace')", + "name": "roleType", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "역할 ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "MC-IAM 권한 ID", + "name": "permissionId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/setup/check-user-roles": { + "get": { + "description": "Check all roles assigned to a user. 특정 유저가 가진 role 목록을 조회합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Check user roles", + "operationId": "checkUserRoles", + "parameters": [ + { + "type": "string", + "description": "Username to check roles", + "name": "username", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/model.Response" + } + } + } + } + }, + "/api/setup/initial-role-menu-permission": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSV 파일을 읽어서 메뉴 권한을 초기화합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Initialize menu permissions from CSV", + "operationId": "initializeMenuPermissions", + "parameters": [ + { + "type": "string", + "description": "CSV file path (optional, uses default if not provided)", + "name": "filePath", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/model.Response" + } + } + } + } + }, + "/api/setup/sync-projects": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "mc-infra-manager의 네임스페이스 목록을 조회하여 로컬 DB에 없는 프로젝트를 추가합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "mc-infra-manager와 프로젝트 동기화", + "operationId": "syncProjects", + "responses": { + "200": { + "description": "message: Project synchronization successful", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류 또는 동기화 실패", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new user with the specified information.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Create new user", + "operationId": "createUser", + "parameters": [ + { + "description": "User Info", + "name": "user", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.User" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve user details by user ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user by ID", + "operationId": "getUserByID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/status": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update user status (active/inactive)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Update user status", + "operationId": "updateUserStatus", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Status", + "name": "status", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UserStatusRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/workspaces/id/{workspaceId}/roles/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspaces and roles for a specific user and workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user workspace and workspace roles by user ID and workspace ID", + "operationId": "getUserWorkspaceAndWorkspaceRolesByUserIDAndWorkspaceID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/workspaces/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspaces for a specific user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user workspaces by user ID", + "operationId": "getUserWorkspacesByUserID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/id/{userId}/workspaces/roles/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get workspaces and roles for a specific user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user workspace and workspace roles by user ID", + "operationId": "getUserWorkspaceAndWorkspaceRolesByUserID", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/kc/{kcUserId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get user details by KcID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user by KcID", + "operationId": "getUserByKcID", + "parameters": [ + { + "type": "string", + "description": "User KcID", + "name": "kcUserId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of all users.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List all users", + "operationId": "listUsers", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.User" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/menus-tree/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the menu tree accessible to the current user's platform role.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "menus" + ], + "summary": "Get current user's menu tree", + "operationId": "listUserMenuTree", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.MenuTreeNode" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/menus/list": { + "post": { + "description": "Get the menu list accessible to the current user's platform role.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "menus" + ], + "summary": "Get current user's menu list", + "operationId": "listUserMenu", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Menu" + } + } + } + } + } + }, + "/api/users/name/{username}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get user details by username", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Get user by username", + "operationId": "getUserByUsername", + "parameters": [ + { + "type": "string", + "description": "Username", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/workspaces/id/{workspaceId}/projects/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List projects for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List user projects by workspace", + "operationId": "listUserProjectsByWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/workspaces/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List workspaces for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List user workspaces", + "operationId": "listUserWorkspaces", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/workspaces/roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List workspaces and roles for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "List user workspace and roles", + "operationId": "listUserWorkspaceAndWorkspaceRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/users/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the details of an existing user.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Update user", + "operationId": "updateUser", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Info", + "name": "user", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.User" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a user by their ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "Delete user", + "operationId": "deleteUser", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new workspace with the specified information.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Create new workspace", + "operationId": "createWorkspace", + "parameters": [ + { + "description": "Workspace Info", + "name": "workspace", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Workspace" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/assign/projects": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a project to a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Add project to workspace", + "operationId": "addProjectToWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "400": { + "description": "error: Invalid request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace or Project not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve workspace details by workspace ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Get workspace by ID", + "operationId": "getWorkspaceByID", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the details of an existing workspace.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Update workspace", + "operationId": "updateWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Workspace Info", + "name": "workspace", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Workspace" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a workspace by its ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Delete workspace", + "operationId": "deleteWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}/projects/list": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve project list belonging to specific workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace projects", + "operationId": "getWorkspaceProjectsByWorkspaceId", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}/users/id/{userId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get roles assigned to a user in a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "roles" + ], + "summary": "Get user workspace roles", + "operationId": "getUserWorkspaceRoles", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/id/{workspaceId}/users/list": { + "post": { + "description": "Retrieve users and roles list belonging to workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List users and roles by workspace", + "operationId": "listUsersAndRolesByWorkspace", + "parameters": [ + { + "type": "integer", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + }, + "400": { + "description": "error: Invalid workspace ID", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of all workspaces.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List all workspaces", + "operationId": "listWorkspaces", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/name/{workspaceName}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve specific workspace by name", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Get workspace by name", + "operationId": "getWorkspaceByName", + "parameters": [ + { + "type": "string", + "description": "Workspace Name", + "name": "workspaceName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.Workspace" + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/projects/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve project list belonging to specific workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace projects", + "operationId": "listWorkspaceProjects", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Project" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Workspace not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/temporary-credentials": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get temporary credentials for CSP", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "Get temporary credentials", + "operationId": "mciamGetTemporaryCredentials", + "responses": {} + } + }, + "/api/workspaces/unassign/projects": { + "delete": { + "description": "Remove a project from a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Remove project from workspace", + "operationId": "removeProjectFromWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, + "/api/workspaces/users-roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve the list of users and roles assigned to the workspace.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List users and roles in workspace", + "operationId": "listAllWorkspaceUsersAndRoles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.WorkspaceWithUsersAndRoles" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/users/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "List users by workspace criteria", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace users", + "operationId": "listWorkspaceUsers", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.WorkspaceWithUsersAndRoles" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/workspace-ticket": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Set workspace ticket", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Set workspace ticket", + "operationId": "mciamWorkspaceTicket", + "responses": { + "200": { + "description": "message: Workspace ticket set successfully", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a user to a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Add user to workspace", + "operationId": "addUserToWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users/{userId}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a user from a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Remove user from workspace", + "operationId": "removeUserFromWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/readyz": { + "get": { + "description": "Check the health status of the service.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Health check", + "operationId": "mciamCheckHealth", + "parameters": [ + { + "type": "string", + "description": "Detail check components (nginx,db,keycloak,all)", + "name": "detail", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + }, + "definitions": { + "constants.AuthMethod": { + "type": "string", + "enum": [ + "OIDC", + "SAML" + ], + "x-enum-varnames": [ + "AuthMethodOIDC", + "AuthMethodSAML" + ] + }, + "constants.CSPType": { + "type": "string", + "enum": [ + "aws", + "gcp", + "azure" + ], + "x-enum-varnames": [ + "CSPTypeAWS", + "CSPTypeGCP", + "CSPTypeAzure" + ] + }, + "constants.IAMRoleType": { + "type": "string", + "enum": [ + "platform", + "workspace", + "csp" + ], + "x-enum-comments": { + "RoleTypeCSP": "CSP 역할", + "RoleTypePlatform": "플랫폼 역할", + "RoleTypeWorkspace": "워크스페이스 역할" + }, + "x-enum-descriptions": [ + "플랫폼 역할", + "워크스페이스 역할", + "CSP 역할" + ], + "x-enum-varnames": [ + "RoleTypePlatform", + "RoleTypeWorkspace", + "RoleTypeCSP" + ] + }, + "idp.UserLogin": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "mcmpapi.McmpApiAction": { + "type": "object", + "properties": { + "actionName": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "description": "Auto-incrementing primary key", + "type": "integer" + }, + "method": { + "type": "string" + }, + "resourcePath": { + "type": "string" + }, + "serviceName": { + "description": "Foreign key reference (indexed)", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "mcmpapi.McmpApiAuthInfo": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "mcmpapi.McmpApiDefinitions": { + "type": "object", + "properties": { + "serviceActions": { + "description": "Use renamed ServiceAction", + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/mcmpapi.McmpApiServiceAction" + } + } + }, + "services": { + "description": "Use renamed ServiceDefinition", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/mcmpapi.McmpApiServiceDefinition" + } + } + } + }, + "mcmpapi.McmpApiPermissionActionMapping": { + "type": "object", + "properties": { + "actionID": { + "type": "integer" + }, + "actionName": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "permissionID": { + "type": "string" + }, + "updatedAt": { + "type": "string" } } }, - "mcmpapi.ApiQueryParam": { + "mcmpapi.McmpApiServiceAction": { "type": "object", - "required": [ - "key", - "value" - ], "properties": { - "key": { + "description": { "type": "string" }, - "value": { + "method": { + "type": "string" + }, + "resourcePath": { "type": "string" } } }, - "mcmpapi.McmpApiAuthInfo": { + "mcmpapi.McmpApiServiceDefinition": { "type": "object", "properties": { - "password": { + "auth": { + "description": "Use renamed AuthInfo", + "allOf": [ + { + "$ref": "#/definitions/mcmpapi.McmpApiAuthInfo" + } + ] + }, + "baseURL": { "type": "string" }, - "type": { + "version": { + "type": "string" + } + } + }, + "model.AssignRoleRequest": { + "type": "object", + "properties": { + "roleId": { + "description": "역할 ID (문자열로 받음)", + "type": "string" + }, + "roleName": { + "description": "역할명", + "type": "string" + }, + "roleType": { + "description": "역할 타입 (platform/workspace)", + "type": "string" + }, + "userId": { + "description": "사용자 ID (문자열로 받음)", "type": "string" }, "username": { + "description": "사용자명", + "type": "string" + }, + "workspaceId": { + "description": "워크스페이스 ID (문자열로 받음)", "type": "string" } } }, - "mcmpapi.McmpApiCallRequest": { + "model.AssignWorkspaceRoleRequest": { "type": "object", - "required": [ - "actionName", - "serviceName" - ], "properties": { - "actionName": { - "description": "Target action name (operationId)", + "roleId": { + "description": "역할 ID (문자열로 받음)", "type": "string" }, - "requestParams": { - "description": "Parameters for the external API call", - "allOf": [ - { - "$ref": "#/definitions/mcmpapi.McmpApiRequestParams" - } - ] + "roleName": { + "description": "역할명", + "type": "string" }, - "serviceName": { - "description": "Target service name", + "userId": { + "description": "사용자 ID (문자열로 받음)", + "type": "string" + }, + "username": { + "description": "사용자명", + "type": "string" + }, + "workspaceId": { + "description": "워크스페이스 ID (문자열로 받음)", "type": "string" } } }, - "mcmpapi.McmpApiDefinitions": { + "model.CreateCspRoleRequest": { "type": "object", "properties": { - "serviceActions": { - "description": "Use renamed ServiceAction", - "type": "object", - "additionalProperties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/mcmpapi.McmpApiServiceAction" - } - } + "cspRoleName": { + "description": "csp의 RoleName. 여러 role이 있기때문에 csp에 정의한 role로 구분하기 위해 사용", + "type": "string" }, - "services": { - "description": "Use renamed ServiceDefinition", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/mcmpapi.McmpApiServiceDefinition" + "cspType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "iamIdentifier": { + "type": "string" + }, + "iamRoleId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idpIdentifier": { + "type": "string" + }, + "path": { + "type": "string" + }, + "status": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Tag" + } + } + } + }, + "model.CreateCspRolesRequest": { + "type": "object", + "required": [ + "cspRoles" + ], + "properties": { + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" } } } }, - "mcmpapi.McmpApiRequestParams": { + "model.CreateMenuMappingRequest": { "type": "object", + "required": [ + "menuIds", + "roleId" + ], "properties": { - "body": { - "description": "Request body (accept any JSON structure) - Changed from json.RawMessage for swag compatibility" + "menuIds": { + "type": "array", + "items": { + "type": "string" + } }, - "pathParams": { - "description": "Parameters to replace in the resource path (e.g., {userId})", - "type": "object", - "additionalProperties": { + "roleId": { + "type": "string" + } + } + }, + "model.CreateRoleRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" + } + }, + "description": { + "type": "string" + }, + "menuIds": { + "type": "array", + "items": { "type": "string" } }, - "queryParams": { - "description": "Parameters to append as query string (?key=value)", - "type": "object", - "additionalProperties": { + "name": { + "type": "string" + }, + "parentId": { + "type": "integer" + }, + "roleTypes": { + "description": "RoleTypes []constants.IAMRoleType ` + "`" + `json:\"roleTypes\" validate:\"required,dive,oneof=platform workspace csp\"` + "`" + `", + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" + } + } + } + }, + "model.CspRole": { + "type": "object", + "properties": { + "create_date": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "csp_type": { + "type": "string" + }, + "deleted_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "iam_identifier": { + "type": "string" + }, + "iam_role_id": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "idp_identifier": { + "type": "string" + }, + "max_session_duration": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "permissions": { + "type": "array", + "items": { "type": "string" } + }, + "permissions_boundary": { + "type": "string" + }, + "role_last_used": { + "$ref": "#/definitions/model.RoleLastUsed" + }, + "status": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Tag" + } + }, + "updated_at": { + "type": "string" + } + } + }, + "model.FilterRoleMasterMappingRequest": { + "type": "object", + "properties": { + "authMethod": { + "type": "string" + }, + "cspRoleId": { + "type": "string" + }, + "cspRoleName": { + "type": "string" + }, + "cspType": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectName": { + "type": "string" + }, + "roleId": { + "type": "string" + }, + "roleTypes": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" + } + }, + "userId": { + "type": "string" + }, + "username": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "workspaceName": { + "type": "string" } } }, - "mcmpapi.McmpApiServiceAction": { + "model.MciamPermission": { "type": "object", "properties": { + "action": { + "description": "e.g., create, read, update, delete", + "type": "string" + }, + "createdAt": { + "description": "Match DB schema", + "type": "string" + }, "description": { "type": "string" }, - "method": { + "frameworkId": { + "description": "FK to mcmp_resource_types.framework_id", "type": "string" }, - "resourcePath": { + "id": { + "description": "Format: \u003cframework_id\u003e:\u003cresource_type_id\u003e:\u003caction\u003e", + "type": "string" + }, + "name": { + "type": "string" + }, + "resourceTypeId": { + "description": "FK to mcmp_resource_types.id", + "type": "string" + }, + "updatedAt": { + "description": "Match DB schema", "type": "string" } } }, - "mcmpapi.McmpApiServiceDefinition": { + "model.McmpApiCallRequest": { "type": "object", + "required": [ + "actionName", + "serviceName" + ], "properties": { - "auth": { - "description": "Use renamed AuthInfo", + "actionName": { + "description": "Target action name (operationId)", + "type": "string" + }, + "requestParams": { + "description": "Parameters for the external API call", "allOf": [ { - "$ref": "#/definitions/mcmpapi.McmpApiAuthInfo" + "$ref": "#/definitions/model.McmpApiRequestParams" } - ] - }, - "baseURL": { - "type": "string" + ] }, - "version": { + "serviceName": { + "description": "Target service name", "type": "string" } } }, - "mcmpapi.ServiceApiCallRequest": { - "type": "object" + "model.McmpApiRequestParams": { + "type": "object", + "properties": { + "body": { + "description": "Request body (accept any JSON structure) - Changed from json.RawMessage for swag compatibility" + }, + "pathParams": { + "description": "Parameters to replace in the resource path (e.g., {userId})", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "queryParams": { + "description": "Parameters to append as query string (?key=value)", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } }, "model.Menu": { "type": "object", "properties": { - "display_name": { + "displayName": { "type": "string" }, "id": { "type": "string" }, - "is_action": { + "isAction": { "type": "boolean" }, - "menu_number": { + "menuNumber": { "type": "integer" }, - "parent_id": { + "parentId": { "type": "string" }, "priority": { "type": "integer" }, - "res_type": { + "resType": { "type": "string" } } @@ -2999,78 +7633,126 @@ const docTemplate = `{ "$ref": "#/definitions/model.MenuTreeNode" } }, - "display_name": { + "displayName": { "type": "string" }, "id": { "type": "string" }, - "is_action": { + "isAction": { "type": "boolean" }, - "menu_number": { + "menuNumber": { "type": "integer" }, - "parent_id": { + "parentId": { "type": "string" }, "priority": { "type": "integer" }, - "res_type": { + "resType": { "type": "string" } } }, - "model.Permission": { + "model.Project": { "type": "object", "properties": { "created_at": { "type": "string" }, "description": { - "description": "Increased size to match roles", "type": "string" }, "id": { - "description": "Changed to string", - "type": "string" + "type": "integer" }, "name": { - "description": "Assuming Name column exists or needs to be added", + "type": "string" + }, + "nsid": { + "description": "Namespace ID", "type": "string" }, "updated_at": { "type": "string" + }, + "workspaces": { + "description": "M:N relationship", + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } } } }, - "model.PlatformRole": { + "model.ResourceType": { "type": "object", "properties": { - "created_at": { + "createdAt": { "type": "string" }, "description": { "type": "string" }, + "frameworkId": { + "description": "Identifier of the framework (e.g., \"mc-iam-manager\", \"mc-infra-manager\")", + "type": "string" + }, "id": { - "type": "integer" + "description": "Unique identifier within the framework (e.g., \"workspace\", \"vm\")", + "type": "string" }, "name": { + "description": "Display name (e.g., \"Workspace\", \"Virtual Machine\")", "type": "string" }, - "updated_at": { + "updatedAt": { "type": "string" } } }, - "model.Project": { + "model.Response": { + "type": "object", + "properties": { + "error": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + }, + "model.RoleLastUsed": { + "type": "object", + "properties": { + "last_used_date": { + "type": "string" + }, + "region": { + "type": "string" + } + } + }, + "model.RoleMaster": { "type": "object", "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + }, "created_at": { "type": "string" }, + "csp_role_mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterCspRoleMapping" + } + }, "description": { "type": "string" }, @@ -3080,19 +7762,141 @@ const docTemplate = `{ "name": { "type": "string" }, - "nsid": { - "description": "Namespace ID", - "type": "string" + "parent": { + "$ref": "#/definitions/model.RoleMaster" + }, + "parent_id": { + "type": "integer" + }, + "predefined": { + "type": "boolean" + }, + "role_subs": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleSub" + } }, "updated_at": { "type": "string" + } + } + }, + "model.RoleMasterCspRoleMapping": { + "type": "object", + "properties": { + "auth_method": { + "$ref": "#/definitions/constants.AuthMethod" + }, + "createdAt": { + "type": "string" }, - "workspaces": { - "description": "M:N relationship", + "cspRoles": { + "description": "서비스 레이어에서 조합", "type": "array", "items": { - "$ref": "#/definitions/model.Workspace" + "$ref": "#/definitions/model.CspRole" + } + }, + "description": { + "type": "string" + }, + "roleId": { + "type": "integer" + } + } + }, + "model.RoleMasterCspRoleMappingRequest": { + "type": "object", + "properties": { + "authMethod": { + "$ref": "#/definitions/constants.AuthMethod" + }, + "cspRoleId": { + "type": "string" + }, + "cspType": { + "$ref": "#/definitions/constants.CSPType" + }, + "description": { + "type": "string" + }, + "roleId": { + "type": "string" + } + } + }, + "model.RoleMasterMapping": { + "type": "object", + "properties": { + "role_id": { + "type": "integer" + }, + "role_master_csp_role_mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMasterCspRoleMapping" + } + }, + "role_name": { + "type": "string" + }, + "user_platform_roles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserPlatformRole" } + }, + "user_workspace_roles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } + } + } + }, + "model.RoleSub": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "role_id": { + "type": "integer" + }, + "role_type": { + "$ref": "#/definitions/constants.IAMRoleType" + }, + "updated_at": { + "type": "string" + } + } + }, + "model.SetupInitialAdminRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "model.Tag": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" } } }, @@ -3130,10 +7934,10 @@ const docTemplate = `{ "type": "string" }, "platform_roles": { - "description": "관계 정의 (Foreign Key는 DB ID인 'ID' 필드를 참조해야 함)", + "description": "관계 정의", "type": "array", "items": { - "$ref": "#/definitions/model.PlatformRole" + "$ref": "#/definitions/model.RoleMaster" } }, "updated_at": { @@ -3144,116 +7948,132 @@ const docTemplate = `{ "type": "string" }, "workspace_roles": { - "description": "Changed foreignKey to ID", "type": "array", "items": { - "$ref": "#/definitions/model.WorkspaceRole" + "$ref": "#/definitions/model.RoleMaster" } } } }, - "model.Workspace": { + "model.UserPlatformRole": { "type": "object", "properties": { "created_at": { "type": "string" }, - "description": { - "type": "string" - }, - "id": { + "role_id": { "type": "integer" }, - "name": { - "type": "string" - }, - "projects": { - "description": "M:N relationship", - "type": "array", - "items": { - "$ref": "#/definitions/model.Project" - } + "user_id": { + "type": "integer" }, - "updated_at": { + "username": { + "description": "사용자 정보 (JOIN으로 가져올 필드들)", "type": "string" } } }, - "model.WorkspaceRole": { + "model.UserStatusRequest": { "type": "object", "properties": { - "created_at": { - "type": "string" - }, - "description": { - "type": "string" - }, "id": { - "type": "integer" + "description": "DB에 저장되는 정보 (mcmp_users 테이블)", + "type": "string" }, - "name": { - "description": "이름은 고유해야 함", + "kc_id": { + "description": "Keycloak User ID", "type": "string" }, - "updated_at": { + "status": { + "description": "사용자 상태", "type": "string" } } }, - "service.HealthStatus": { + "model.UserWorkspaceRole": { "type": "object", "properties": { - "db_connection": { + "created_at": { "type": "string" }, - "keycloak_admin_login": { - "type": "string" + "role": { + "$ref": "#/definitions/model.RoleMaster" }, - "keycloak_client_check": { - "type": "string" + "role_id": { + "type": "integer" }, - "keycloak_realm_check": { + "role_name": { "type": "string" }, - "mcmp_actions_count": { - "type": "integer" + "user": { + "$ref": "#/definitions/model.User" }, - "mcmp_services_count": { + "user_id": { "type": "integer" }, - "menus_count": { - "type": "integer" + "username": { + "type": "string" }, - "platform_roles_count": { - "type": "integer" + "workspace": { + "$ref": "#/definitions/model.Workspace" }, - "workspace_roles_count": { + "workspace_id": { "type": "integer" + }, + "workspace_name": { + "type": "string" } } }, - "service.UserWithRoles": { + "model.Workspace": { "type": "object", "properties": { - "roles": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "projects": { "type": "array", "items": { - "$ref": "#/definitions/model.WorkspaceRole" + "$ref": "#/definitions/model.Project" } }, - "user": { - "$ref": "#/definitions/model.User" + "updated_at": { + "type": "string" } } }, - "service.WorkspaceRoleInfo": { + "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { - "role": { - "$ref": "#/definitions/model.WorkspaceRole" + "created_at": { + "type": "string" }, - "workspace": { - "$ref": "#/definitions/model.Workspace" + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserWorkspaceRole" + } } } } @@ -3271,7 +8091,7 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ Version: "1.0", - Host: "localhost:3000", + Host: "localhost", BasePath: "/api/v1", Schemes: []string{}, Title: "MC IAM Manager API", diff --git a/docs/swagger.json b/docs/swagger.json index 23b1d808..421e2f8f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2003,7 +2003,7 @@ "BearerAuth": [] } ], - "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", + "description": "Create a new project with the specified information.", "consumes": [ "application/json" ], @@ -2022,7 +2022,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.CreateProjectRequest" + "$ref": "#/definitions/model.Project" } } ], @@ -2042,15 +2042,6 @@ } } }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, "500": { "description": "Internal Server Error", "schema": { @@ -2063,74 +2054,6 @@ } } }, - "/api/projects/id/{projectId}/workspaces": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve list of workspaces that the project is assigned to", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "Get workspaces assigned to project", - "operationId": "getProjectWorkspaces", - "parameters": [ - { - "type": "string", - "description": "Project ID", - "name": "projectId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Workspace" - } - } - }, - "400": { - "description": "error: Invalid project ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: Project not found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: Internal server error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/api/projects/list": { "post": { "security": [ @@ -6066,7 +5989,7 @@ "BearerAuth": [] } ], - "description": "Create a new workspace with the specified information. Optionally assign existing projects to the workspace.", + "description": "Create a new workspace with the specified information.", "consumes": [ "application/json" ], @@ -7441,24 +7364,6 @@ } } }, - "model.CreateProjectRequest": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "workspaceId": { - "description": "optional workspace to assign project to", - "type": "string" - } - } - }, "model.CreateRoleRequest": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 573635a8..a3019661 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -209,18 +209,6 @@ definitions: - menuIds - roleId type: object - model.CreateProjectRequest: - properties: - description: - type: string - name: - type: string - workspaceId: - description: optional workspace to assign project to - type: string - required: - - name - type: object model.CreateRoleRequest: properties: cspRoles: @@ -2010,8 +1998,7 @@ paths: post: consumes: - application/json - description: Create a new project with the specified information. Optionally - specify a workspace to assign the project to. + description: Create a new project with the specified information. operationId: createProject parameters: - description: Project Info @@ -2019,7 +2006,7 @@ paths: name: project required: true schema: - $ref: '#/definitions/model.CreateProjectRequest' + $ref: '#/definitions/model.Project' produces: - application/json responses: @@ -2033,12 +2020,6 @@ paths: additionalProperties: type: string type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object "500": description: Internal Server Error schema: @@ -2211,50 +2192,6 @@ paths: summary: 프로젝트에 워크스페이스 연결 tags: - projects - /api/projects/id/{projectId}/workspaces: - get: - consumes: - - application/json - description: Retrieve list of workspaces that the project is assigned to - operationId: getProjectWorkspaces - parameters: - - description: Project ID - in: path - name: projectId - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/model.Workspace' - type: array - "400": - description: 'error: Invalid project ID' - schema: - additionalProperties: - type: string - type: object - "404": - description: 'error: Project not found' - schema: - additionalProperties: - type: string - type: object - "500": - description: 'error: Internal server error' - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Get workspaces assigned to project - tags: - - projects /api/projects/list: post: consumes: @@ -4628,8 +4565,7 @@ paths: post: consumes: - application/json - description: Create a new workspace with the specified information. Optionally - assign existing projects to the workspace. + description: Create a new workspace with the specified information. operationId: createWorkspace parameters: - description: Workspace Info diff --git a/src/csp/interface.go b/src/csp/interface.go index 95299951..008d2f3c 100644 --- a/src/csp/interface.go +++ b/src/csp/interface.go @@ -76,3 +76,109 @@ type IAMClientConfig struct { WebIdentityToken string WorkspaceTicket string } + +// AssumeRoleConfig STS AssumeRole 요청 설정 +type AssumeRoleConfig struct { + RoleArn string `json:"role_arn"` + RoleSessionName string `json:"role_session_name"` + WebIdentityToken string `json:"web_identity_token,omitempty"` + DurationSeconds int32 `json:"duration_seconds,omitempty"` + ExternalID string `json:"external_id,omitempty"` + Policy string `json:"policy,omitempty"` + PolicyArns []string `json:"policy_arns,omitempty"` + Tags map[string]string `json:"tags,omitempty"` +} + +// CredentialResponse 임시 자격 증명 응답 +type CredentialResponse struct { + AccessKeyID string `json:"access_key_id"` + SecretAccessKey string `json:"secret_access_key"` + SessionToken string `json:"session_token"` + Expiration time.Time `json:"expiration"` + Provider string `json:"provider"` +} + +// CredentialService CSP 자격 증명 서비스 인터페이스 +type CredentialService interface { + // AssumeRoleWithWebIdentity OIDC 토큰으로 역할 인수 + AssumeRoleWithWebIdentity(ctx context.Context, config *AssumeRoleConfig) (*CredentialResponse, error) + + // AssumeRoleWithSAML SAML assertion으로 역할 인수 + AssumeRoleWithSAML(ctx context.Context, config *AssumeRoleConfig, samlAssertion string) (*CredentialResponse, error) + + // AssumeRole Secret Key로 역할 인수 + AssumeRole(ctx context.Context, config *AssumeRoleConfig) (*CredentialResponse, error) + + // ValidateCredentials 자격 증명 유효성 검증 + ValidateCredentials(ctx context.Context, credentials map[string]string) error + + // GetCspType CSP 타입 반환 + GetCspType() string +} + +// PolicyDefinition 정책 정의 +type PolicyDefinition struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` + PolicyDoc map[string]interface{} `json:"policy_doc"` + Path string `json:"path,omitempty"` + Tags map[string]string `json:"tags,omitempty"` +} + +// PolicyInfo 정책 정보 +type PolicyInfo struct { + Arn string `json:"arn"` + Name string `json:"name"` + PolicyID string `json:"policy_id"` + Description string `json:"description,omitempty"` + PolicyDoc map[string]interface{} `json:"policy_doc,omitempty"` + Path string `json:"path,omitempty"` + DefaultVersion string `json:"default_version,omitempty"` + AttachmentCount int `json:"attachment_count"` + IsAttachable bool `json:"is_attachable"` + CreateDate time.Time `json:"create_date"` + UpdateDate time.Time `json:"update_date"` +} + +// PolicyFilter 정책 조회 필터 +type PolicyFilter struct { + Scope string `json:"scope,omitempty"` // All, AWS, Local + PathPrefix string `json:"path_prefix,omitempty"` // 경로 접두사 + PolicyUsageType string `json:"policy_usage_type,omitempty"` // PermissionsPolicy, PermissionsBoundary + OnlyAttached bool `json:"only_attached,omitempty"` // 연결된 정책만 + MaxItems int `json:"max_items,omitempty"` + Marker string `json:"marker,omitempty"` +} + +// PolicyManager CSP 정책 관리 인터페이스 +type PolicyManager interface { + // CreatePolicy 정책 생성 + CreatePolicy(ctx context.Context, policy *PolicyDefinition) (*PolicyInfo, error) + + // GetPolicy 정책 조회 + GetPolicy(ctx context.Context, policyArn string) (*PolicyInfo, error) + + // GetPolicyDocument 정책 문서 조회 + GetPolicyDocument(ctx context.Context, policyArn string, versionId string) (map[string]interface{}, error) + + // UpdatePolicy 정책 수정 (새 버전 생성) + UpdatePolicy(ctx context.Context, policyArn string, policy *PolicyDefinition) (*PolicyInfo, error) + + // DeletePolicy 정책 삭제 + DeletePolicy(ctx context.Context, policyArn string) error + + // ListPolicies 정책 목록 조회 + ListPolicies(ctx context.Context, filter *PolicyFilter) ([]*PolicyInfo, string, error) + + // AttachPolicyToRole 역할에 정책 연결 + AttachPolicyToRole(ctx context.Context, roleName, policyArn string) error + + // DetachPolicyFromRole 역할에서 정책 분리 + DetachPolicyFromRole(ctx context.Context, roleName, policyArn string) error + + // ListAttachedRolePolicies 역할에 연결된 정책 목록 조회 + ListAttachedRolePolicies(ctx context.Context, roleName string) ([]*PolicyInfo, error) + + // GetCspType CSP 타입 반환 + GetCspType() string +} diff --git a/src/docs/docs.go b/src/docs/docs.go index 7009f4f4..fc7dea8c 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -251,14 +251,72 @@ const docTemplate = `{ } } }, - "/api/csp-credentials": { + "/api/csp-accounts": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new CSP account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Create CSP account", + "operationId": "createCspAccount", + "parameters": [ + { + "description": "CSP Account Info", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspAccountRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.CspAccount" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "모든 CSP 인증 정보 목록을 조회합니다", + "description": "Retrieve CSP account details by ID", "consumes": [ "application/json" ], @@ -266,19 +324,196 @@ const docTemplate = `{ "application/json" ], "tags": [ - "csp-credentials" + "csp-accounts" ], - "summary": "CSP 인증 정보 목록 조회", - "operationId": "mciamListCredentials", - "responses": {} + "summary": "Get CSP account by ID", + "operationId": "getCspAccountByID", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspAccount" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update CSP account details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Update CSP account", + "operationId": "updateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + }, + { + "description": "CSP Account Info", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UpdateCspAccountRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspAccount" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a CSP account by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Delete CSP account", + "operationId": "deleteCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}/activate": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "새로운 CSP 인증 정보를 생성합니다", + "description": "Activate a CSP account", "consumes": [ "application/json" ], @@ -286,21 +521,1341 @@ const docTemplate = `{ "application/json" ], "tags": [ - "csp-credentials" + "csp-accounts" ], - "summary": "새 CSP 인증 정보 생성", - "operationId": "mciamCreateCredential", - "responses": {} + "summary": "Activate CSP account", + "operationId": "activateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}/deactivate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Deactivate a CSP account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Deactivate CSP account", + "operationId": "deactivateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}/validate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Validate CSP account configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Validate CSP account", + "operationId": "validateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of CSP accounts with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "List CSP accounts", + "operationId": "listCspAccounts", + "parameters": [ + { + "description": "Filter options", + "name": "filter", + "in": "body", + "schema": { + "$ref": "#/definitions/model.CspAccountFilter" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspAccount" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-credentials": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "모든 CSP 인증 정보 목록을 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 목록 조회", + "operationId": "mciamListCredentials", + "responses": {} + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "새로운 CSP 인증 정보를 생성합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "새 CSP 인증 정보 생성", + "operationId": "mciamCreateCredential", + "responses": {} + } + }, + "/api/csp-credentials/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "특정 CSP 인증 정보를 ID로 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 ID로 조회", + "operationId": "mciamGetCredentialByID", + "parameters": [ + { + "type": "string", + "description": "Credential ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 업데이트합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 업데이트", + "operationId": "mciamUpdateCredential", + "parameters": [ + { + "type": "string", + "description": "Credential ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 삭제합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 삭제", + "operationId": "mciamDeleteCredential", + "parameters": [ + { + "type": "string", + "description": "Credential ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new CSP IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Create CSP IDP config", + "operationId": "createCspIdpConfig", + "parameters": [ + { + "description": "CSP IDP Config Info", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspIdpConfigRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.CspIdpConfig" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve CSP IDP configuration details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Get CSP IDP config by ID", + "operationId": "getCspIdpConfigByID", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspIdpConfig" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update CSP IDP configuration details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Update CSP IDP config", + "operationId": "updateCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + }, + { + "description": "CSP IDP Config Info", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UpdateCspIdpConfigRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspIdpConfig" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a CSP IDP configuration by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Delete CSP IDP config", + "operationId": "deleteCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}/activate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Activate a CSP IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Activate CSP IDP config", + "operationId": "activateCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}/deactivate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Deactivate a CSP IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Deactivate CSP IDP config", + "operationId": "deactivateCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}/test": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Test connection to CSP using IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Test CSP IDP connection", + "operationId": "testCspIdpConnection", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of CSP IDP configurations with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "List CSP IDP configs", + "operationId": "listCspIdpConfigs", + "parameters": [ + { + "description": "Filter options", + "name": "filter", + "in": "body", + "schema": { + "$ref": "#/definitions/model.CspIdpConfigFilter" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspIdpConfig" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new CSP policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Create CSP policy", + "operationId": "createCspPolicy", + "parameters": [ + { + "description": "CSP Policy Info", + "name": "policy", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspPolicyRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.CspPolicy" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/attach": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Attach a CSP policy to a CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Attach policy to role", + "operationId": "attachPolicyToRole", + "parameters": [ + { + "description": "Attach Policy Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AttachPolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/detach": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Detach a CSP policy from a CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Detach policy from role", + "operationId": "detachPolicyFromRole", + "parameters": [ + { + "description": "Detach Policy Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AttachPolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/id/{policyId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve CSP policy details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Get CSP policy by ID", + "operationId": "getCspPolicyByID", + "parameters": [ + { + "type": "string", + "description": "Policy ID", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspPolicy" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update CSP policy details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Update CSP policy", + "operationId": "updateCspPolicy", + "parameters": [ + { + "type": "string", + "description": "Policy ID", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "CSP Policy Info", + "name": "policy", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UpdateCspPolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspPolicy" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a CSP policy by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Delete CSP policy", + "operationId": "deleteCspPolicy", + "parameters": [ + { + "type": "string", + "description": "Policy ID", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } } }, - "/api/csp-credentials/{id}": { + "/api/csp-policies/id/{policyId}/document": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "특정 CSP 인증 정보를 ID로 조회합니다", + "description": "Get the policy document content", "consumes": [ "application/json" ], @@ -308,22 +1863,47 @@ const docTemplate = `{ "application/json" ], "tags": [ - "csp-credentials" + "csp-policies" ], - "summary": "CSP 인증 정보 ID로 조회", - "operationId": "mciamGetCredentialByID", + "summary": "Get policy document", + "operationId": "getPolicyDocument", "parameters": [ { "type": "string", - "description": "Credential ID", - "name": "id", + "description": "Policy ID", + "name": "policyId", "in": "path", "required": true } ], "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "404": { - "description": "error: Credential not found", + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -332,14 +1912,16 @@ const docTemplate = `{ } } } - }, - "put": { + } + }, + "/api/csp-policies/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "CSP 인증 정보를 업데이트합니다", + "description": "Retrieve a list of CSP policies with optional filters", "consumes": [ "application/json" ], @@ -347,22 +1929,32 @@ const docTemplate = `{ "application/json" ], "tags": [ - "csp-credentials" + "csp-policies" ], - "summary": "CSP 인증 정보 업데이트", - "operationId": "mciamUpdateCredential", + "summary": "List CSP policies", + "operationId": "listCspPolicies", "parameters": [ { - "type": "string", - "description": "Credential ID", - "name": "id", - "in": "path", - "required": true + "description": "Filter options", + "name": "filter", + "in": "body", + "schema": { + "$ref": "#/definitions/model.CspPolicyFilter" + } } ], "responses": { - "404": { - "description": "error: Credential not found", + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspPolicy" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -371,14 +1963,16 @@ const docTemplate = `{ } } } - }, - "delete": { + } + }, + "/api/csp-policies/role/{roleId}": { + "get": { "security": [ { "BearerAuth": [] } ], - "description": "CSP 인증 정보를 삭제합니다", + "description": "Get list of policies attached to a CSP role", "consumes": [ "application/json" ], @@ -386,25 +1980,31 @@ const docTemplate = `{ "application/json" ], "tags": [ - "csp-credentials" + "csp-policies" ], - "summary": "CSP 인증 정보 삭제", - "operationId": "mciamDeleteCredential", + "summary": "Get policies attached to role", + "operationId": "getRolePolicies", "parameters": [ { "type": "string", - "description": "Credential ID", - "name": "id", + "description": "Role ID", + "name": "roleId", "in": "path", "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspPolicy" + } + } }, - "401": { - "description": "error: Unauthorized", + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -412,8 +2012,60 @@ const docTemplate = `{ } } }, - "403": { - "description": "error: Forbidden", + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/sync": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Synchronize policies from the CSP cloud", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Sync CSP policies from cloud", + "operationId": "syncCspPolicies", + "parameters": [ + { + "description": "Sync Policies Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.SyncPoliciesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspPolicy" + } + } + }, + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -421,8 +2073,8 @@ const docTemplate = `{ } } }, - "404": { - "description": "error: Credential not found", + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -658,41 +2310,135 @@ const docTemplate = `{ } } } - }, - "delete": { - "description": "Deletes a mapping between a permission and an API action", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "mcmp-api-permission-action-mappings" - ], - "summary": "Delete permission-action mapping", - "operationId": "deleteMapping", - "parameters": [ - { - "type": "string", - "description": "Permission ID", - "name": "permissionId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Action ID", - "name": "actionId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + "delete": { + "description": "Deletes a mapping between a permission and an API action", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mcmp-api-permission-action-mappings" + ], + "summary": "Delete permission-action mapping", + "operationId": "deleteMapping", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Action ID", + "name": "actionId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions": { + "get": { + "description": "Returns all platform actions mapped to a specific permission", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mcmp-api-permission-action-mappings" + ], + "summary": "Get platform actions by permission ID", + "operationId": "getPlatformActionsByPermissionID", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" + } + } + } + } + } + }, + "/api/mcmp-apis/import": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Fetches API specifications from remote URLs and imports them to the database. Supports swagger and openapi source types.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "McmpAPI" + ], + "summary": "Import MCMP APIs from Remote Sources", + "operationId": "importAPIs", + "parameters": [ + { + "description": "Frameworks to import", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.ImportApiRequest" + } + } + ], + "responses": { + "200": { + "description": "Import results", + "schema": { + "$ref": "#/definitions/model.ImportApiResponse" + } + }, + "400": { + "description": "error: Invalid request body", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Failed to import APIs", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } } }, "/api/mcmp-apis/list": { @@ -969,42 +2715,6 @@ const docTemplate = `{ } } }, - "/api/mcmp-apis/permission-action-mappings/id/{id}": { - "get": { - "description": "Returns all platform actions mapped to a specific permission", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "mcmp-api-permission-action-mappings" - ], - "summary": "Get platform actions by permission ID", - "operationId": "getPlatformActionsByPermissionID", - "parameters": [ - { - "type": "string", - "description": "Permission ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/mcmpapi.McmpApiAction" - } - } - } - } - } - }, "/api/mcmp-apis/syncMcmpAPIs": { "post": { "security": [ @@ -2009,7 +3719,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", + "description": "Create a new project with the specified information.", "consumes": [ "application/json" ], @@ -2028,7 +3738,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.CreateProjectRequest" + "$ref": "#/definitions/model.Project" } } ], @@ -2048,15 +3758,6 @@ const docTemplate = `{ } } }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, "500": { "description": "Internal Server Error", "schema": { @@ -2069,138 +3770,6 @@ const docTemplate = `{ } } }, - "/api/projects/assign/workspaces": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "프로젝트에 워크스페이스를 연결합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "operationId": "addWorkspaceToProject", - "parameters": [ - { - "description": "Workspace and Project IDs", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/projects/id/{projectId}/workspaces": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve list of workspaces that the project is assigned to", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "Get workspaces assigned to project", - "operationId": "getProjectWorkspaces", - "parameters": [ - { - "type": "string", - "description": "Project ID", - "name": "projectId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Workspace" - } - } - }, - "400": { - "description": "error: Invalid project ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: Project not found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: Internal server error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/api/projects/list": { "post": { "security": [ @@ -2277,63 +3846,8 @@ const docTemplate = `{ "$ref": "#/definitions/model.Project" } }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/projects/unassign/workspaces": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Remove a workspace from a project", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "Remove workspace from project", - "operationId": "removeWorkspaceFromProject", - "parameters": [ - { - "description": "Workspace and Project IDs", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: Invalid request", + "404": { + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2342,7 +3856,7 @@ const docTemplate = `{ } }, "500": { - "description": "error: Internal server error", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2532,6 +4046,75 @@ const docTemplate = `{ } } }, + "/api/projects/{id}/workspaces/{workspaceId}": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "프로젝트에 워크스페이스를 연결합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", + "parameters": [ + { + "type": "integer", + "description": "프로젝트 ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "워크스페이스 ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: 잘못된 ID 형식", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/resource-types/cloud-resources": { "post": { "security": [ @@ -4937,7 +6520,7 @@ const docTemplate = `{ "roles" ], "summary": "List workspace roles", - "operationId": "listWorkspaceRoles", + "operationId": "listRolesOfWorkspaceType", "responses": { "200": { "description": "OK", @@ -6122,7 +7705,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Create a new workspace with the specified information. Optionally assign existing projects to the workspace.", + "description": "Create a new workspace with the specified information.", "consumes": [ "application/json" ], @@ -6804,6 +8387,67 @@ const docTemplate = `{ } } }, + "/api/workspaces/roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve all workspace-level roles with optional filtering", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace roles", + "operationId": "listWorkspaceRoles", + "parameters": [ + { + "description": "Role filter parameters", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleFilterRequest" + } + } + ], + "responses": { + "200": { + "description": "Successfully retrieved workspace roles", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "400": { + "description": "error: Invalid request format", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Failed to retrieve workspace roles", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/workspaces/temporary-credentials": { "post": { "security": [ @@ -6990,8 +8634,160 @@ const docTemplate = `{ } } }, - "401": { - "description": "error: Unauthorized", + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a user to a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Add user to workspace", + "operationId": "addUserToWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users/{userId}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a user from a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Remove user from workspace", + "operationId": "removeUserFromWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -7274,6 +9070,139 @@ const docTemplate = `{ } } }, + "model.AttachPolicyRequest": { + "type": "object", + "required": [ + "csp_policy_id", + "csp_role_id" + ], + "properties": { + "csp_policy_id": { + "type": "integer" + }, + "csp_role_id": { + "type": "integer" + } + } + }, + "model.AuthMethodType": { + "type": "string", + "enum": [ + "OIDC", + "SAML", + "SECRET_KEY" + ], + "x-enum-varnames": [ + "AuthMethodOIDC", + "AuthMethodSAML", + "AuthMethodSecretKey" + ] + }, + "model.CreateCspAccountRequest": { + "type": "object", + "required": [ + "csp_type", + "name" + ], + "properties": { + "account_info": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "csp_type": { + "type": "string", + "enum": [ + "aws", + "gcp", + "azure" + ] + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "model.CreateCspIdpConfigRequest": { + "type": "object", + "required": [ + "auth_method", + "config", + "csp_account_id", + "name" + ], + "properties": { + "auth_method": { + "enum": [ + "OIDC", + "SAML", + "SECRET_KEY" + ], + "allOf": [ + { + "$ref": "#/definitions/model.AuthMethodType" + } + ] + }, + "config": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "csp_account_id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "model.CreateCspPolicyRequest": { + "type": "object", + "required": [ + "csp_account_id", + "name", + "policy_type" + ], + "properties": { + "csp_account_id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "policy_arn": { + "type": "string" + }, + "policy_doc": { + "type": "object", + "additionalProperties": true + }, + "policy_type": { + "enum": [ + "inline", + "managed", + "custom" + ], + "allOf": [ + { + "$ref": "#/definitions/model.PolicyType" + } + ] + } + } + }, "model.CreateCspRoleRequest": { "type": "object", "properties": { @@ -7299,103 +9228,247 @@ const docTemplate = `{ "idpIdentifier": { "type": "string" }, - "path": { + "path": { + "type": "string" + }, + "status": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Tag" + } + } + } + }, + "model.CreateCspRolesRequest": { + "type": "object", + "required": [ + "cspRoles" + ], + "properties": { + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" + } + } + } + }, + "model.CreateMenuMappingRequest": { + "type": "object", + "required": [ + "menuIds", + "roleId" + ], + "properties": { + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "roleId": { + "type": "string" + } + } + }, + "model.CreateRoleRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" + } + }, + "description": { + "type": "string" + }, + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "integer" + }, + "roleTypes": { + "description": "RoleTypes []constants.IAMRoleType ` + "`" + `json:\"roleTypes\" validate:\"required,dive,oneof=platform workspace csp\"` + "`" + `", + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" + } + } + } + }, + "model.CspAccount": { + "type": "object", + "properties": { + "account_info": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "csp_type": { + "description": "aws, gcp, azure", + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "model.CspAccountFilter": { + "type": "object", + "properties": { + "csp_type": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "model.CspIdpConfig": { + "type": "object", + "properties": { + "auth_method": { + "description": "OIDC, SAML, SECRET_KEY", + "allOf": [ + { + "$ref": "#/definitions/model.AuthMethodType" + } + ] + }, + "config": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "csp_account": { + "$ref": "#/definitions/model.CspAccount" + }, + "csp_account_id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "name": { "type": "string" }, - "status": { + "updated_at": { "type": "string" - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Tag" - } - } - } - }, - "model.CreateCspRolesRequest": { - "type": "object", - "required": [ - "cspRoles" - ], - "properties": { - "cspRoles": { - "type": "array", - "items": { - "$ref": "#/definitions/model.CreateCspRoleRequest" - } } } }, - "model.CreateMenuMappingRequest": { + "model.CspIdpConfigFilter": { "type": "object", - "required": [ - "menuIds", - "roleId" - ], "properties": { - "menuIds": { - "type": "array", - "items": { - "type": "string" - } + "auth_method": { + "$ref": "#/definitions/model.AuthMethodType" }, - "roleId": { + "csp_account_id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "name": { "type": "string" } } }, - "model.CreateProjectRequest": { + "model.CspPolicy": { "type": "object", - "required": [ - "name" - ], "properties": { + "created_at": { + "type": "string" + }, + "csp_account": { + "$ref": "#/definitions/model.CspAccount" + }, + "csp_account_id": { + "type": "integer" + }, "description": { "type": "string" }, + "id": { + "type": "integer" + }, "name": { "type": "string" }, - "workspaceId": { - "description": "optional workspace to assign project to", + "policy_arn": { + "type": "string" + }, + "policy_doc": { + "type": "object", + "additionalProperties": true + }, + "policy_type": { + "description": "inline, managed, custom", + "allOf": [ + { + "$ref": "#/definitions/model.PolicyType" + } + ] + }, + "updated_at": { "type": "string" } } }, - "model.CreateRoleRequest": { + "model.CspPolicyFilter": { "type": "object", - "required": [ - "name" - ], "properties": { - "cspRoles": { - "type": "array", - "items": { - "$ref": "#/definitions/model.CreateCspRoleRequest" - } - }, - "description": { - "type": "string" - }, - "menuIds": { - "type": "array", - "items": { - "type": "string" - } + "csp_account_id": { + "type": "integer" }, "name": { "type": "string" }, - "parentId": { - "type": "integer" - }, - "roleTypes": { - "description": "RoleTypes []constants.IAMRoleType ` + "`" + `json:\"roleTypes\" validate:\"required,dive,oneof=platform workspace csp\"` + "`" + `", - "type": "array", - "items": { - "$ref": "#/definitions/constants.IAMRoleType" - } + "policy_type": { + "$ref": "#/definitions/model.PolicyType" } } }, @@ -7408,6 +9481,19 @@ const docTemplate = `{ "created_at": { "type": "string" }, + "csp_account": { + "$ref": "#/definitions/model.CspAccount" + }, + "csp_account_id": { + "description": "CSP 계정 및 IDP 설정 참조 (신규 추가)", + "type": "integer" + }, + "csp_idp_config": { + "$ref": "#/definitions/model.CspIdpConfig" + }, + "csp_idp_config_id": { + "type": "integer" + }, "csp_type": { "type": "string" }, @@ -7417,6 +9503,10 @@ const docTemplate = `{ "description": { "type": "string" }, + "extended_config": { + "type": "object", + "additionalProperties": true + }, "iam_identifier": { "type": "string" }, @@ -7508,6 +9598,101 @@ const docTemplate = `{ } } }, + "model.ImportApiFramework": { + "type": "object", + "required": [ + "name", + "sourceType", + "sourceUrl", + "version" + ], + "properties": { + "name": { + "description": "Framework name (e.g., \"mc-infra-manager\")", + "type": "string" + }, + "repository": { + "description": "Repository URL (e.g., \"https://github.com/...\")", + "type": "string" + }, + "sourceType": { + "description": "Source type: \"swagger\" or \"openapi\"", + "type": "string" + }, + "sourceUrl": { + "description": "URL to fetch the API specification from", + "type": "string" + }, + "version": { + "description": "Framework version (e.g., \"0.9.22\")", + "type": "string" + } + } + }, + "model.ImportApiFrameworkResult": { + "type": "object", + "properties": { + "actionCount": { + "description": "Number of actions imported (on success)", + "type": "integer" + }, + "errorMessage": { + "description": "Error message (on failure)", + "type": "string" + }, + "name": { + "description": "Framework name", + "type": "string" + }, + "success": { + "description": "Whether the import was successful", + "type": "boolean" + }, + "version": { + "description": "Framework version", + "type": "string" + } + } + }, + "model.ImportApiRequest": { + "type": "object", + "required": [ + "frameworks" + ], + "properties": { + "frameworks": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/model.ImportApiFramework" + } + } + } + }, + "model.ImportApiResponse": { + "type": "object", + "properties": { + "failureCount": { + "description": "Number of failed frameworks", + "type": "integer" + }, + "frameworkResults": { + "description": "Detailed results for each framework", + "type": "array", + "items": { + "$ref": "#/definitions/model.ImportApiFrameworkResult" + } + }, + "successCount": { + "description": "Number of successfully imported frameworks", + "type": "integer" + }, + "totalFrameworks": { + "description": "Total number of frameworks in request", + "type": "integer" + } + } + }, "model.MciamPermission": { "type": "object", "properties": { @@ -7649,6 +9834,29 @@ const docTemplate = `{ } } }, + "model.PolicyType": { + "type": "string", + "enum": [ + "inline", + "managed", + "custom" + ], + "x-enum-comments": { + "PolicyTypeCustom": "사용자 정의 정책", + "PolicyTypeInline": "인라인 정책 (역할에 직접 포함)", + "PolicyTypeManaged": "관리형 정책 (독립 정책)" + }, + "x-enum-descriptions": [ + "인라인 정책 (역할에 직접 포함)", + "관리형 정책 (독립 정책)", + "사용자 정의 정책" + ], + "x-enum-varnames": [ + "PolicyTypeInline", + "PolicyTypeManaged", + "PolicyTypeCustom" + ] + }, "model.Project": { "type": "object", "properties": { @@ -7717,6 +9925,23 @@ const docTemplate = `{ } } }, + "model.RoleFilterRequest": { + "type": "object", + "properties": { + "roleId": { + "type": "string" + }, + "roleName": { + "type": "string" + }, + "roleTypes": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" + } + } + } + }, "model.RoleLastUsed": { "type": "object", "properties": { @@ -7882,6 +10107,21 @@ const docTemplate = `{ } } }, + "model.SyncPoliciesRequest": { + "type": "object", + "required": [ + "csp_account_id" + ], + "properties": { + "csp_account_id": { + "type": "integer" + }, + "policy_scope": { + "description": "All, AWS, Local", + "type": "string" + } + } + }, "model.Tag": { "type": "object", "properties": { @@ -7893,6 +10133,64 @@ const docTemplate = `{ } } }, + "model.UpdateCspAccountRequest": { + "type": "object", + "properties": { + "account_info": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "model.UpdateCspIdpConfigRequest": { + "type": "object", + "properties": { + "config": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "model.UpdateCspPolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "policy_arn": { + "type": "string" + }, + "policy_doc": { + "type": "object", + "additionalProperties": true + } + } + }, "model.User": { "type": "object", "properties": { @@ -8044,24 +10342,6 @@ const docTemplate = `{ } } }, - "model.WorkspaceProjectMappingRequest": { - "type": "object", - "required": [ - "projectIds", - "workspaceId" - ], - "properties": { - "projectIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "workspaceId": { - "type": "string" - } - } - }, "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 6d890308..1dc86bfe 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -245,14 +245,72 @@ } } }, - "/api/csp-credentials": { + "/api/csp-accounts": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new CSP account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Create CSP account", + "operationId": "createCspAccount", + "parameters": [ + { + "description": "CSP Account Info", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspAccountRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.CspAccount" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "모든 CSP 인증 정보 목록을 조회합니다", + "description": "Retrieve CSP account details by ID", "consumes": [ "application/json" ], @@ -260,19 +318,196 @@ "application/json" ], "tags": [ - "csp-credentials" + "csp-accounts" ], - "summary": "CSP 인증 정보 목록 조회", - "operationId": "mciamListCredentials", - "responses": {} + "summary": "Get CSP account by ID", + "operationId": "getCspAccountByID", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspAccount" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update CSP account details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Update CSP account", + "operationId": "updateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + }, + { + "description": "CSP Account Info", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UpdateCspAccountRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspAccount" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a CSP account by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Delete CSP account", + "operationId": "deleteCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}/activate": { "post": { "security": [ { "BearerAuth": [] } ], - "description": "새로운 CSP 인증 정보를 생성합니다", + "description": "Activate a CSP account", "consumes": [ "application/json" ], @@ -280,21 +515,1341 @@ "application/json" ], "tags": [ - "csp-credentials" + "csp-accounts" ], - "summary": "새 CSP 인증 정보 생성", - "operationId": "mciamCreateCredential", - "responses": {} + "summary": "Activate CSP account", + "operationId": "activateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}/deactivate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Deactivate a CSP account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Deactivate CSP account", + "operationId": "deactivateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/id/{accountId}/validate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Validate CSP account configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "Validate CSP account", + "operationId": "validateCspAccount", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-accounts/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of CSP accounts with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-accounts" + ], + "summary": "List CSP accounts", + "operationId": "listCspAccounts", + "parameters": [ + { + "description": "Filter options", + "name": "filter", + "in": "body", + "schema": { + "$ref": "#/definitions/model.CspAccountFilter" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspAccount" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-credentials": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "모든 CSP 인증 정보 목록을 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 목록 조회", + "operationId": "mciamListCredentials", + "responses": {} + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "새로운 CSP 인증 정보를 생성합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "새 CSP 인증 정보 생성", + "operationId": "mciamCreateCredential", + "responses": {} + } + }, + "/api/csp-credentials/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "특정 CSP 인증 정보를 ID로 조회합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 ID로 조회", + "operationId": "mciamGetCredentialByID", + "parameters": [ + { + "type": "string", + "description": "Credential ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 업데이트합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 업데이트", + "operationId": "mciamUpdateCredential", + "parameters": [ + { + "type": "string", + "description": "Credential ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "CSP 인증 정보를 삭제합니다", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-credentials" + ], + "summary": "CSP 인증 정보 삭제", + "operationId": "mciamDeleteCredential", + "parameters": [ + { + "type": "string", + "description": "Credential ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "403": { + "description": "error: Forbidden", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Credential not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new CSP IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Create CSP IDP config", + "operationId": "createCspIdpConfig", + "parameters": [ + { + "description": "CSP IDP Config Info", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspIdpConfigRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.CspIdpConfig" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve CSP IDP configuration details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Get CSP IDP config by ID", + "operationId": "getCspIdpConfigByID", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspIdpConfig" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update CSP IDP configuration details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Update CSP IDP config", + "operationId": "updateCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + }, + { + "description": "CSP IDP Config Info", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UpdateCspIdpConfigRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspIdpConfig" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a CSP IDP configuration by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Delete CSP IDP config", + "operationId": "deleteCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}/activate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Activate a CSP IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Activate CSP IDP config", + "operationId": "activateCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}/deactivate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Deactivate a CSP IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Deactivate CSP IDP config", + "operationId": "deactivateCspIdpConfig", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/id/{configId}/test": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Test connection to CSP using IDP configuration", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "Test CSP IDP connection", + "operationId": "testCspIdpConnection", + "parameters": [ + { + "type": "string", + "description": "Config ID", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-idp-configs/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve a list of CSP IDP configurations with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-idp-configs" + ], + "summary": "List CSP IDP configs", + "operationId": "listCspIdpConfigs", + "parameters": [ + { + "description": "Filter options", + "name": "filter", + "in": "body", + "schema": { + "$ref": "#/definitions/model.CspIdpConfigFilter" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspIdpConfig" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new CSP policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Create CSP policy", + "operationId": "createCspPolicy", + "parameters": [ + { + "description": "CSP Policy Info", + "name": "policy", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.CreateCspPolicyRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/model.CspPolicy" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/attach": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Attach a CSP policy to a CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Attach policy to role", + "operationId": "attachPolicyToRole", + "parameters": [ + { + "description": "Attach Policy Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AttachPolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/detach": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Detach a CSP policy from a CSP role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Detach policy from role", + "operationId": "detachPolicyFromRole", + "parameters": [ + { + "description": "Detach Policy Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AttachPolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/id/{policyId}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve CSP policy details by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Get CSP policy by ID", + "operationId": "getCspPolicyByID", + "parameters": [ + { + "type": "string", + "description": "Policy ID", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspPolicy" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update CSP policy details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Update CSP policy", + "operationId": "updateCspPolicy", + "parameters": [ + { + "type": "string", + "description": "Policy ID", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "CSP Policy Info", + "name": "policy", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UpdateCspPolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.CspPolicy" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a CSP policy by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Delete CSP policy", + "operationId": "deleteCspPolicy", + "parameters": [ + { + "type": "string", + "description": "Policy ID", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } } }, - "/api/csp-credentials/{id}": { + "/api/csp-policies/id/{policyId}/document": { "get": { "security": [ { "BearerAuth": [] } ], - "description": "특정 CSP 인증 정보를 ID로 조회합니다", + "description": "Get the policy document content", "consumes": [ "application/json" ], @@ -302,22 +1857,47 @@ "application/json" ], "tags": [ - "csp-credentials" + "csp-policies" ], - "summary": "CSP 인증 정보 ID로 조회", - "operationId": "mciamGetCredentialByID", + "summary": "Get policy document", + "operationId": "getPolicyDocument", "parameters": [ { "type": "string", - "description": "Credential ID", - "name": "id", + "description": "Policy ID", + "name": "policyId", "in": "path", "required": true } ], "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "404": { - "description": "error: Credential not found", + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -326,14 +1906,16 @@ } } } - }, - "put": { + } + }, + "/api/csp-policies/list": { + "post": { "security": [ { "BearerAuth": [] } ], - "description": "CSP 인증 정보를 업데이트합니다", + "description": "Retrieve a list of CSP policies with optional filters", "consumes": [ "application/json" ], @@ -341,22 +1923,32 @@ "application/json" ], "tags": [ - "csp-credentials" + "csp-policies" ], - "summary": "CSP 인증 정보 업데이트", - "operationId": "mciamUpdateCredential", + "summary": "List CSP policies", + "operationId": "listCspPolicies", "parameters": [ { - "type": "string", - "description": "Credential ID", - "name": "id", - "in": "path", - "required": true + "description": "Filter options", + "name": "filter", + "in": "body", + "schema": { + "$ref": "#/definitions/model.CspPolicyFilter" + } } ], "responses": { - "404": { - "description": "error: Credential not found", + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspPolicy" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -365,14 +1957,16 @@ } } } - }, - "delete": { + } + }, + "/api/csp-policies/role/{roleId}": { + "get": { "security": [ { "BearerAuth": [] } ], - "description": "CSP 인증 정보를 삭제합니다", + "description": "Get list of policies attached to a CSP role", "consumes": [ "application/json" ], @@ -380,25 +1974,31 @@ "application/json" ], "tags": [ - "csp-credentials" + "csp-policies" ], - "summary": "CSP 인증 정보 삭제", - "operationId": "mciamDeleteCredential", + "summary": "Get policies attached to role", + "operationId": "getRolePolicies", "parameters": [ { "type": "string", - "description": "Credential ID", - "name": "id", + "description": "Role ID", + "name": "roleId", "in": "path", "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspPolicy" + } + } }, - "401": { - "description": "error: Unauthorized", + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -406,8 +2006,60 @@ } } }, - "403": { - "description": "error: Forbidden", + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/csp-policies/sync": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Synchronize policies from the CSP cloud", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "csp-policies" + ], + "summary": "Sync CSP policies from cloud", + "operationId": "syncCspPolicies", + "parameters": [ + { + "description": "Sync Policies Request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.SyncPoliciesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CspPolicy" + } + } + }, + "400": { + "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { @@ -415,8 +2067,8 @@ } } }, - "404": { - "description": "error: Credential not found", + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -652,41 +2304,135 @@ } } } - }, - "delete": { - "description": "Deletes a mapping between a permission and an API action", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "mcmp-api-permission-action-mappings" - ], - "summary": "Delete permission-action mapping", - "operationId": "deleteMapping", - "parameters": [ - { - "type": "string", - "description": "Permission ID", - "name": "permissionId", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Action ID", - "name": "actionId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + "delete": { + "description": "Deletes a mapping between a permission and an API action", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mcmp-api-permission-action-mappings" + ], + "summary": "Delete permission-action mapping", + "operationId": "deleteMapping", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Action ID", + "name": "actionId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions": { + "get": { + "description": "Returns all platform actions mapped to a specific permission", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mcmp-api-permission-action-mappings" + ], + "summary": "Get platform actions by permission ID", + "operationId": "getPlatformActionsByPermissionID", + "parameters": [ + { + "type": "string", + "description": "Permission ID", + "name": "permissionId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/mcmpapi.McmpApiAction" + } + } + } + } + } + }, + "/api/mcmp-apis/import": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Fetches API specifications from remote URLs and imports them to the database. Supports swagger and openapi source types.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "McmpAPI" + ], + "summary": "Import MCMP APIs from Remote Sources", + "operationId": "importAPIs", + "parameters": [ + { + "description": "Frameworks to import", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.ImportApiRequest" + } + } + ], + "responses": { + "200": { + "description": "Import results", + "schema": { + "$ref": "#/definitions/model.ImportApiResponse" + } + }, + "400": { + "description": "error: Invalid request body", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Failed to import APIs", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } } }, "/api/mcmp-apis/list": { @@ -963,42 +2709,6 @@ } } }, - "/api/mcmp-apis/permission-action-mappings/id/{id}": { - "get": { - "description": "Returns all platform actions mapped to a specific permission", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "mcmp-api-permission-action-mappings" - ], - "summary": "Get platform actions by permission ID", - "operationId": "getPlatformActionsByPermissionID", - "parameters": [ - { - "type": "string", - "description": "Permission ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/mcmpapi.McmpApiAction" - } - } - } - } - } - }, "/api/mcmp-apis/syncMcmpAPIs": { "post": { "security": [ @@ -2003,7 +3713,7 @@ "BearerAuth": [] } ], - "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", + "description": "Create a new project with the specified information.", "consumes": [ "application/json" ], @@ -2022,7 +3732,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.CreateProjectRequest" + "$ref": "#/definitions/model.Project" } } ], @@ -2042,15 +3752,6 @@ } } }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, "500": { "description": "Internal Server Error", "schema": { @@ -2063,138 +3764,6 @@ } } }, - "/api/projects/assign/workspaces": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "프로젝트에 워크스페이스를 연결합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "operationId": "addWorkspaceToProject", - "parameters": [ - { - "description": "Workspace and Project IDs", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/projects/id/{projectId}/workspaces": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Retrieve list of workspaces that the project is assigned to", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "Get workspaces assigned to project", - "operationId": "getProjectWorkspaces", - "parameters": [ - { - "type": "string", - "description": "Project ID", - "name": "projectId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Workspace" - } - } - }, - "400": { - "description": "error: Invalid project ID", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: Project not found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: Internal server error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/api/projects/list": { "post": { "security": [ @@ -2271,63 +3840,8 @@ "$ref": "#/definitions/model.Project" } }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/projects/unassign/workspaces": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Remove a workspace from a project", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "Remove workspace from project", - "operationId": "removeWorkspaceFromProject", - "parameters": [ - { - "description": "Workspace and Project IDs", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: Invalid request", + "404": { + "description": "Not Found", "schema": { "type": "object", "additionalProperties": { @@ -2336,7 +3850,7 @@ } }, "500": { - "description": "error: Internal server error", + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -2526,6 +4040,75 @@ } } }, + "/api/projects/{id}/workspaces/{workspaceId}": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "프로젝트에 워크스페이스를 연결합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", + "parameters": [ + { + "type": "integer", + "description": "프로젝트 ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "워크스페이스 ID", + "name": "workspaceId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: 잘못된 ID 형식", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/resource-types/cloud-resources": { "post": { "security": [ @@ -4931,7 +6514,7 @@ "roles" ], "summary": "List workspace roles", - "operationId": "listWorkspaceRoles", + "operationId": "listRolesOfWorkspaceType", "responses": { "200": { "description": "OK", @@ -6116,7 +7699,7 @@ "BearerAuth": [] } ], - "description": "Create a new workspace with the specified information. Optionally assign existing projects to the workspace.", + "description": "Create a new workspace with the specified information.", "consumes": [ "application/json" ], @@ -6798,6 +8381,67 @@ } } }, + "/api/workspaces/roles/list": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve all workspace-level roles with optional filtering", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspace roles", + "operationId": "listWorkspaceRoles", + "parameters": [ + { + "description": "Role filter parameters", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RoleFilterRequest" + } + } + ], + "responses": { + "200": { + "description": "Successfully retrieved workspace roles", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleMaster" + } + } + }, + "400": { + "description": "error: Invalid request format", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Failed to retrieve workspace roles", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/workspaces/temporary-credentials": { "post": { "security": [ @@ -6984,8 +8628,160 @@ } } }, - "401": { - "description": "error: Unauthorized", + "401": { + "description": "error: Unauthorized", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a user to a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Add user to workspace", + "operationId": "addUserToWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.AssignRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/workspaces/{id}/users/{userId}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a user from a workspace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Remove user from workspace", + "operationId": "removeUserFromWorkspace", + "parameters": [ + { + "type": "string", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User ID", + "name": "userId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { @@ -7268,6 +9064,139 @@ } } }, + "model.AttachPolicyRequest": { + "type": "object", + "required": [ + "csp_policy_id", + "csp_role_id" + ], + "properties": { + "csp_policy_id": { + "type": "integer" + }, + "csp_role_id": { + "type": "integer" + } + } + }, + "model.AuthMethodType": { + "type": "string", + "enum": [ + "OIDC", + "SAML", + "SECRET_KEY" + ], + "x-enum-varnames": [ + "AuthMethodOIDC", + "AuthMethodSAML", + "AuthMethodSecretKey" + ] + }, + "model.CreateCspAccountRequest": { + "type": "object", + "required": [ + "csp_type", + "name" + ], + "properties": { + "account_info": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "csp_type": { + "type": "string", + "enum": [ + "aws", + "gcp", + "azure" + ] + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "model.CreateCspIdpConfigRequest": { + "type": "object", + "required": [ + "auth_method", + "config", + "csp_account_id", + "name" + ], + "properties": { + "auth_method": { + "enum": [ + "OIDC", + "SAML", + "SECRET_KEY" + ], + "allOf": [ + { + "$ref": "#/definitions/model.AuthMethodType" + } + ] + }, + "config": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "csp_account_id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "model.CreateCspPolicyRequest": { + "type": "object", + "required": [ + "csp_account_id", + "name", + "policy_type" + ], + "properties": { + "csp_account_id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "policy_arn": { + "type": "string" + }, + "policy_doc": { + "type": "object", + "additionalProperties": true + }, + "policy_type": { + "enum": [ + "inline", + "managed", + "custom" + ], + "allOf": [ + { + "$ref": "#/definitions/model.PolicyType" + } + ] + } + } + }, "model.CreateCspRoleRequest": { "type": "object", "properties": { @@ -7293,103 +9222,247 @@ "idpIdentifier": { "type": "string" }, - "path": { + "path": { + "type": "string" + }, + "status": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Tag" + } + } + } + }, + "model.CreateCspRolesRequest": { + "type": "object", + "required": [ + "cspRoles" + ], + "properties": { + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" + } + } + } + }, + "model.CreateMenuMappingRequest": { + "type": "object", + "required": [ + "menuIds", + "roleId" + ], + "properties": { + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "roleId": { + "type": "string" + } + } + }, + "model.CreateRoleRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "cspRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.CreateCspRoleRequest" + } + }, + "description": { + "type": "string" + }, + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "integer" + }, + "roleTypes": { + "description": "RoleTypes []constants.IAMRoleType `json:\"roleTypes\" validate:\"required,dive,oneof=platform workspace csp\"`", + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" + } + } + } + }, + "model.CspAccount": { + "type": "object", + "properties": { + "account_info": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "csp_type": { + "description": "aws, gcp, azure", + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "model.CspAccountFilter": { + "type": "object", + "properties": { + "csp_type": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "model.CspIdpConfig": { + "type": "object", + "properties": { + "auth_method": { + "description": "OIDC, SAML, SECRET_KEY", + "allOf": [ + { + "$ref": "#/definitions/model.AuthMethodType" + } + ] + }, + "config": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "csp_account": { + "$ref": "#/definitions/model.CspAccount" + }, + "csp_account_id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "name": { "type": "string" }, - "status": { + "updated_at": { "type": "string" - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Tag" - } - } - } - }, - "model.CreateCspRolesRequest": { - "type": "object", - "required": [ - "cspRoles" - ], - "properties": { - "cspRoles": { - "type": "array", - "items": { - "$ref": "#/definitions/model.CreateCspRoleRequest" - } } } }, - "model.CreateMenuMappingRequest": { + "model.CspIdpConfigFilter": { "type": "object", - "required": [ - "menuIds", - "roleId" - ], "properties": { - "menuIds": { - "type": "array", - "items": { - "type": "string" - } + "auth_method": { + "$ref": "#/definitions/model.AuthMethodType" }, - "roleId": { + "csp_account_id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "name": { "type": "string" } } }, - "model.CreateProjectRequest": { + "model.CspPolicy": { "type": "object", - "required": [ - "name" - ], "properties": { + "created_at": { + "type": "string" + }, + "csp_account": { + "$ref": "#/definitions/model.CspAccount" + }, + "csp_account_id": { + "type": "integer" + }, "description": { "type": "string" }, + "id": { + "type": "integer" + }, "name": { "type": "string" }, - "workspaceId": { - "description": "optional workspace to assign project to", + "policy_arn": { + "type": "string" + }, + "policy_doc": { + "type": "object", + "additionalProperties": true + }, + "policy_type": { + "description": "inline, managed, custom", + "allOf": [ + { + "$ref": "#/definitions/model.PolicyType" + } + ] + }, + "updated_at": { "type": "string" } } }, - "model.CreateRoleRequest": { + "model.CspPolicyFilter": { "type": "object", - "required": [ - "name" - ], "properties": { - "cspRoles": { - "type": "array", - "items": { - "$ref": "#/definitions/model.CreateCspRoleRequest" - } - }, - "description": { - "type": "string" - }, - "menuIds": { - "type": "array", - "items": { - "type": "string" - } + "csp_account_id": { + "type": "integer" }, "name": { "type": "string" }, - "parentId": { - "type": "integer" - }, - "roleTypes": { - "description": "RoleTypes []constants.IAMRoleType `json:\"roleTypes\" validate:\"required,dive,oneof=platform workspace csp\"`", - "type": "array", - "items": { - "$ref": "#/definitions/constants.IAMRoleType" - } + "policy_type": { + "$ref": "#/definitions/model.PolicyType" } } }, @@ -7402,6 +9475,19 @@ "created_at": { "type": "string" }, + "csp_account": { + "$ref": "#/definitions/model.CspAccount" + }, + "csp_account_id": { + "description": "CSP 계정 및 IDP 설정 참조 (신규 추가)", + "type": "integer" + }, + "csp_idp_config": { + "$ref": "#/definitions/model.CspIdpConfig" + }, + "csp_idp_config_id": { + "type": "integer" + }, "csp_type": { "type": "string" }, @@ -7411,6 +9497,10 @@ "description": { "type": "string" }, + "extended_config": { + "type": "object", + "additionalProperties": true + }, "iam_identifier": { "type": "string" }, @@ -7502,6 +9592,101 @@ } } }, + "model.ImportApiFramework": { + "type": "object", + "required": [ + "name", + "sourceType", + "sourceUrl", + "version" + ], + "properties": { + "name": { + "description": "Framework name (e.g., \"mc-infra-manager\")", + "type": "string" + }, + "repository": { + "description": "Repository URL (e.g., \"https://github.com/...\")", + "type": "string" + }, + "sourceType": { + "description": "Source type: \"swagger\" or \"openapi\"", + "type": "string" + }, + "sourceUrl": { + "description": "URL to fetch the API specification from", + "type": "string" + }, + "version": { + "description": "Framework version (e.g., \"0.9.22\")", + "type": "string" + } + } + }, + "model.ImportApiFrameworkResult": { + "type": "object", + "properties": { + "actionCount": { + "description": "Number of actions imported (on success)", + "type": "integer" + }, + "errorMessage": { + "description": "Error message (on failure)", + "type": "string" + }, + "name": { + "description": "Framework name", + "type": "string" + }, + "success": { + "description": "Whether the import was successful", + "type": "boolean" + }, + "version": { + "description": "Framework version", + "type": "string" + } + } + }, + "model.ImportApiRequest": { + "type": "object", + "required": [ + "frameworks" + ], + "properties": { + "frameworks": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/model.ImportApiFramework" + } + } + } + }, + "model.ImportApiResponse": { + "type": "object", + "properties": { + "failureCount": { + "description": "Number of failed frameworks", + "type": "integer" + }, + "frameworkResults": { + "description": "Detailed results for each framework", + "type": "array", + "items": { + "$ref": "#/definitions/model.ImportApiFrameworkResult" + } + }, + "successCount": { + "description": "Number of successfully imported frameworks", + "type": "integer" + }, + "totalFrameworks": { + "description": "Total number of frameworks in request", + "type": "integer" + } + } + }, "model.MciamPermission": { "type": "object", "properties": { @@ -7643,6 +9828,29 @@ } } }, + "model.PolicyType": { + "type": "string", + "enum": [ + "inline", + "managed", + "custom" + ], + "x-enum-comments": { + "PolicyTypeCustom": "사용자 정의 정책", + "PolicyTypeInline": "인라인 정책 (역할에 직접 포함)", + "PolicyTypeManaged": "관리형 정책 (독립 정책)" + }, + "x-enum-descriptions": [ + "인라인 정책 (역할에 직접 포함)", + "관리형 정책 (독립 정책)", + "사용자 정의 정책" + ], + "x-enum-varnames": [ + "PolicyTypeInline", + "PolicyTypeManaged", + "PolicyTypeCustom" + ] + }, "model.Project": { "type": "object", "properties": { @@ -7711,6 +9919,23 @@ } } }, + "model.RoleFilterRequest": { + "type": "object", + "properties": { + "roleId": { + "type": "string" + }, + "roleName": { + "type": "string" + }, + "roleTypes": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.IAMRoleType" + } + } + } + }, "model.RoleLastUsed": { "type": "object", "properties": { @@ -7876,6 +10101,21 @@ } } }, + "model.SyncPoliciesRequest": { + "type": "object", + "required": [ + "csp_account_id" + ], + "properties": { + "csp_account_id": { + "type": "integer" + }, + "policy_scope": { + "description": "All, AWS, Local", + "type": "string" + } + } + }, "model.Tag": { "type": "object", "properties": { @@ -7887,6 +10127,64 @@ } } }, + "model.UpdateCspAccountRequest": { + "type": "object", + "properties": { + "account_info": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "model.UpdateCspIdpConfigRequest": { + "type": "object", + "properties": { + "config": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "model.UpdateCspPolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "policy_arn": { + "type": "string" + }, + "policy_doc": { + "type": "object", + "additionalProperties": true + } + } + }, "model.User": { "type": "object", "properties": { @@ -8038,24 +10336,6 @@ } } }, - "model.WorkspaceProjectMappingRequest": { - "type": "object", - "required": [ - "projectIds", - "workspaceId" - ], - "properties": { - "projectIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "workspaceId": { - "type": "string" - } - } - }, "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 4c6f0c4b..969050e0 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -162,6 +162,96 @@ definitions: description: 워크스페이스 ID (문자열로 받음) type: string type: object + model.AttachPolicyRequest: + properties: + csp_policy_id: + type: integer + csp_role_id: + type: integer + required: + - csp_policy_id + - csp_role_id + type: object + model.AuthMethodType: + enum: + - OIDC + - SAML + - SECRET_KEY + type: string + x-enum-varnames: + - AuthMethodOIDC + - AuthMethodSAML + - AuthMethodSecretKey + model.CreateCspAccountRequest: + properties: + account_info: + additionalProperties: + type: string + type: object + csp_type: + enum: + - aws + - gcp + - azure + type: string + description: + type: string + name: + type: string + required: + - csp_type + - name + type: object + model.CreateCspIdpConfigRequest: + properties: + auth_method: + allOf: + - $ref: '#/definitions/model.AuthMethodType' + enum: + - OIDC + - SAML + - SECRET_KEY + config: + additionalProperties: + type: string + type: object + csp_account_id: + type: integer + description: + type: string + name: + type: string + required: + - auth_method + - config + - csp_account_id + - name + type: object + model.CreateCspPolicyRequest: + properties: + csp_account_id: + type: integer + description: + type: string + name: + type: string + policy_arn: + type: string + policy_doc: + additionalProperties: true + type: object + policy_type: + allOf: + - $ref: '#/definitions/model.PolicyType' + enum: + - inline + - managed + - custom + required: + - csp_account_id + - name + - policy_type + type: object model.CreateCspRoleRequest: properties: cspRoleName: @@ -209,18 +299,6 @@ definitions: - menuIds - roleId type: object - model.CreateProjectRequest: - properties: - description: - type: string - name: - type: string - workspaceId: - description: optional workspace to assign project to - type: string - required: - - name - type: object model.CreateRoleRequest: properties: cspRoles: @@ -246,18 +324,134 @@ definitions: required: - name type: object + model.CspAccount: + properties: + account_info: + additionalProperties: + type: string + type: object + created_at: + type: string + csp_type: + description: aws, gcp, azure + type: string + description: + type: string + id: + type: integer + is_active: + type: boolean + name: + type: string + updated_at: + type: string + type: object + model.CspAccountFilter: + properties: + csp_type: + type: string + is_active: + type: boolean + name: + type: string + type: object + model.CspIdpConfig: + properties: + auth_method: + allOf: + - $ref: '#/definitions/model.AuthMethodType' + description: OIDC, SAML, SECRET_KEY + config: + additionalProperties: + type: string + type: object + created_at: + type: string + csp_account: + $ref: '#/definitions/model.CspAccount' + csp_account_id: + type: integer + description: + type: string + id: + type: integer + is_active: + type: boolean + name: + type: string + updated_at: + type: string + type: object + model.CspIdpConfigFilter: + properties: + auth_method: + $ref: '#/definitions/model.AuthMethodType' + csp_account_id: + type: integer + is_active: + type: boolean + name: + type: string + type: object + model.CspPolicy: + properties: + created_at: + type: string + csp_account: + $ref: '#/definitions/model.CspAccount' + csp_account_id: + type: integer + description: + type: string + id: + type: integer + name: + type: string + policy_arn: + type: string + policy_doc: + additionalProperties: true + type: object + policy_type: + allOf: + - $ref: '#/definitions/model.PolicyType' + description: inline, managed, custom + updated_at: + type: string + type: object + model.CspPolicyFilter: + properties: + csp_account_id: + type: integer + name: + type: string + policy_type: + $ref: '#/definitions/model.PolicyType' + type: object model.CspRole: properties: create_date: type: string created_at: type: string + csp_account: + $ref: '#/definitions/model.CspAccount' + csp_account_id: + description: CSP 계정 및 IDP 설정 참조 (신규 추가) + type: integer + csp_idp_config: + $ref: '#/definitions/model.CspIdpConfig' + csp_idp_config_id: + type: integer csp_type: type: string deleted_at: type: string description: type: string + extended_config: + additionalProperties: true + type: object iam_identifier: type: string iam_role_id: @@ -318,6 +512,74 @@ definitions: workspaceName: type: string type: object + model.ImportApiFramework: + properties: + name: + description: Framework name (e.g., "mc-infra-manager") + type: string + repository: + description: Repository URL (e.g., "https://github.com/...") + type: string + sourceType: + description: 'Source type: "swagger" or "openapi"' + type: string + sourceUrl: + description: URL to fetch the API specification from + type: string + version: + description: Framework version (e.g., "0.9.22") + type: string + required: + - name + - sourceType + - sourceUrl + - version + type: object + model.ImportApiFrameworkResult: + properties: + actionCount: + description: Number of actions imported (on success) + type: integer + errorMessage: + description: Error message (on failure) + type: string + name: + description: Framework name + type: string + success: + description: Whether the import was successful + type: boolean + version: + description: Framework version + type: string + type: object + model.ImportApiRequest: + properties: + frameworks: + items: + $ref: '#/definitions/model.ImportApiFramework' + minItems: 1 + type: array + required: + - frameworks + type: object + model.ImportApiResponse: + properties: + failureCount: + description: Number of failed frameworks + type: integer + frameworkResults: + description: Detailed results for each framework + items: + $ref: '#/definitions/model.ImportApiFrameworkResult' + type: array + successCount: + description: Number of successfully imported frameworks + type: integer + totalFrameworks: + description: Total number of frameworks in request + type: integer + type: object model.MciamPermission: properties: action: @@ -414,6 +676,24 @@ definitions: resType: type: string type: object + model.PolicyType: + enum: + - inline + - managed + - custom + type: string + x-enum-comments: + PolicyTypeCustom: 사용자 정의 정책 + PolicyTypeInline: 인라인 정책 (역할에 직접 포함) + PolicyTypeManaged: 관리형 정책 (독립 정책) + x-enum-descriptions: + - 인라인 정책 (역할에 직접 포함) + - 관리형 정책 (독립 정책) + - 사용자 정의 정책 + x-enum-varnames: + - PolicyTypeInline + - PolicyTypeManaged + - PolicyTypeCustom model.Project: properties: created_at: @@ -460,6 +740,17 @@ definitions: message: type: string type: object + model.RoleFilterRequest: + properties: + roleId: + type: string + roleName: + type: string + roleTypes: + items: + $ref: '#/definitions/constants.IAMRoleType' + type: array + type: object model.RoleLastUsed: properties: last_used_date: @@ -568,6 +859,16 @@ definitions: username: type: string type: object + model.SyncPoliciesRequest: + properties: + csp_account_id: + type: integer + policy_scope: + description: All, AWS, Local + type: string + required: + - csp_account_id + type: object model.Tag: properties: key: @@ -575,6 +876,44 @@ definitions: value: type: string type: object + model.UpdateCspAccountRequest: + properties: + account_info: + additionalProperties: + type: string + type: object + description: + type: string + is_active: + type: boolean + name: + type: string + type: object + model.UpdateCspIdpConfigRequest: + properties: + config: + additionalProperties: + type: string + type: object + description: + type: string + is_active: + type: boolean + name: + type: string + type: object + model.UpdateCspPolicyRequest: + properties: + description: + type: string + name: + type: string + policy_arn: + type: string + policy_doc: + additionalProperties: true + type: object + type: object model.User: properties: created_at: @@ -678,18 +1017,6 @@ definitions: updated_at: type: string type: object - model.WorkspaceProjectMappingRequest: - properties: - projectIds: - items: - type: string - type: array - workspaceId: - type: string - required: - - projectIds - - workspaceId - type: object model.WorkspaceWithUsersAndRoles: properties: created_at: @@ -872,43 +1199,918 @@ paths: summary: Validate access token tags: - auth - /api/csp-credentials: - get: + /api/csp-accounts: + post: consumes: - application/json - description: 모든 CSP 인증 정보 목록을 조회합니다 - operationId: mciamListCredentials + description: Create a new CSP account + operationId: createCspAccount + parameters: + - description: CSP Account Info + in: body + name: account + required: true + schema: + $ref: '#/definitions/model.CreateCspAccountRequest' produces: - application/json - responses: {} + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.CspAccount' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object security: - BearerAuth: [] - summary: CSP 인증 정보 목록 조회 + summary: Create CSP account tags: - - csp-credentials - post: + - csp-accounts + /api/csp-accounts/id/{accountId}: + delete: consumes: - application/json - description: 새로운 CSP 인증 정보를 생성합니다 - operationId: mciamCreateCredential + description: Delete a CSP account by ID + operationId: deleteCspAccount + parameters: + - description: Account ID + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Delete CSP account + tags: + - csp-accounts + get: + consumes: + - application/json + description: Retrieve CSP account details by ID + operationId: getCspAccountByID + parameters: + - description: Account ID + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.CspAccount' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get CSP account by ID + tags: + - csp-accounts + put: + consumes: + - application/json + description: Update CSP account details + operationId: updateCspAccount + parameters: + - description: Account ID + in: path + name: accountId + required: true + type: string + - description: CSP Account Info + in: body + name: account + required: true + schema: + $ref: '#/definitions/model.UpdateCspAccountRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.CspAccount' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Update CSP account + tags: + - csp-accounts + /api/csp-accounts/id/{accountId}/activate: + post: + consumes: + - application/json + description: Activate a CSP account + operationId: activateCspAccount + parameters: + - description: Account ID + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Activate CSP account + tags: + - csp-accounts + /api/csp-accounts/id/{accountId}/deactivate: + post: + consumes: + - application/json + description: Deactivate a CSP account + operationId: deactivateCspAccount + parameters: + - description: Account ID + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Deactivate CSP account + tags: + - csp-accounts + /api/csp-accounts/id/{accountId}/validate: + post: + consumes: + - application/json + description: Validate CSP account configuration + operationId: validateCspAccount + parameters: + - description: Account ID + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Validate CSP account + tags: + - csp-accounts + /api/csp-accounts/list: + post: + consumes: + - application/json + description: Retrieve a list of CSP accounts with optional filters + operationId: listCspAccounts + parameters: + - description: Filter options + in: body + name: filter + schema: + $ref: '#/definitions/model.CspAccountFilter' + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.CspAccount' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List CSP accounts + tags: + - csp-accounts + /api/csp-credentials: + get: + consumes: + - application/json + description: 모든 CSP 인증 정보 목록을 조회합니다 + operationId: mciamListCredentials + produces: + - application/json + responses: {} + security: + - BearerAuth: [] + summary: CSP 인증 정보 목록 조회 + tags: + - csp-credentials + post: + consumes: + - application/json + description: 새로운 CSP 인증 정보를 생성합니다 + operationId: mciamCreateCredential + produces: + - application/json + responses: {} + security: + - BearerAuth: [] + summary: 새 CSP 인증 정보 생성 + tags: + - csp-credentials + /api/csp-credentials/{id}: + delete: + consumes: + - application/json + description: CSP 인증 정보를 삭제합니다 + operationId: mciamDeleteCredential + parameters: + - description: Credential ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "401": + description: 'error: Unauthorized' + schema: + additionalProperties: + type: string + type: object + "403": + description: 'error: Forbidden' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Credential not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: CSP 인증 정보 삭제 + tags: + - csp-credentials + get: + consumes: + - application/json + description: 특정 CSP 인증 정보를 ID로 조회합니다 + operationId: mciamGetCredentialByID + parameters: + - description: Credential ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "404": + description: 'error: Credential not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: CSP 인증 정보 ID로 조회 + tags: + - csp-credentials + put: + consumes: + - application/json + description: CSP 인증 정보를 업데이트합니다 + operationId: mciamUpdateCredential + parameters: + - description: Credential ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "404": + description: 'error: Credential not found' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: CSP 인증 정보 업데이트 + tags: + - csp-credentials + /api/csp-idp-configs: + post: + consumes: + - application/json + description: Create a new CSP IDP configuration + operationId: createCspIdpConfig + parameters: + - description: CSP IDP Config Info + in: body + name: config + required: true + schema: + $ref: '#/definitions/model.CreateCspIdpConfigRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.CspIdpConfig' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create CSP IDP config + tags: + - csp-idp-configs + /api/csp-idp-configs/id/{configId}: + delete: + consumes: + - application/json + description: Delete a CSP IDP configuration by ID + operationId: deleteCspIdpConfig + parameters: + - description: Config ID + in: path + name: configId + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Delete CSP IDP config + tags: + - csp-idp-configs + get: + consumes: + - application/json + description: Retrieve CSP IDP configuration details by ID + operationId: getCspIdpConfigByID + parameters: + - description: Config ID + in: path + name: configId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.CspIdpConfig' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get CSP IDP config by ID + tags: + - csp-idp-configs + put: + consumes: + - application/json + description: Update CSP IDP configuration details + operationId: updateCspIdpConfig + parameters: + - description: Config ID + in: path + name: configId + required: true + type: string + - description: CSP IDP Config Info + in: body + name: config + required: true + schema: + $ref: '#/definitions/model.UpdateCspIdpConfigRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.CspIdpConfig' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Update CSP IDP config + tags: + - csp-idp-configs + /api/csp-idp-configs/id/{configId}/activate: + post: + consumes: + - application/json + description: Activate a CSP IDP configuration + operationId: activateCspIdpConfig + parameters: + - description: Config ID + in: path + name: configId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Activate CSP IDP config + tags: + - csp-idp-configs + /api/csp-idp-configs/id/{configId}/deactivate: + post: + consumes: + - application/json + description: Deactivate a CSP IDP configuration + operationId: deactivateCspIdpConfig + parameters: + - description: Config ID + in: path + name: configId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Deactivate CSP IDP config + tags: + - csp-idp-configs + /api/csp-idp-configs/id/{configId}/test: + post: + consumes: + - application/json + description: Test connection to CSP using IDP configuration + operationId: testCspIdpConnection + parameters: + - description: Config ID + in: path + name: configId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Test CSP IDP connection + tags: + - csp-idp-configs + /api/csp-idp-configs/list: + post: + consumes: + - application/json + description: Retrieve a list of CSP IDP configurations with optional filters + operationId: listCspIdpConfigs + parameters: + - description: Filter options + in: body + name: filter + schema: + $ref: '#/definitions/model.CspIdpConfigFilter' + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.CspIdpConfig' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List CSP IDP configs + tags: + - csp-idp-configs + /api/csp-policies: + post: + consumes: + - application/json + description: Create a new CSP policy + operationId: createCspPolicy + parameters: + - description: CSP Policy Info + in: body + name: policy + required: true + schema: + $ref: '#/definitions/model.CreateCspPolicyRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/model.CspPolicy' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create CSP policy + tags: + - csp-policies + /api/csp-policies/attach: + post: + consumes: + - application/json + description: Attach a CSP policy to a CSP role + operationId: attachPolicyToRole + parameters: + - description: Attach Policy Request + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AttachPolicyRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Attach policy to role + tags: + - csp-policies + /api/csp-policies/detach: + post: + consumes: + - application/json + description: Detach a CSP policy from a CSP role + operationId: detachPolicyFromRole + parameters: + - description: Detach Policy Request + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AttachPolicyRequest' produces: - application/json - responses: {} + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object security: - BearerAuth: [] - summary: 새 CSP 인증 정보 생성 + summary: Detach policy from role tags: - - csp-credentials - /api/csp-credentials/{id}: + - csp-policies + /api/csp-policies/id/{policyId}: delete: consumes: - application/json - description: CSP 인증 정보를 삭제합니다 - operationId: mciamDeleteCredential + description: Delete a CSP policy by ID + operationId: deleteCspPolicy parameters: - - description: Credential ID + - description: Policy ID in: path - name: id + name: policyId required: true type: string produces: @@ -916,79 +2118,269 @@ paths: responses: "204": description: No Content - "401": - description: 'error: Unauthorized' + "400": + description: Bad Request schema: additionalProperties: type: string type: object - "403": - description: 'error: Forbidden' + "404": + description: Not Found schema: additionalProperties: type: string type: object - "404": - description: 'error: Credential not found' + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: CSP 인증 정보 삭제 + summary: Delete CSP policy tags: - - csp-credentials + - csp-policies get: consumes: - application/json - description: 특정 CSP 인증 정보를 ID로 조회합니다 - operationId: mciamGetCredentialByID + description: Retrieve CSP policy details by ID + operationId: getCspPolicyByID parameters: - - description: Credential ID + - description: Policy ID in: path - name: id + name: policyId required: true type: string produces: - application/json responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.CspPolicy' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object "404": - description: 'error: Credential not found' + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: CSP 인증 정보 ID로 조회 + summary: Get CSP policy by ID tags: - - csp-credentials + - csp-policies put: consumes: - application/json - description: CSP 인증 정보를 업데이트합니다 - operationId: mciamUpdateCredential + description: Update CSP policy details + operationId: updateCspPolicy parameters: - - description: Credential ID + - description: Policy ID in: path - name: id + name: policyId required: true type: string + - description: CSP Policy Info + in: body + name: policy + required: true + schema: + $ref: '#/definitions/model.UpdateCspPolicyRequest' produces: - application/json responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.CspPolicy' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object "404": - description: 'error: Credential not found' + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error schema: additionalProperties: type: string type: object security: - BearerAuth: [] - summary: CSP 인증 정보 업데이트 + summary: Update CSP policy tags: - - csp-credentials + - csp-policies + /api/csp-policies/id/{policyId}/document: + get: + consumes: + - application/json + description: Get the policy document content + operationId: getPolicyDocument + parameters: + - description: Policy ID + in: path + name: policyId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get policy document + tags: + - csp-policies + /api/csp-policies/list: + post: + consumes: + - application/json + description: Retrieve a list of CSP policies with optional filters + operationId: listCspPolicies + parameters: + - description: Filter options + in: body + name: filter + schema: + $ref: '#/definitions/model.CspPolicyFilter' + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.CspPolicy' + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List CSP policies + tags: + - csp-policies + /api/csp-policies/role/{roleId}: + get: + consumes: + - application/json + description: Get list of policies attached to a CSP role + operationId: getRolePolicies + parameters: + - description: Role ID + in: path + name: roleId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.CspPolicy' + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get policies attached to role + tags: + - csp-policies + /api/csp-policies/sync: + post: + consumes: + - application/json + description: Synchronize policies from the CSP cloud + operationId: syncCspPolicies + parameters: + - description: Sync Policies Request + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.SyncPoliciesRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.CspPolicy' + type: array + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Sync CSP policies from cloud + tags: + - csp-policies /api/initial-admin: post: consumes: @@ -1149,22 +2541,84 @@ paths: type: integer - description: Updated mapping in: body - name: mapping + name: mapping + required: true + schema: + $ref: '#/definitions/mcmpapi.McmpApiPermissionActionMapping' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + summary: Update permission-action mapping + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions: + get: + consumes: + - application/json + description: Returns all platform actions mapped to a specific permission + operationId: getPlatformActionsByPermissionID + parameters: + - description: Permission ID + in: path + name: permissionId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/mcmpapi.McmpApiAction' + type: array + summary: Get platform actions by permission ID + tags: + - mcmp-api-permission-action-mappings + /api/mcmp-apis/import: + post: + consumes: + - application/json + description: Fetches API specifications from remote URLs and imports them to + the database. Supports swagger and openapi source types. + operationId: importAPIs + parameters: + - description: Frameworks to import + in: body + name: request required: true schema: - $ref: '#/definitions/mcmpapi.McmpApiPermissionActionMapping' + $ref: '#/definitions/model.ImportApiRequest' produces: - application/json responses: "200": - description: OK + description: Import results + schema: + $ref: '#/definitions/model.ImportApiResponse' + "400": + description: 'error: Invalid request body' schema: additionalProperties: type: string type: object - summary: Update permission-action mapping + "500": + description: 'error: Failed to import APIs' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Import MCMP APIs from Remote Sources tags: - - mcmp-api-permission-action-mappings + - McmpAPI /api/mcmp-apis/list: post: consumes: @@ -1347,30 +2801,6 @@ paths: summary: Set Active Version for a Service tags: - McmpAPI - /api/mcmp-apis/permission-action-mappings/id/{id}: - get: - consumes: - - application/json - description: Returns all platform actions mapped to a specific permission - operationId: getPlatformActionsByPermissionID - parameters: - - description: Permission ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/mcmpapi.McmpApiAction' - type: array - summary: Get platform actions by permission ID - tags: - - mcmp-api-permission-action-mappings /api/mcmp-apis/syncMcmpAPIs: post: consumes: @@ -2022,8 +3452,7 @@ paths: post: consumes: - application/json - description: Create a new project with the specified information. Optionally - specify a workspace to assign the project to. + description: Create a new project with the specified information. operationId: createProject parameters: - description: Project Info @@ -2031,7 +3460,7 @@ paths: name: project required: true schema: - $ref: '#/definitions/model.CreateProjectRequest' + $ref: '#/definitions/model.Project' produces: - application/json responses: @@ -2045,12 +3474,6 @@ paths: additionalProperties: type: string type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object "500": description: Internal Server Error schema: @@ -2178,19 +3601,23 @@ paths: summary: Update project tags: - projects - /api/projects/assign/workspaces: + /api/projects/{id}/workspaces/{workspaceId}: post: consumes: - application/json description: 프로젝트에 워크스페이스를 연결합니다. operationId: addWorkspaceToProject parameters: - - description: Workspace and Project IDs - in: body - name: request + - description: 프로젝트 ID + in: path + name: id required: true - schema: - $ref: '#/definitions/model.WorkspaceProjectMappingRequest' + type: integer + - description: 워크스페이스 ID + in: path + name: workspaceId + required: true + type: integer produces: - application/json responses: @@ -2219,50 +3646,6 @@ paths: summary: 프로젝트에 워크스페이스 연결 tags: - projects - /api/projects/id/{projectId}/workspaces: - get: - consumes: - - application/json - description: Retrieve list of workspaces that the project is assigned to - operationId: getProjectWorkspaces - parameters: - - description: Project ID - in: path - name: projectId - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/model.Workspace' - type: array - "400": - description: 'error: Invalid project ID' - schema: - additionalProperties: - type: string - type: object - "404": - description: 'error: Project not found' - schema: - additionalProperties: - type: string - type: object - "500": - description: 'error: Internal server error' - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Get workspaces assigned to project - tags: - - projects /api/projects/list: post: consumes: @@ -2325,41 +3708,6 @@ paths: summary: Get project by name tags: - projects - /api/projects/unassign/workspaces: - delete: - consumes: - - application/json - description: Remove a workspace from a project - operationId: removeWorkspaceFromProject - parameters: - - description: Workspace and Project IDs - in: body - name: request - required: true - schema: - $ref: '#/definitions/model.WorkspaceProjectMappingRequest' - produces: - - application/json - responses: - "204": - description: No Content - "400": - description: 'error: Invalid request' - schema: - additionalProperties: - type: string - type: object - "500": - description: 'error: Internal server error' - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Remove workspace from project - tags: - - projects /api/resource-types/cloud-resources: post: consumes: @@ -3988,7 +5336,7 @@ paths: consumes: - application/json description: Get a list of all workspace roles - operationId: listWorkspaceRoles + operationId: listRolesOfWorkspaceType produces: - application/json responses: @@ -4671,8 +6019,7 @@ paths: post: consumes: - application/json - description: Create a new workspace with the specified information. Optionally - assign existing projects to the workspace. + description: Create a new workspace with the specified information. operationId: createWorkspace parameters: - description: Workspace Info @@ -4705,6 +6052,105 @@ paths: summary: Create new workspace tags: - workspaces + /api/workspaces/{id}/users: + post: + consumes: + - application/json + description: Add a user to a workspace + operationId: addUserToWorkspace + parameters: + - description: Workspace ID + in: path + name: id + required: true + type: string + - description: User Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.AssignRoleRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Add user to workspace + tags: + - workspaces + /api/workspaces/{id}/users/{userId}: + delete: + consumes: + - application/json + description: Remove a user from a workspace + operationId: removeUserFromWorkspace + parameters: + - description: Workspace ID + in: path + name: id + required: true + type: string + - description: User ID + in: path + name: userId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Remove user from workspace + tags: + - workspaces /api/workspaces/assign/projects: post: consumes: @@ -5115,6 +6561,45 @@ paths: summary: List workspace projects tags: - workspaces + /api/workspaces/roles/list: + post: + consumes: + - application/json + description: Retrieve all workspace-level roles with optional filtering + operationId: listWorkspaceRoles + parameters: + - description: Role filter parameters + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.RoleFilterRequest' + produces: + - application/json + responses: + "200": + description: Successfully retrieved workspace roles + schema: + items: + $ref: '#/definitions/model.RoleMaster' + type: array + "400": + description: 'error: Invalid request format' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Failed to retrieve workspace roles' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: List workspace roles + tags: + - workspaces /api/workspaces/temporary-credentials: post: consumes: diff --git a/src/go.sum b/src/go.sum index 150778ef..d8bc0a46 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,3 +1,5 @@ +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= @@ -49,6 +51,7 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -61,6 +64,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -74,6 +79,10 @@ github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRj github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= 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= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -88,8 +97,6 @@ github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrk github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= @@ -124,14 +131,16 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= +github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -151,6 +160,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= @@ -181,8 +192,6 @@ golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= @@ -218,6 +227,8 @@ gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= +gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g= +gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw= gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= diff --git a/src/handler/auth_handler.go b/src/handler/auth_handler.go index e8ef8aa6..f59a0da1 100755 --- a/src/handler/auth_handler.go +++ b/src/handler/auth_handler.go @@ -110,32 +110,6 @@ func (h *AuthHandler) Login(c echo.Context) error { return c.JSON(http.StatusOK, token) } -// Callback godoc -// @Summary OIDC callback -// @Description Process callback after OIDC authentication -// @Param code query string true "Authentication code" -// @Param state query string true "State" -// @Success 200 {object} map[string]interface{} -// @Router /auth/callback [get] -// func (h *AuthHandler) Callback(c echo.Context) error { -// code := c.QueryParam("code") -// state := c.QueryParam("state") - -// if state != "state" { -// return c.JSON(http.StatusBadRequest, map[string]string{ -// "error": "Invalid state value", -// }) -// } - -// token, err := h.config.Exchange(c.Request().Context(), code) -// if err != nil { -// return c.JSON(http.StatusInternalServerError, map[string]string{ -// "error": "Token exchange failed", -// }) -// } - -// return c.JSON(http.StatusOK, token) -// } // Logout godoc // @Summary Logout user diff --git a/src/handler/csp_account_handler.go b/src/handler/csp_account_handler.go new file mode 100644 index 00000000..880520c5 --- /dev/null +++ b/src/handler/csp_account_handler.go @@ -0,0 +1,274 @@ +package handler + +import ( + "fmt" + "net/http" + + "github.com/labstack/echo/v4" + "github.com/m-cmp/mc-iam-manager/model" + "github.com/m-cmp/mc-iam-manager/service" + "github.com/m-cmp/mc-iam-manager/util" + "gorm.io/gorm" +) + +// CspAccountHandler CSP 계정 관리 핸들러 +type CspAccountHandler struct { + cspAccountService *service.CspAccountService +} + +// NewCspAccountHandler 새 CspAccountHandler 인스턴스 생성 +func NewCspAccountHandler(db *gorm.DB) *CspAccountHandler { + return &CspAccountHandler{ + cspAccountService: service.NewCspAccountService(db), + } +} + +// CreateCspAccount godoc +// @Summary Create CSP account +// @Description Create a new CSP account +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param account body model.CreateCspAccountRequest true "CSP Account Info" +// @Success 201 {object} model.CspAccount +// @Failure 400 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts [post] +// @Id createCspAccount +func (h *CspAccountHandler) CreateCspAccount(c echo.Context) error { + var req model.CreateCspAccountRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + // 필수 필드 검증 + if req.Name == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Name is required"}) + } + if req.CspType == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP type is required"}) + } + if req.CspType != "aws" && req.CspType != "gcp" && req.CspType != "azure" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid CSP type. Must be one of: aws, gcp, azure"}) + } + + account, err := h.cspAccountService.CreateCspAccount(&req) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to create CSP account: %v", err)}) + } + + return c.JSON(http.StatusCreated, account) +} + +// ListCspAccounts godoc +// @Summary List CSP accounts +// @Description Retrieve a list of CSP accounts with optional filters +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param filter body model.CspAccountFilter false "Filter options" +// @Success 200 {array} model.CspAccount +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts/list [post] +// @Id listCspAccounts +func (h *CspAccountHandler) ListCspAccounts(c echo.Context) error { + var filter model.CspAccountFilter + if err := c.Bind(&filter); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + accounts, err := h.cspAccountService.ListCspAccounts(&filter) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to list CSP accounts: %v", err)}) + } + + if accounts == nil { + accounts = []*model.CspAccount{} + } + + return c.JSON(http.StatusOK, accounts) +} + +// GetCspAccountByID godoc +// @Summary Get CSP account by ID +// @Description Retrieve CSP account details by ID +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param accountId path string true "Account ID" +// @Success 200 {object} model.CspAccount +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts/id/{accountId} [get] +// @Id getCspAccountByID +func (h *CspAccountHandler) GetCspAccountByID(c echo.Context) error { + accountID, err := util.StringToUint(c.Param("accountId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid account ID"}) + } + + account, err := h.cspAccountService.GetCspAccountByID(accountID) + if err != nil { + if err.Error() == fmt.Sprintf("CSP account not found with ID: %d", accountID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to get CSP account: %v", err)}) + } + + return c.JSON(http.StatusOK, account) +} + +// UpdateCspAccount godoc +// @Summary Update CSP account +// @Description Update CSP account details +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param accountId path string true "Account ID" +// @Param account body model.UpdateCspAccountRequest true "CSP Account Info" +// @Success 200 {object} model.CspAccount +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts/id/{accountId} [put] +// @Id updateCspAccount +func (h *CspAccountHandler) UpdateCspAccount(c echo.Context) error { + accountID, err := util.StringToUint(c.Param("accountId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid account ID"}) + } + + var req model.UpdateCspAccountRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + account, err := h.cspAccountService.UpdateCspAccount(accountID, &req) + if err != nil { + if err.Error() == fmt.Sprintf("CSP account not found with ID: %d", accountID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to update CSP account: %v", err)}) + } + + return c.JSON(http.StatusOK, account) +} + +// DeleteCspAccount godoc +// @Summary Delete CSP account +// @Description Delete a CSP account by ID +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param accountId path string true "Account ID" +// @Success 204 "No Content" +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts/id/{accountId} [delete] +// @Id deleteCspAccount +func (h *CspAccountHandler) DeleteCspAccount(c echo.Context) error { + accountID, err := util.StringToUint(c.Param("accountId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid account ID"}) + } + + if err := h.cspAccountService.DeleteCspAccount(accountID); err != nil { + if err.Error() == fmt.Sprintf("CSP account not found with ID: %d", accountID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to delete CSP account: %v", err)}) + } + + return c.NoContent(http.StatusNoContent) +} + +// ValidateCspAccount godoc +// @Summary Validate CSP account +// @Description Validate CSP account configuration +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param accountId path string true "Account ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts/id/{accountId}/validate [post] +// @Id validateCspAccount +func (h *CspAccountHandler) ValidateCspAccount(c echo.Context) error { + accountID, err := util.StringToUint(c.Param("accountId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid account ID"}) + } + + if err := h.cspAccountService.ValidateCspAccount(accountID); err != nil { + if err.Error() == fmt.Sprintf("CSP account not found with ID: %d", accountID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("Validation failed: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "CSP account is valid"}) +} + +// ActivateCspAccount godoc +// @Summary Activate CSP account +// @Description Activate a CSP account +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param accountId path string true "Account ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts/id/{accountId}/activate [post] +// @Id activateCspAccount +func (h *CspAccountHandler) ActivateCspAccount(c echo.Context) error { + accountID, err := util.StringToUint(c.Param("accountId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid account ID"}) + } + + if err := h.cspAccountService.ActivateCspAccount(accountID); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to activate CSP account: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "CSP account activated successfully"}) +} + +// DeactivateCspAccount godoc +// @Summary Deactivate CSP account +// @Description Deactivate a CSP account +// @Tags csp-accounts +// @Accept json +// @Produce json +// @Param accountId path string true "Account ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-accounts/id/{accountId}/deactivate [post] +// @Id deactivateCspAccount +func (h *CspAccountHandler) DeactivateCspAccount(c echo.Context) error { + accountID, err := util.StringToUint(c.Param("accountId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid account ID"}) + } + + if err := h.cspAccountService.DeactivateCspAccount(accountID); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to deactivate CSP account: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "CSP account deactivated successfully"}) +} diff --git a/src/handler/csp_idp_config_handler.go b/src/handler/csp_idp_config_handler.go new file mode 100644 index 00000000..4b5b5336 --- /dev/null +++ b/src/handler/csp_idp_config_handler.go @@ -0,0 +1,281 @@ +package handler + +import ( + "fmt" + "net/http" + + "github.com/labstack/echo/v4" + "github.com/m-cmp/mc-iam-manager/model" + "github.com/m-cmp/mc-iam-manager/service" + "github.com/m-cmp/mc-iam-manager/util" + "gorm.io/gorm" +) + +// CspIdpConfigHandler CSP IDP 설정 관리 핸들러 +type CspIdpConfigHandler struct { + cspIdpConfigService *service.CspIdpConfigService +} + +// NewCspIdpConfigHandler 새 CspIdpConfigHandler 인스턴스 생성 +func NewCspIdpConfigHandler(db *gorm.DB) *CspIdpConfigHandler { + keycloakService := service.NewKeycloakService() + return &CspIdpConfigHandler{ + cspIdpConfigService: service.NewCspIdpConfigService(db, keycloakService), + } +} + +// CreateCspIdpConfig godoc +// @Summary Create CSP IDP config +// @Description Create a new CSP IDP configuration +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param config body model.CreateCspIdpConfigRequest true "CSP IDP Config Info" +// @Success 201 {object} model.CspIdpConfig +// @Failure 400 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs [post] +// @Id createCspIdpConfig +func (h *CspIdpConfigHandler) CreateCspIdpConfig(c echo.Context) error { + var req model.CreateCspIdpConfigRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + // 필수 필드 검증 + if req.Name == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Name is required"}) + } + if req.CspAccountID == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP Account ID is required"}) + } + if req.AuthMethod == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Auth method is required"}) + } + if req.AuthMethod != model.AuthMethodOIDC && req.AuthMethod != model.AuthMethodSAML && req.AuthMethod != model.AuthMethodSecretKey { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid auth method. Must be one of: OIDC, SAML, SECRET_KEY"}) + } + if req.Config == nil || len(req.Config) == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Config is required"}) + } + + idpConfig, err := h.cspIdpConfigService.CreateCspIdpConfig(&req) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to create IDP config: %v", err)}) + } + + return c.JSON(http.StatusCreated, idpConfig) +} + +// ListCspIdpConfigs godoc +// @Summary List CSP IDP configs +// @Description Retrieve a list of CSP IDP configurations with optional filters +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param filter body model.CspIdpConfigFilter false "Filter options" +// @Success 200 {array} model.CspIdpConfig +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs/list [post] +// @Id listCspIdpConfigs +func (h *CspIdpConfigHandler) ListCspIdpConfigs(c echo.Context) error { + var filter model.CspIdpConfigFilter + if err := c.Bind(&filter); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + configs, err := h.cspIdpConfigService.ListCspIdpConfigs(&filter) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to list IDP configs: %v", err)}) + } + + if configs == nil { + configs = []*model.CspIdpConfig{} + } + + return c.JSON(http.StatusOK, configs) +} + +// GetCspIdpConfigByID godoc +// @Summary Get CSP IDP config by ID +// @Description Retrieve CSP IDP configuration details by ID +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param configId path string true "Config ID" +// @Success 200 {object} model.CspIdpConfig +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs/id/{configId} [get] +// @Id getCspIdpConfigByID +func (h *CspIdpConfigHandler) GetCspIdpConfigByID(c echo.Context) error { + configID, err := util.StringToUint(c.Param("configId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid config ID"}) + } + + idpConfig, err := h.cspIdpConfigService.GetCspIdpConfigByID(configID) + if err != nil { + if err.Error() == fmt.Sprintf("IDP config not found with ID: %d", configID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to get IDP config: %v", err)}) + } + + return c.JSON(http.StatusOK, idpConfig) +} + +// UpdateCspIdpConfig godoc +// @Summary Update CSP IDP config +// @Description Update CSP IDP configuration details +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param configId path string true "Config ID" +// @Param config body model.UpdateCspIdpConfigRequest true "CSP IDP Config Info" +// @Success 200 {object} model.CspIdpConfig +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs/id/{configId} [put] +// @Id updateCspIdpConfig +func (h *CspIdpConfigHandler) UpdateCspIdpConfig(c echo.Context) error { + configID, err := util.StringToUint(c.Param("configId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid config ID"}) + } + + var req model.UpdateCspIdpConfigRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + idpConfig, err := h.cspIdpConfigService.UpdateCspIdpConfig(configID, &req) + if err != nil { + if err.Error() == fmt.Sprintf("IDP config not found with ID: %d", configID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to update IDP config: %v", err)}) + } + + return c.JSON(http.StatusOK, idpConfig) +} + +// DeleteCspIdpConfig godoc +// @Summary Delete CSP IDP config +// @Description Delete a CSP IDP configuration by ID +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param configId path string true "Config ID" +// @Success 204 "No Content" +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs/id/{configId} [delete] +// @Id deleteCspIdpConfig +func (h *CspIdpConfigHandler) DeleteCspIdpConfig(c echo.Context) error { + configID, err := util.StringToUint(c.Param("configId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid config ID"}) + } + + if err := h.cspIdpConfigService.DeleteCspIdpConfig(configID); err != nil { + if err.Error() == fmt.Sprintf("IDP config not found with ID: %d", configID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to delete IDP config: %v", err)}) + } + + return c.NoContent(http.StatusNoContent) +} + +// TestCspIdpConnection godoc +// @Summary Test CSP IDP connection +// @Description Test connection to CSP using IDP configuration +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param configId path string true "Config ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs/id/{configId}/test [post] +// @Id testCspIdpConnection +func (h *CspIdpConfigHandler) TestCspIdpConnection(c echo.Context) error { + configID, err := util.StringToUint(c.Param("configId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid config ID"}) + } + + if err := h.cspIdpConfigService.TestConnection(c.Request().Context(), configID); err != nil { + if err.Error() == fmt.Sprintf("IDP config not found with ID: %d", configID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("Connection test failed: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "Connection test successful"}) +} + +// ActivateCspIdpConfig godoc +// @Summary Activate CSP IDP config +// @Description Activate a CSP IDP configuration +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param configId path string true "Config ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs/id/{configId}/activate [post] +// @Id activateCspIdpConfig +func (h *CspIdpConfigHandler) ActivateCspIdpConfig(c echo.Context) error { + configID, err := util.StringToUint(c.Param("configId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid config ID"}) + } + + if err := h.cspIdpConfigService.ActivateIdpConfig(configID); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to activate IDP config: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "IDP config activated successfully"}) +} + +// DeactivateCspIdpConfig godoc +// @Summary Deactivate CSP IDP config +// @Description Deactivate a CSP IDP configuration +// @Tags csp-idp-configs +// @Accept json +// @Produce json +// @Param configId path string true "Config ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-idp-configs/id/{configId}/deactivate [post] +// @Id deactivateCspIdpConfig +func (h *CspIdpConfigHandler) DeactivateCspIdpConfig(c echo.Context) error { + configID, err := util.StringToUint(c.Param("configId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid config ID"}) + } + + if err := h.cspIdpConfigService.DeactivateIdpConfig(configID); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to deactivate IDP config: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "IDP config deactivated successfully"}) +} diff --git a/src/handler/csp_policy_handler.go b/src/handler/csp_policy_handler.go new file mode 100644 index 00000000..59ec97b6 --- /dev/null +++ b/src/handler/csp_policy_handler.go @@ -0,0 +1,356 @@ +package handler + +import ( + "fmt" + "net/http" + + "github.com/labstack/echo/v4" + "github.com/m-cmp/mc-iam-manager/model" + "github.com/m-cmp/mc-iam-manager/service" + "github.com/m-cmp/mc-iam-manager/util" + "gorm.io/gorm" +) + +// CspPolicyHandler CSP 정책 관리 핸들러 +type CspPolicyHandler struct { + cspPolicyService *service.CspPolicyService +} + +// NewCspPolicyHandler 새 CspPolicyHandler 인스턴스 생성 +func NewCspPolicyHandler(db *gorm.DB) *CspPolicyHandler { + keycloakService := service.NewKeycloakService() + cspIdpConfigService := service.NewCspIdpConfigService(db, keycloakService) + return &CspPolicyHandler{ + cspPolicyService: service.NewCspPolicyService(db, cspIdpConfigService), + } +} + +// CreateCspPolicy godoc +// @Summary Create CSP policy +// @Description Create a new CSP policy +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param policy body model.CreateCspPolicyRequest true "CSP Policy Info" +// @Success 201 {object} model.CspPolicy +// @Failure 400 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies [post] +// @Id createCspPolicy +func (h *CspPolicyHandler) CreateCspPolicy(c echo.Context) error { + var req model.CreateCspPolicyRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + // 필수 필드 검증 + if req.Name == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Name is required"}) + } + if req.CspAccountID == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP Account ID is required"}) + } + if req.PolicyType == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Policy type is required"}) + } + if req.PolicyType != model.PolicyTypeInline && req.PolicyType != model.PolicyTypeManaged && req.PolicyType != model.PolicyTypeCustom { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid policy type. Must be one of: inline, managed, custom"}) + } + + policy, err := h.cspPolicyService.CreateCspPolicy(&req) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to create policy: %v", err)}) + } + + return c.JSON(http.StatusCreated, policy) +} + +// ListCspPolicies godoc +// @Summary List CSP policies +// @Description Retrieve a list of CSP policies with optional filters +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param filter body model.CspPolicyFilter false "Filter options" +// @Success 200 {array} model.CspPolicy +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/list [post] +// @Id listCspPolicies +func (h *CspPolicyHandler) ListCspPolicies(c echo.Context) error { + var filter model.CspPolicyFilter + if err := c.Bind(&filter); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + policies, err := h.cspPolicyService.ListCspPolicies(&filter) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to list policies: %v", err)}) + } + + if policies == nil { + policies = []*model.CspPolicy{} + } + + return c.JSON(http.StatusOK, policies) +} + +// GetCspPolicyByID godoc +// @Summary Get CSP policy by ID +// @Description Retrieve CSP policy details by ID +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param policyId path string true "Policy ID" +// @Success 200 {object} model.CspPolicy +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/id/{policyId} [get] +// @Id getCspPolicyByID +func (h *CspPolicyHandler) GetCspPolicyByID(c echo.Context) error { + policyID, err := util.StringToUint(c.Param("policyId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid policy ID"}) + } + + policy, err := h.cspPolicyService.GetCspPolicyByID(policyID) + if err != nil { + if err.Error() == fmt.Sprintf("policy not found with ID: %d", policyID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to get policy: %v", err)}) + } + + return c.JSON(http.StatusOK, policy) +} + +// UpdateCspPolicy godoc +// @Summary Update CSP policy +// @Description Update CSP policy details +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param policyId path string true "Policy ID" +// @Param policy body model.UpdateCspPolicyRequest true "CSP Policy Info" +// @Success 200 {object} model.CspPolicy +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/id/{policyId} [put] +// @Id updateCspPolicy +func (h *CspPolicyHandler) UpdateCspPolicy(c echo.Context) error { + policyID, err := util.StringToUint(c.Param("policyId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid policy ID"}) + } + + var req model.UpdateCspPolicyRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + policy, err := h.cspPolicyService.UpdateCspPolicy(policyID, &req) + if err != nil { + if err.Error() == fmt.Sprintf("policy not found with ID: %d", policyID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to update policy: %v", err)}) + } + + return c.JSON(http.StatusOK, policy) +} + +// DeleteCspPolicy godoc +// @Summary Delete CSP policy +// @Description Delete a CSP policy by ID +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param policyId path string true "Policy ID" +// @Success 204 "No Content" +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/id/{policyId} [delete] +// @Id deleteCspPolicy +func (h *CspPolicyHandler) DeleteCspPolicy(c echo.Context) error { + policyID, err := util.StringToUint(c.Param("policyId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid policy ID"}) + } + + if err := h.cspPolicyService.DeleteCspPolicy(policyID); err != nil { + if err.Error() == fmt.Sprintf("policy not found with ID: %d", policyID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to delete policy: %v", err)}) + } + + return c.NoContent(http.StatusNoContent) +} + +// SyncCspPolicies godoc +// @Summary Sync CSP policies from cloud +// @Description Synchronize policies from the CSP cloud +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param request body model.SyncPoliciesRequest true "Sync Policies Request" +// @Success 200 {array} model.CspPolicy +// @Failure 400 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/sync [post] +// @Id syncCspPolicies +func (h *CspPolicyHandler) SyncCspPolicies(c echo.Context) error { + var req model.SyncPoliciesRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + if req.CspAccountID == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP Account ID is required"}) + } + + policies, err := h.cspPolicyService.SyncPoliciesFromCloud(c.Request().Context(), &req) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to sync policies: %v", err)}) + } + + return c.JSON(http.StatusOK, policies) +} + +// AttachPolicyToRole godoc +// @Summary Attach policy to role +// @Description Attach a CSP policy to a CSP role +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param request body model.AttachPolicyRequest true "Attach Policy Request" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/attach [post] +// @Id attachPolicyToRole +func (h *CspPolicyHandler) AttachPolicyToRole(c echo.Context) error { + var req model.AttachPolicyRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + if req.CspRoleID == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP Role ID is required"}) + } + if req.CspPolicyID == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP Policy ID is required"}) + } + + if err := h.cspPolicyService.AttachPolicyToRole(req.CspRoleID, req.CspPolicyID); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to attach policy: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "Policy attached successfully"}) +} + +// DetachPolicyFromRole godoc +// @Summary Detach policy from role +// @Description Detach a CSP policy from a CSP role +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param request body model.AttachPolicyRequest true "Detach Policy Request" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/detach [post] +// @Id detachPolicyFromRole +func (h *CspPolicyHandler) DetachPolicyFromRole(c echo.Context) error { + var req model.AttachPolicyRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + if req.CspRoleID == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP Role ID is required"}) + } + if req.CspPolicyID == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "CSP Policy ID is required"}) + } + + if err := h.cspPolicyService.DetachPolicyFromRole(req.CspRoleID, req.CspPolicyID); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to detach policy: %v", err)}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "Policy detached successfully"}) +} + +// GetRolePolicies godoc +// @Summary Get policies attached to role +// @Description Get list of policies attached to a CSP role +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param roleId path string true "Role ID" +// @Success 200 {array} model.CspPolicy +// @Failure 400 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/role/{roleId} [get] +// @Id getRolePolicies +func (h *CspPolicyHandler) GetRolePolicies(c echo.Context) error { + roleID, err := util.StringToUint(c.Param("roleId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid role ID"}) + } + + policies, err := h.cspPolicyService.GetPoliciesByRoleID(roleID) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to get policies: %v", err)}) + } + + if policies == nil { + policies = []*model.CspPolicy{} + } + + return c.JSON(http.StatusOK, policies) +} + +// GetPolicyDocument godoc +// @Summary Get policy document +// @Description Get the policy document content +// @Tags csp-policies +// @Accept json +// @Produce json +// @Param policyId path string true "Policy ID" +// @Success 200 {object} map[string]interface{} +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/csp-policies/id/{policyId}/document [get] +// @Id getPolicyDocument +func (h *CspPolicyHandler) GetPolicyDocument(c echo.Context) error { + policyID, err := util.StringToUint(c.Param("policyId")) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid policy ID"}) + } + + document, err := h.cspPolicyService.GetPolicyDocument(c.Request().Context(), policyID) + if err != nil { + if err.Error() == fmt.Sprintf("policy not found with ID: %d", policyID) { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to get policy document: %v", err)}) + } + + return c.JSON(http.StatusOK, document) +} diff --git a/src/handler/mcmpapi_handler.go b/src/handler/mcmpapi_handler.go index 55555897..487eb8b4 100644 --- a/src/handler/mcmpapi_handler.go +++ b/src/handler/mcmpapi_handler.go @@ -49,6 +49,53 @@ func (h *McmpApiHandler) SyncMcmpAPIs(c echo.Context) error { return c.JSON(http.StatusOK, map[string]string{"message": "Successfully triggered MCMP API sync"}) } +// ImportAPIs godoc +// @Summary Import MCMP APIs from Remote Sources +// @Description Fetches API specifications from remote URLs and imports them to the database. Supports swagger and openapi source types. +// @Tags McmpAPI +// @Accept json +// @Produce json +// @Param request body model.ImportApiRequest true "Frameworks to import" +// @Success 200 {object} model.ImportApiResponse "Import results" +// @Failure 400 {object} map[string]string "error: Invalid request body" +// @Failure 500 {object} map[string]string "error: Failed to import APIs" +// @Router /api/mcmp-apis/import [post] +// @Security BearerAuth +// @Id importAPIs +func (h *McmpApiHandler) ImportAPIs(c echo.Context) error { + var req model.ImportApiRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request body: " + err.Error()}) + } + + // Validate request + if len(req.Frameworks) == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "At least one framework is required"}) + } + + for i, fw := range req.Frameworks { + if fw.Name == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("Framework at index %d: name is required", i)}) + } + if fw.Version == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("Framework '%s': version is required", fw.Name)}) + } + if fw.SourceType == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("Framework '%s': sourceType is required (swagger or openapi)", fw.Name)}) + } + if fw.SourceURL == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("Framework '%s': sourceUrl is required", fw.Name)}) + } + } + + response, err := h.service.ImportAPIs(&req) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to import APIs: " + err.Error()}) + } + + return c.JSON(http.StatusOK, response) +} + // Add other handler methods if needed, e.g., to get API definitions via API // SetActiveVersion godoc @@ -348,120 +395,3 @@ func (h *McmpApiHandler) UpdateFrameworkService(c echo.Context) error { return c.JSON(http.StatusOK, map[string]string{"message": fmt.Sprintf("Service '%s' updated successfully", serviceName)}) } -// ListMCMPAPIs godoc -// @Summary MCMP API 목록 조회 -// @Description 모든 MCMP API 목록을 조회합니다 -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Success 200 {array} model.MCMPAPI -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Security BearerAuth -// @Router /api/mcmp-apis [get] - -// GetMCMPAPIByID godoc -// @Summary MCMP API ID로 조회 -// @Description 특정 MCMP API를 ID로 조회합니다 -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Param id path string true "API ID" -// @Success 200 {object} model.MCMPAPI -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Failure 404 {object} map[string]string "error: API not found" -// @Security BearerAuth -// @Router /api/mcmp-apis/{id} [get] - -// CreateMCMPAPI godoc -// @Summary 새 MCMP API 생성 -// @Description 새로운 MCMP API를 생성합니다 -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Param api body model.MCMPAPI true "API Info" -// @Success 201 {object} model.MCMPAPI -// @Failure 400 {object} map[string]string "error: Invalid request" -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Security BearerAuth -// @Router /api/mcmp-apis [post] - -// UpdateMCMPAPI godoc -// @Summary MCMP API 업데이트 -// @Description MCMP API 정보를 업데이트합니다 -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Param id path string true "API ID" -// @Param api body model.MCMPAPI true "API Info" -// @Success 200 {object} model.MCMPAPI -// @Failure 400 {object} map[string]string "error: Invalid request" -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Failure 404 {object} map[string]string "error: API not found" -// @Security BearerAuth -// @Router /api/mcmp-apis/{id} [put] - -// DeleteMCMPAPI godoc -// @Summary MCMP API 삭제 -// @Description MCMP API를 삭제합니다 -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Param id path string true "API ID" -// @Success 204 "No Content" -// @Failure 401 {object} map[string]string "error: Unauthorized" -// @Failure 403 {object} map[string]string "error: Forbidden" -// @Failure 404 {object} map[string]string "error: API not found" -// @Security BearerAuth -// @Router /api/mcmp-apis/{id} [delete] - -// @Summary List services and actions -// @Description Get a list of all MCMP services and their actions -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Success 200 {array} model.McmpService -// @Failure 500 {object} map[string]string -// @Security BearerAuth -// @Router /api/mcmp-apis/list [post] - -// @Summary Set active version -// @Description Set the active version for a service -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Param serviceName path string true "Service Name" -// @Param version path string true "Version" -// @Success 200 {object} map[string]string -// @Failure 400 {object} map[string]string -// @Failure 500 {object} map[string]string -// @Security BearerAuth -// @Router /api/mcmp-apis/name/{serviceName}/versions/{version}/activate [put] - -// @Summary Make MCMP API call -// @Description Make a call to MCMP API -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Param request body model.McmpApiCallRequest true "API Call Request" -// @Success 200 {object} map[string]interface{} -// @Failure 400 {object} map[string]string -// @Failure 500 {object} map[string]string -// @Security BearerAuth -// @Router /api/mcmp-apis/call [post] - -// @Summary Update service -// @Description Update MCMP service information -// @Tags mcmp-apis -// @Accept json -// @Produce json -// @Param serviceName path string true "Service Name" -// @Param service body model.McmpService true "Service Info" -// @Success 200 {object} model.McmpService -// @Failure 400 {object} map[string]string -// @Failure 500 {object} map[string]string -// @Security BearerAuth -// @Router /api/mcmp-apis/name/{serviceName} [put] diff --git a/src/handler/mcmpapi_permission_action_mapping_handler.go b/src/handler/mcmpapi_permission_action_mapping_handler.go index ef86254f..d4fac0ff 100644 --- a/src/handler/mcmpapi_permission_action_mapping_handler.go +++ b/src/handler/mcmpapi_permission_action_mapping_handler.go @@ -46,9 +46,9 @@ func (h *McmpApiPermissionActionMappingHandler) ListPlatformActions(c echo.Conte // @Tags mcmp-api-permission-action-mappings // @Accept json // @Produce json -// @Param id path string true "Permission ID" +// @Param permissionId path string true "Permission ID" // @Success 200 {array} mcmpapi.McmpApiAction -// @Router /api/mcmp-apis/permission-action-mappings/id/{id} [get] +// @Router /api/mcmp-api-permission-action-mappings/platforms/id/{permissionId}/actions [get] // @Id getPlatformActionsByPermissionID func (h *McmpApiPermissionActionMappingHandler) GetPlatformActionsByPermissionID(c echo.Context) error { permissionID := c.Param("permission_id") @@ -226,3 +226,4 @@ func (h *McmpApiPermissionActionMappingHandler) UpdateMapping(c echo.Context) er return c.JSON(http.StatusOK, map[string]string{"message": "mapping updated successfully"}) } + diff --git a/src/handler/role_handler.go b/src/handler/role_handler.go index 5f42938e..c0c1aeab 100644 --- a/src/handler/role_handler.go +++ b/src/handler/role_handler.go @@ -715,7 +715,7 @@ func (h *RoleHandler) ListPlatformRoles(c echo.Context) error { // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/roles/workspace-roles/list [post] -// @Id listWorkspaceRoles +// @Id listRolesOfWorkspaceType func (h *RoleHandler) ListWorkspaceRoles(c echo.Context) error { var req model.RoleFilterRequest if err := c.Bind(&req); err != nil { diff --git a/src/handler/workspace_handler.go b/src/handler/workspace_handler.go index d2dbfbad..17f5fce1 100644 --- a/src/handler/workspace_handler.go +++ b/src/handler/workspace_handler.go @@ -144,7 +144,7 @@ func (h *WorkspaceHandler) GetWorkspaceByID(c echo.Context) error { // CreateWorkspace godoc // @Summary Create new workspace -// @Description Create a new workspace with the specified information. Optionally assign existing projects to the workspace. +// @Description Create a new workspace with the specified information. // @Tags workspaces // @Accept json // @Produce json @@ -163,35 +163,12 @@ func (h *WorkspaceHandler) CreateWorkspace(c echo.Context) error { }) } - // Save projects array temporarily and clear it to avoid duplicate assignment - projectsToAssign := workspace.Projects - workspace.Projects = nil - - // Create workspace if err := h.workspaceService.CreateWorkspace(&workspace); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to create workspace", }) } - // If projects array is provided, assign them to the new workspace - if len(projectsToAssign) > 0 { - for _, project := range projectsToAssign { - // AddProjectToWorkspace will remove project from any existing workspace - // and assign it to the new workspace (enforcing 1:N relationship) - if err := h.workspaceService.AddProjectToWorkspace(workspace.ID, project.ID); err != nil { - log.Printf("Failed to assign project %d to workspace %d: %v", project.ID, workspace.ID, err) - // Continue with other projects even if one fails - } - } - - // Fetch updated workspace with assigned projects - updatedWorkspace, err := h.workspaceService.GetWorkspaceProjectsByWorkspaceId(workspace.ID) - if err == nil && updatedWorkspace != nil { - return c.JSON(http.StatusCreated, updatedWorkspace) - } - } - return c.JSON(http.StatusCreated, workspace) } @@ -573,6 +550,121 @@ func (h *WorkspaceHandler) ListWorkspaceUsersAndRoles(c echo.Context) error { return c.JSON(http.StatusOK, workspaceUsersRoles) } +// ListWorkspaceRoles godoc +// @Summary List workspace roles +// @Description Retrieve all workspace-level roles with optional filtering +// @Tags workspaces +// @Accept json +// @Produce json +// @Param request body model.RoleFilterRequest true "Role filter parameters" +// @Success 200 {array} model.RoleMaster "Successfully retrieved workspace roles" +// @Failure 400 {object} map[string]string "error: Invalid request format" +// @Failure 500 {object} map[string]string "error: Failed to retrieve workspace roles" +// @Security BearerAuth +// @Router /api/workspaces/roles/list [post] +// @Id listWorkspaceRoles +func (h *WorkspaceHandler) ListWorkspaceRoles(c echo.Context) error { + var req model.RoleFilterRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + + // workspace 역할만 조회 + if req.RoleTypes == nil { + req.RoleTypes = []constants.IAMRoleType{constants.RoleTypeWorkspace} + } + + roles, err := h.roleService.ListWorkspaceRoles(&req) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusOK, roles) +} + + +// AddUserToWorkspace godoc +// @Summary Add user to workspace +// @Description Add a user to a workspace +// @Tags workspaces +// @Accept json +// @Produce json +// @Param id path string true "Workspace ID" +// @Param request body model.AssignRoleRequest true "User Info" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/workspaces/{id}/users [post] +// @Id addUserToWorkspace +func (h *WorkspaceHandler) AddUserToWorkspace(c echo.Context) error { + var req model.AssignRoleRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"}) + } + if req.WorkspaceID == "" || req.UserID == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Workspace ID and User ID are required"}) + } + + // 워크스페이스 역할 할당 + var workspaceID uint + var userID uint + var err error + workspaceID, err = util.StringToUint(req.WorkspaceID) + if err != nil { + log.Printf("Workspace ID conversion error: %v", err) + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid workspace ID format"}) + } + if req.UserID != "" { + userID, err = util.StringToUint(req.UserID) + if err != nil { + log.Printf("User ID conversion error: %v", err) + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid user ID format"}) + } + } + + if err := h.workspaceService.AddUserToWorkspace(workspaceID, userID); err != nil { + if err.Error() == "workspace not found" || err.Error() == "user not found" { + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("Failed to add user: %v", err)}) + } + return c.NoContent(http.StatusNoContent) +} + +// RemoveUserFromWorkspace godoc +// @Summary Remove user from workspace +// @Description Remove a user from a workspace +// @Tags workspaces +// @Accept json +// @Produce json +// @Param id path string true "Workspace ID" +// @Param userId path string true "User ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} map[string]string +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Security BearerAuth +// @Router /api/workspaces/{id}/users/{userId} [delete] +// @Id removeUserFromWorkspace +func (h *WorkspaceHandler) RemoveUserFromWorkspace(c echo.Context) error { + workspaceID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid workspace ID"}) + } + + userID, err := strconv.ParseUint(c.Param("userId"), 10, 32) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid user ID"}) + } + + if err := h.workspaceService.RemoveUserFromWorkspace(uint(workspaceID), uint(userID)); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) + } + + return c.NoContent(http.StatusNoContent) +} + // Helper function to get user DB ID and Platform Roles from context // TODO: Move this to a shared location or middleware func getUserDbIdAndPlatformRoles(ctx context.Context, c echo.Context, userService *service.UserService) (uint, []*model.RoleMaster, error) { diff --git a/src/main.go b/src/main.go index 7abf05e9..315d2a3a 100755 --- a/src/main.go +++ b/src/main.go @@ -82,11 +82,16 @@ func main() { &model.ResourceType{}, &model.UserPlatformRole{}, &model.UserWorkspaceRole{}, + &model.CspAccount{}, + &model.CspIdpConfig{}, &model.CspRole{}, + &model.CspPolicy{}, + &model.CspRolePolicyMapping{}, &model.RoleMasterCspRoleMapping{}, &model.TempCredential{}, &mcmpapi.McmpApiService{}, &mcmpapi.McmpApiAction{}, + &mcmpapi.McmpApiServiceMeta{}, &model.MciamPermission{}, &model.MciamRoleMciamPermission{}, ); err != nil { @@ -115,6 +120,11 @@ func main() { permissionHandler := handler.NewMciamPermissionHandler(db) roleHandler := handler.NewRoleHandler(db) + // CSP 관리 핸들러 초기화 + cspAccountHandler := handler.NewCspAccountHandler(db) + cspIdpConfigHandler := handler.NewCspIdpConfigHandler(db) + cspPolicyHandler := handler.NewCspPolicyHandler(db) + // Echo 인스턴스 생성 e := echo.New() @@ -210,12 +220,15 @@ func main() { workspaces.POST("/users/list", workspaceHandler.ListWorkspaceUsers) // workspace의 사용자 목록 조회 workspaces.POST("/users-roles/list", workspaceHandler.ListWorkspaceUsersAndRoles, middleware.PlatformRoleMiddleware(middleware.Write)) // workspace와 사용자 및 role 조회 + workspaces.POST("/roles/list", workspaceHandler.ListWorkspaceRoles) // workspace 역할 목록 조회 workspaces.POST("/projects/list", workspaceHandler.ListWorkspaceProjects) workspaces.GET("/id/:workspaceId/projects/list", workspaceHandler.GetWorkspaceProjectsByWorkspaceId) workspaces.POST("/id/:workspaceId/users/list", workspaceHandler.ListUsersAndRolesByWorkspaces) // TODO ListAllWorkspaceUsersAndRoles으로 대체 또는 통합 가능하지 않나? workspaces.GET("/id/:workspaceId/users/id/:userId", roleHandler.GetUserWorkspaceRoles, middleware.PlatformRoleMiddleware(middleware.Write)) // 특정 사용자에게 할당된 워크스페이스 역할 조회 ( 관리자가 사용자의 workspace role 조회) --> get을 post로 바꿀까? + workspaces.POST("/id/:id/users", workspaceHandler.AddUserToWorkspace, middleware.PlatformRoleMiddleware(middleware.Write)) // workspace에 사용자 추가 + workspaces.DELETE("/id/:id/users/:userId", workspaceHandler.RemoveUserFromWorkspace, middleware.PlatformRoleMiddleware(middleware.Write)) // workspace에서 사용자 제거 workspaces.POST("/assign/projects", workspaceHandler.AddProjectToWorkspace, middleware.PlatformAdminMiddleware) workspaces.DELETE("/unassign/projects", workspaceHandler.RemoveProjectFromWorkspace, middleware.PlatformAdminMiddleware) @@ -373,6 +386,7 @@ func main() { mcmpApis.POST("/call", mcmpApiHandler.McmpApiCall, middleware.PlatformRoleMiddleware(middleware.Manage)) mcmpApis.GET("/test/mc-infra-manager/getallns", mcmpApiHandler.TestCallGetAllNs, middleware.PlatformRoleMiddleware(middleware.Manage)) mcmpApis.PUT("/name/:serviceName", mcmpApiHandler.UpdateFrameworkService, middleware.PlatformRoleMiddleware(middleware.Manage)) + mcmpApis.POST("/import", mcmpApiHandler.ImportAPIs, middleware.PlatformRoleMiddleware(middleware.Manage)) } // MCMP API 권한-액션 매핑 라우트 @@ -388,6 +402,47 @@ func main() { mcmpApiPermissionActionMappings.DELETE("/permissions/:permissionId/actions/:actionId", mcmpApiPermissionActionMappingHandler.DeleteMapping, middleware.PlatformRoleMiddleware(middleware.Manage)) } + // CSP 계정 관리 라우트 + cspAccounts := api.Group("/csp-accounts", middleware.PlatformRoleMiddleware(middleware.Read)) + { + cspAccounts.POST("/list", cspAccountHandler.ListCspAccounts) + cspAccounts.POST("", cspAccountHandler.CreateCspAccount, middleware.PlatformAdminMiddleware) + cspAccounts.GET("/id/:accountId", cspAccountHandler.GetCspAccountByID) + cspAccounts.PUT("/id/:accountId", cspAccountHandler.UpdateCspAccount, middleware.PlatformAdminMiddleware) + cspAccounts.DELETE("/id/:accountId", cspAccountHandler.DeleteCspAccount, middleware.PlatformAdminMiddleware) + cspAccounts.POST("/id/:accountId/validate", cspAccountHandler.ValidateCspAccount, middleware.PlatformAdminMiddleware) + cspAccounts.POST("/id/:accountId/activate", cspAccountHandler.ActivateCspAccount, middleware.PlatformAdminMiddleware) + cspAccounts.POST("/id/:accountId/deactivate", cspAccountHandler.DeactivateCspAccount, middleware.PlatformAdminMiddleware) + } + + // CSP IDP 설정 관리 라우트 + cspIdpConfigs := api.Group("/csp-idp-configs", middleware.PlatformRoleMiddleware(middleware.Read)) + { + cspIdpConfigs.POST("/list", cspIdpConfigHandler.ListCspIdpConfigs) + cspIdpConfigs.POST("", cspIdpConfigHandler.CreateCspIdpConfig, middleware.PlatformAdminMiddleware) + cspIdpConfigs.GET("/id/:configId", cspIdpConfigHandler.GetCspIdpConfigByID) + cspIdpConfigs.PUT("/id/:configId", cspIdpConfigHandler.UpdateCspIdpConfig, middleware.PlatformAdminMiddleware) + cspIdpConfigs.DELETE("/id/:configId", cspIdpConfigHandler.DeleteCspIdpConfig, middleware.PlatformAdminMiddleware) + cspIdpConfigs.POST("/id/:configId/test", cspIdpConfigHandler.TestCspIdpConnection, middleware.PlatformAdminMiddleware) + cspIdpConfigs.POST("/id/:configId/activate", cspIdpConfigHandler.ActivateCspIdpConfig, middleware.PlatformAdminMiddleware) + cspIdpConfigs.POST("/id/:configId/deactivate", cspIdpConfigHandler.DeactivateCspIdpConfig, middleware.PlatformAdminMiddleware) + } + + // CSP 정책 관리 라우트 + cspPolicies := api.Group("/csp-policies", middleware.PlatformRoleMiddleware(middleware.Read)) + { + cspPolicies.POST("/list", cspPolicyHandler.ListCspPolicies) + cspPolicies.POST("", cspPolicyHandler.CreateCspPolicy, middleware.PlatformAdminMiddleware) + cspPolicies.GET("/id/:policyId", cspPolicyHandler.GetCspPolicyByID) + cspPolicies.PUT("/id/:policyId", cspPolicyHandler.UpdateCspPolicy, middleware.PlatformAdminMiddleware) + cspPolicies.DELETE("/id/:policyId", cspPolicyHandler.DeleteCspPolicy, middleware.PlatformAdminMiddleware) + cspPolicies.GET("/id/:policyId/document", cspPolicyHandler.GetPolicyDocument) + cspPolicies.POST("/sync", cspPolicyHandler.SyncCspPolicies, middleware.PlatformAdminMiddleware) + cspPolicies.POST("/attach", cspPolicyHandler.AttachPolicyToRole, middleware.PlatformAdminMiddleware) + cspPolicies.POST("/detach", cspPolicyHandler.DetachPolicyFromRole, middleware.PlatformAdminMiddleware) + cspPolicies.GET("/role/:roleId", cspPolicyHandler.GetRolePolicies) + } + // Swagger 문서 라우트 e.GET("/swagger/*", echoSwagger.WrapHandler) diff --git a/src/model/csp_account.go b/src/model/csp_account.go new file mode 100644 index 00000000..e33566f0 --- /dev/null +++ b/src/model/csp_account.go @@ -0,0 +1,110 @@ +package model + +import ( + "time" +) + +// CspAccount CSP 계정 정보 모델 +// AWS Account ID, GCP Project ID, Azure Subscription ID 등 CSP별 계정 정보를 관리 +type CspAccount struct { + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"size:255;not null" json:"name"` + CspType string `gorm:"size:50;not null" json:"csp_type"` // aws, gcp, azure + AccountInfo map[string]string `gorm:"type:jsonb;serializer:json" json:"account_info"` + IsActive bool `gorm:"default:true" json:"is_active"` + Description string `gorm:"size:500" json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// TableName CspAccount 테이블 이름 반환 +func (CspAccount) TableName() string { + return "mcmp_csp_accounts" +} + +// CspAccountInfo CSP별 계정 정보 구조 +// AWS AccountInfo 예시: +// +// { +// "account_id": "050864702683", +// "alias": "my-aws-account", +// "region": "ap-northeast-2" +// } +// +// GCP AccountInfo 예시: +// +// { +// "project_id": "my-gcp-project", +// "project_number": "123456789" +// } +// +// Azure AccountInfo 예시: +// +// { +// "subscription_id": "xxx-xxx-xxx", +// "tenant_id": "yyy-yyy-yyy", +// "directory_id": "zzz-zzz-zzz" +// } + +// GetAccountID AWS Account ID 반환 +func (c *CspAccount) GetAccountID() string { + if c.AccountInfo == nil { + return "" + } + return c.AccountInfo["account_id"] +} + +// GetProjectID GCP Project ID 반환 +func (c *CspAccount) GetProjectID() string { + if c.AccountInfo == nil { + return "" + } + return c.AccountInfo["project_id"] +} + +// GetSubscriptionID Azure Subscription ID 반환 +func (c *CspAccount) GetSubscriptionID() string { + if c.AccountInfo == nil { + return "" + } + return c.AccountInfo["subscription_id"] +} + +// GetTenantID Azure Tenant ID 반환 +func (c *CspAccount) GetTenantID() string { + if c.AccountInfo == nil { + return "" + } + return c.AccountInfo["tenant_id"] +} + +// GetRegion 리전 정보 반환 +func (c *CspAccount) GetRegion() string { + if c.AccountInfo == nil { + return "" + } + return c.AccountInfo["region"] +} + +// CspAccountFilter CSP 계정 조회 필터 +type CspAccountFilter struct { + CspType string `json:"csp_type,omitempty"` + IsActive *bool `json:"is_active,omitempty"` + Name string `json:"name,omitempty"` +} + +// CreateCspAccountRequest CSP 계정 생성 요청 +type CreateCspAccountRequest struct { + Name string `json:"name" binding:"required"` + CspType string `json:"csp_type" binding:"required,oneof=aws gcp azure"` + AccountInfo map[string]string `json:"account_info"` + Description string `json:"description"` +} + +// UpdateCspAccountRequest CSP 계정 수정 요청 +type UpdateCspAccountRequest struct { + Name string `json:"name"` + AccountInfo map[string]string `json:"account_info"` + IsActive *bool `json:"is_active"` + Description string `json:"description"` +} diff --git a/src/model/csp_idp_config.go b/src/model/csp_idp_config.go new file mode 100644 index 00000000..e32f0618 --- /dev/null +++ b/src/model/csp_idp_config.go @@ -0,0 +1,274 @@ +package model + +import ( + "time" +) + +// AuthMethodType IDP 인증 방식 타입 +type AuthMethodType string + +const ( + AuthMethodOIDC AuthMethodType = "OIDC" + AuthMethodSAML AuthMethodType = "SAML" + AuthMethodSecretKey AuthMethodType = "SECRET_KEY" +) + +// CspIdpConfig CSP IDP 연동 설정 모델 +// OIDC, SAML, Secret Key 등 CSP별 IDP 연동 정보를 관리 +type CspIdpConfig struct { + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"size:255;not null" json:"name"` + CspAccountID uint `gorm:"not null" json:"csp_account_id"` + CspAccount *CspAccount `gorm:"foreignKey:CspAccountID" json:"csp_account,omitempty"` + AuthMethod AuthMethodType `gorm:"size:50;not null" json:"auth_method"` // OIDC, SAML, SECRET_KEY + Config map[string]string `gorm:"type:jsonb;serializer:json" json:"config"` + IsActive bool `gorm:"default:true" json:"is_active"` + Description string `gorm:"size:500" json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// TableName CspIdpConfig 테이블 이름 반환 +func (CspIdpConfig) TableName() string { + return "mcmp_csp_idp_configs" +} + +// CspIdpConfigInfo 인증 방식별 설정 정보 구조 +// OIDC Config 예시: +// +// { +// "oidc_provider_arn": "arn:aws:iam::050864702683:oidc-provider/keycloak.example.com", +// "audience": "mciam-client", +// "sts_endpoint": "https://sts.amazonaws.com" +// } +// +// SAML Config 예시: +// +// AWS SAML: +// +// { +// "saml_provider_arn": "arn:aws:iam::050864702683:saml-provider/keycloak", +// "sso_service_location": "https://devkc.onecloudcon.com/realms/saml-demo/protocol/saml", +// "issuer_url": "https://devkc.onecloudcon.com/realms/saml-demo", +// "signin_url": "https://signin.aws.amazon.com/saml/acs/SAMLSPD9IMTW2LM46J042K", +// "metadata_url": "https://signin.aws.amazon.com/static/saml/SAMLSPD9IMTW2LM46J042K/saml-metadata.xml", +// "valid_until": "2125-02-20" +// } +// +// GCP SAML: +// +// { +// "saml_provider_resource_name": "projects/123456789/locations/global/workloadIdentityPools/mciam-pool/providers/keycloak-saml", +// "sso_service_location": "https://devkc.onecloudcon.com/realms/gcp-demo/protocol/saml", +// "issuer_url": "https://devkc.onecloudcon.com/realms/gcp-demo", +// "audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/mciam-pool/providers/keycloak-saml", +// "metadata_url": "https://devkc.onecloudcon.com/realms/gcp-demo/protocol/saml/descriptor" +// } +// +// Azure SAML: +// +// { +// "application_id": "12345678-1234-1234-1234-123456789012", +// "tenant_id": "87654321-4321-4321-4321-210987654321", +// "sso_service_location": "https://devkc.onecloudcon.com/realms/azure-demo/protocol/saml", +// "issuer_url": "https://devkc.onecloudcon.com/realms/azure-demo", +// "reply_url": "https://login.microsoftonline.com/login/saml2", +// "metadata_url": "https://login.microsoftonline.com/TENANT_ID/federationmetadata/2007-06/federationmetadata.xml", +// "federated_credential_id": "credential-12345" +// } +// +// SECRET_KEY Config 예시: +// +// { +// "access_key_id": "AKIAIOSFODNN7EXAMPLE", +// "secret_access_key": "encrypted_value", +// "encrypted": "true" +// } + +// GetOidcProviderArn OIDC Provider ARN 반환 +func (c *CspIdpConfig) GetOidcProviderArn() string { + if c.Config == nil { + return "" + } + return c.Config["oidc_provider_arn"] +} + +// GetAudience OIDC Audience 반환 +func (c *CspIdpConfig) GetAudience() string { + if c.Config == nil { + return "" + } + return c.Config["audience"] +} + +// GetStsEndpoint STS Endpoint 반환 +func (c *CspIdpConfig) GetStsEndpoint() string { + if c.Config == nil { + return "" + } + return c.Config["sts_endpoint"] +} + +// GetSamlProviderArn SAML Provider ARN 반환 (AWS) +func (c *CspIdpConfig) GetSamlProviderArn() string { + if c.Config == nil { + return "" + } + return c.Config["saml_provider_arn"] +} + +// ========== Common SAML Fields (All CSPs) ========== + +// GetSsoServiceLocation SSO Service Location 반환 (SAML) +func (c *CspIdpConfig) GetSsoServiceLocation() string { + if c.Config == nil { + return "" + } + // Prefer sso_service_location, fallback to assertion_endpoint for backward compatibility + if loc := c.Config["sso_service_location"]; loc != "" { + return loc + } + return c.Config["assertion_endpoint"] +} + +// GetIssuerUrl Issuer URL 반환 (SAML) +func (c *CspIdpConfig) GetIssuerUrl() string { + if c.Config == nil { + return "" + } + return c.Config["issuer_url"] +} + +// GetMetadataUrl SAML Metadata Document URL 반환 +func (c *CspIdpConfig) GetMetadataUrl() string { + if c.Config == nil { + return "" + } + return c.Config["metadata_url"] +} + +// ========== AWS-Specific SAML Fields ========== + +// GetSigninUrl AWS SAML Sign-in URL (ACS endpoint) 반환 +func (c *CspIdpConfig) GetSigninUrl() string { + if c.Config == nil { + return "" + } + return c.Config["signin_url"] +} + +// GetValidUntil SAML Assertion Valid Until Date 반환 (AWS) +func (c *CspIdpConfig) GetValidUntil() string { + if c.Config == nil { + return "" + } + return c.Config["valid_until"] +} + +// ========== GCP-Specific SAML Fields ========== + +// GetSamlProviderResourceName GCP SAML Provider Resource Name 반환 +func (c *CspIdpConfig) GetSamlProviderResourceName() string { + if c.Config == nil { + return "" + } + return c.Config["saml_provider_resource_name"] +} + +// ========== Azure-Specific SAML Fields ========== + +// GetApplicationId Azure AD Application ID 반환 +func (c *CspIdpConfig) GetApplicationId() string { + if c.Config == nil { + return "" + } + return c.Config["application_id"] +} + +// GetTenantId Azure AD Tenant ID 반환 +func (c *CspIdpConfig) GetTenantId() string { + if c.Config == nil { + return "" + } + return c.Config["tenant_id"] +} + +// GetReplyUrl Azure AD Reply URL 반환 +func (c *CspIdpConfig) GetReplyUrl() string { + if c.Config == nil { + return "" + } + return c.Config["reply_url"] +} + +// GetFederatedCredentialId Azure Federated Credential ID 반환 +func (c *CspIdpConfig) GetFederatedCredentialId() string { + if c.Config == nil { + return "" + } + return c.Config["federated_credential_id"] +} + +// GetAccessKeyID Secret Key Access Key ID 반환 +func (c *CspIdpConfig) GetAccessKeyID() string { + if c.Config == nil { + return "" + } + return c.Config["access_key_id"] +} + +// GetSecretAccessKey Secret Key Secret Access Key 반환 (암호화된 값) +func (c *CspIdpConfig) GetSecretAccessKey() string { + if c.Config == nil { + return "" + } + return c.Config["secret_access_key"] +} + +// IsEncrypted Secret Key 암호화 여부 확인 +func (c *CspIdpConfig) IsEncrypted() bool { + if c.Config == nil { + return false + } + return c.Config["encrypted"] == "true" +} + +// IsOIDC OIDC 인증 방식인지 확인 +func (c *CspIdpConfig) IsOIDC() bool { + return c.AuthMethod == AuthMethodOIDC +} + +// IsSAML SAML 인증 방식인지 확인 +func (c *CspIdpConfig) IsSAML() bool { + return c.AuthMethod == AuthMethodSAML +} + +// IsSecretKey Secret Key 인증 방식인지 확인 +func (c *CspIdpConfig) IsSecretKey() bool { + return c.AuthMethod == AuthMethodSecretKey +} + +// CspIdpConfigFilter CSP IDP 설정 조회 필터 +type CspIdpConfigFilter struct { + CspAccountID *uint `json:"csp_account_id,omitempty"` + AuthMethod AuthMethodType `json:"auth_method,omitempty"` + IsActive *bool `json:"is_active,omitempty"` + Name string `json:"name,omitempty"` +} + +// CreateCspIdpConfigRequest CSP IDP 설정 생성 요청 +type CreateCspIdpConfigRequest struct { + Name string `json:"name" binding:"required"` + CspAccountID uint `json:"csp_account_id" binding:"required"` + AuthMethod AuthMethodType `json:"auth_method" binding:"required,oneof=OIDC SAML SECRET_KEY"` + Config map[string]string `json:"config" binding:"required"` + Description string `json:"description"` +} + +// UpdateCspIdpConfigRequest CSP IDP 설정 수정 요청 +type UpdateCspIdpConfigRequest struct { + Name string `json:"name"` + Config map[string]string `json:"config"` + IsActive *bool `json:"is_active"` + Description string `json:"description"` +} diff --git a/src/model/csp_policy.go b/src/model/csp_policy.go new file mode 100644 index 00000000..835065d5 --- /dev/null +++ b/src/model/csp_policy.go @@ -0,0 +1,152 @@ +package model + +import ( + "time" +) + +// PolicyType 정책 타입 +type PolicyType string + +const ( + PolicyTypeInline PolicyType = "inline" // 인라인 정책 (역할에 직접 포함) + PolicyTypeManaged PolicyType = "managed" // 관리형 정책 (독립 정책) + PolicyTypeCustom PolicyType = "custom" // 사용자 정의 정책 +) + +// CspPolicy CSP 정책 모델 +// AWS IAM Policy, GCP IAM Role, Azure Role Definition 등을 관리 +type CspPolicy struct { + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"size:255;not null" json:"name"` + CspAccountID uint `gorm:"not null" json:"csp_account_id"` + CspAccount *CspAccount `gorm:"foreignKey:CspAccountID" json:"csp_account,omitempty"` + PolicyType PolicyType `gorm:"size:50;not null" json:"policy_type"` // inline, managed, custom + PolicyArn string `gorm:"size:500" json:"policy_arn,omitempty"` + PolicyDoc map[string]interface{} `gorm:"type:jsonb;serializer:json" json:"policy_doc,omitempty"` + Description string `gorm:"size:500" json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// TableName CspPolicy 테이블 이름 반환 +func (CspPolicy) TableName() string { + return "mcmp_csp_policies" +} + +// CspRolePolicyMapping CSP 역할-정책 매핑 모델 +type CspRolePolicyMapping struct { + CspRoleID uint `gorm:"primaryKey" json:"csp_role_id"` + CspPolicyID uint `gorm:"primaryKey" json:"csp_policy_id"` + CspRole *CspRole `gorm:"foreignKey:CspRoleID" json:"csp_role,omitempty"` + CspPolicy *CspPolicy `gorm:"foreignKey:CspPolicyID" json:"csp_policy,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +// TableName CspRolePolicyMapping 테이블 이름 반환 +func (CspRolePolicyMapping) TableName() string { + return "mcmp_csp_role_policy_mappings" +} + +// PolicyDocument AWS IAM Policy Document 구조 +// PolicyDoc 예시 (AWS): +// +// { +// "Version": "2012-10-17", +// "Statement": [ +// { +// "Effect": "Allow", +// "Action": ["s3:GetObject", "s3:PutObject"], +// "Resource": ["arn:aws:s3:::my-bucket/*"] +// } +// ] +// } +// +// PolicyDoc 예시 (GCP): +// +// { +// "included_permissions": ["storage.objects.get", "storage.objects.create"], +// "stage": "GA" +// } +// +// PolicyDoc 예시 (Azure): +// +// { +// "actions": ["Microsoft.Storage/storageAccounts/read"], +// "not_actions": [], +// "data_actions": [], +// "not_data_actions": [] +// } + +// IsInline 인라인 정책인지 확인 +func (p *CspPolicy) IsInline() bool { + return p.PolicyType == PolicyTypeInline +} + +// IsManaged 관리형 정책인지 확인 +func (p *CspPolicy) IsManaged() bool { + return p.PolicyType == PolicyTypeManaged +} + +// IsCustom 사용자 정의 정책인지 확인 +func (p *CspPolicy) IsCustom() bool { + return p.PolicyType == PolicyTypeCustom +} + +// GetPolicyVersion 정책 버전 반환 (AWS) +func (p *CspPolicy) GetPolicyVersion() string { + if p.PolicyDoc == nil { + return "" + } + if version, ok := p.PolicyDoc["Version"].(string); ok { + return version + } + return "" +} + +// GetStatements 정책 Statement 반환 (AWS) +func (p *CspPolicy) GetStatements() []interface{} { + if p.PolicyDoc == nil { + return nil + } + if statements, ok := p.PolicyDoc["Statement"].([]interface{}); ok { + return statements + } + return nil +} + +// CspPolicyFilter CSP 정책 조회 필터 +type CspPolicyFilter struct { + CspAccountID *uint `json:"csp_account_id,omitempty"` + PolicyType PolicyType `json:"policy_type,omitempty"` + Name string `json:"name,omitempty"` +} + +// CreateCspPolicyRequest CSP 정책 생성 요청 +type CreateCspPolicyRequest struct { + Name string `json:"name" binding:"required"` + CspAccountID uint `json:"csp_account_id" binding:"required"` + PolicyType PolicyType `json:"policy_type" binding:"required,oneof=inline managed custom"` + PolicyArn string `json:"policy_arn"` + PolicyDoc map[string]interface{} `json:"policy_doc"` + Description string `json:"description"` +} + +// UpdateCspPolicyRequest CSP 정책 수정 요청 +type UpdateCspPolicyRequest struct { + Name string `json:"name"` + PolicyArn string `json:"policy_arn"` + PolicyDoc map[string]interface{} `json:"policy_doc"` + Description string `json:"description"` +} + +// AttachPolicyRequest 정책 연결 요청 +type AttachPolicyRequest struct { + CspRoleID uint `json:"csp_role_id" binding:"required"` + CspPolicyID uint `json:"csp_policy_id" binding:"required"` +} + +// SyncPoliciesRequest 정책 동기화 요청 +type SyncPoliciesRequest struct { + CspAccountID uint `json:"csp_account_id" binding:"required"` + PolicyScope string `json:"policy_scope"` // All, AWS, Local +} diff --git a/src/model/csp_role.go b/src/model/csp_role.go index 70a2d95f..66fa6d35 100644 --- a/src/model/csp_role.go +++ b/src/model/csp_role.go @@ -17,7 +17,9 @@ type Tag struct { } // CspRole CSP 역할 모델 -// 대상 CSP와 연결하기 위한 연결정보(AWS에 OIDC로 연결되는 경우 제대로 동작. TODO: SAML을 추가했을 때 Table형태나 다른Table을 추가하게 될 수 있음) +// 대상 CSP와 연결하기 위한 연결정보 +// CspAccount: CSP 계정 정보 참조 +// CspIdpConfig: IDP 연동 설정 참조 (OIDC, SAML, SECRET_KEY) type CspRole struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"size:255;not null" json:"name"` @@ -34,9 +36,17 @@ type CspRole struct { PermissionsBoundary string `gorm:"size:255" json:"permissions_boundary"` RoleLastUsed *RoleLastUsed `gorm:"type:jsonb;serializer:json" json:"role_last_used"` Tags []Tag `gorm:"-" json:"tags"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt *time.Time `json:"deleted_at" gorm:"index"` + + // CSP 계정 및 IDP 설정 참조 (신규 추가) + CspAccountID *uint `gorm:"column:csp_account_id" json:"csp_account_id"` + CspAccount *CspAccount `gorm:"foreignKey:CspAccountID" json:"csp_account,omitempty"` + CspIdpConfigID *uint `gorm:"column:csp_idp_config_id" json:"csp_idp_config_id"` + CspIdpConfig *CspIdpConfig `gorm:"foreignKey:CspIdpConfigID" json:"csp_idp_config,omitempty"` + ExtendedConfig map[string]interface{} `gorm:"type:jsonb;serializer:json" json:"extended_config,omitempty"` + + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt *time.Time `json:"deleted_at" gorm:"index"` } func (CspRole) TableName() string { // Renamed receiver diff --git a/src/model/mcmpapi/mcmpapi_service_meta.go b/src/model/mcmpapi/mcmpapi_service_meta.go new file mode 100644 index 00000000..8dfba816 --- /dev/null +++ b/src/model/mcmpapi/mcmpapi_service_meta.go @@ -0,0 +1,18 @@ +package mcmpapi + +import "time" + +// McmpApiServiceMeta stores version metadata for each service from _meta section +type McmpApiServiceMeta struct { + ServiceName string `gorm:"primaryKey;column:service_name;type:varchar(100)"` + Version string `gorm:"column:version;type:varchar(50)"` + Repository string `gorm:"column:repository;type:varchar(255)"` + GeneratedAt time.Time `gorm:"column:generated_at"` + SyncedAt time.Time `gorm:"column:synced_at;autoUpdateTime"` + CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` +} + +// TableName returns the table name for GORM +func (McmpApiServiceMeta) TableName() string { + return "mcmp_api_service_meta" +} diff --git a/src/model/request.go b/src/model/request.go index 28f0afcd..a6ee8d37 100644 --- a/src/model/request.go +++ b/src/model/request.go @@ -228,3 +228,34 @@ type FilterRoleMasterMappingRequest struct { CspType string `json:"cspType,omitempty"` AuthMethod string `json:"authMethod,omitempty"` } + +// ImportApiFramework represents a single framework to import +type ImportApiFramework struct { + Name string `json:"name" validate:"required"` // Framework name (e.g., "mc-infra-manager") + Version string `json:"version" validate:"required"` // Framework version (e.g., "0.9.22") + Repository string `json:"repository,omitempty"` // Repository URL (e.g., "https://github.com/...") + SourceType string `json:"sourceType" validate:"required"` // Source type: "swagger" or "openapi" + SourceURL string `json:"sourceUrl" validate:"required"` // URL to fetch the API specification from +} + +// ImportApiRequest represents the request body for importing APIs from remote sources +type ImportApiRequest struct { + Frameworks []ImportApiFramework `json:"frameworks" validate:"required,min=1"` +} + +// ImportApiFrameworkResult represents the result of importing a single framework +type ImportApiFrameworkResult struct { + Name string `json:"name"` // Framework name + Version string `json:"version"` // Framework version + Success bool `json:"success"` // Whether the import was successful + ActionCount int `json:"actionCount,omitempty"` // Number of actions imported (on success) + ErrorMessage string `json:"errorMessage,omitempty"` // Error message (on failure) +} + +// ImportApiResponse represents the response body for importing APIs +type ImportApiResponse struct { + TotalFrameworks int `json:"totalFrameworks"` // Total number of frameworks in request + SuccessCount int `json:"successCount"` // Number of successfully imported frameworks + FailureCount int `json:"failureCount"` // Number of failed frameworks + FrameworkResults []ImportApiFrameworkResult `json:"frameworkResults"` // Detailed results for each framework +} diff --git a/src/repository/csp_account_repository.go b/src/repository/csp_account_repository.go new file mode 100644 index 00000000..82719723 --- /dev/null +++ b/src/repository/csp_account_repository.go @@ -0,0 +1,150 @@ +package repository + +import ( + "fmt" + + "github.com/m-cmp/mc-iam-manager/model" + "gorm.io/gorm" +) + +// CspAccountRepository CSP 계정 레포지토리 +type CspAccountRepository struct { + db *gorm.DB +} + +// NewCspAccountRepository 새 CspAccountRepository 인스턴스 생성 +func NewCspAccountRepository(db *gorm.DB) *CspAccountRepository { + return &CspAccountRepository{db: db} +} + +// Create CSP 계정 생성 +func (r *CspAccountRepository) Create(account *model.CspAccount) error { + if err := r.db.Create(account).Error; err != nil { + return fmt.Errorf("failed to create CSP account: %w", err) + } + return nil +} + +// GetByID ID로 CSP 계정 조회 +func (r *CspAccountRepository) GetByID(id uint) (*model.CspAccount, error) { + var account model.CspAccount + if err := r.db.First(&account, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP account by ID: %w", err) + } + return &account, nil +} + +// GetByName 이름으로 CSP 계정 조회 +func (r *CspAccountRepository) GetByName(name string) (*model.CspAccount, error) { + var account model.CspAccount + if err := r.db.Where("name = ?", name).First(&account).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP account by name: %w", err) + } + return &account, nil +} + +// GetByNameAndCspType 이름과 CSP 타입으로 CSP 계정 조회 +func (r *CspAccountRepository) GetByNameAndCspType(name string, cspType string) (*model.CspAccount, error) { + var account model.CspAccount + if err := r.db.Where("name = ? AND csp_type = ?", name, cspType).First(&account).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + return &account, nil +} + +// List CSP 계정 목록 조회 +func (r *CspAccountRepository) List(filter *model.CspAccountFilter) ([]*model.CspAccount, error) { + var accounts []*model.CspAccount + query := r.db.Model(&model.CspAccount{}) + + if filter != nil { + if filter.CspType != "" { + query = query.Where("csp_type = ?", filter.CspType) + } + if filter.IsActive != nil { + query = query.Where("is_active = ?", *filter.IsActive) + } + if filter.Name != "" { + query = query.Where("name LIKE ?", "%"+filter.Name+"%") + } + } + + if err := query.Order("created_at DESC").Find(&accounts).Error; err != nil { + return nil, fmt.Errorf("failed to list CSP accounts: %w", err) + } + return accounts, nil +} + +// Update CSP 계정 수정 +func (r *CspAccountRepository) Update(account *model.CspAccount) error { + if err := r.db.Save(account).Error; err != nil { + return fmt.Errorf("failed to update CSP account: %w", err) + } + return nil +} + +// Delete CSP 계정 삭제 +func (r *CspAccountRepository) Delete(id uint) error { + result := r.db.Delete(&model.CspAccount{}, id) + if result.Error != nil { + return fmt.Errorf("failed to delete CSP account: %w", result.Error) + } + if result.RowsAffected == 0 { + return fmt.Errorf("CSP account not found") + } + return nil +} + +// ExistsByID ID로 CSP 계정 존재 여부 확인 +func (r *CspAccountRepository) ExistsByID(id uint) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspAccount{}).Where("id = ?", id).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP account existence: %w", err) + } + return count > 0, nil +} + +// ExistsByName 이름으로 CSP 계정 존재 여부 확인 +func (r *CspAccountRepository) ExistsByName(name string) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspAccount{}).Where("name = ?", name).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP account existence: %w", err) + } + return count > 0, nil +} + +// ExistsByNameAndCspType 이름과 CSP 타입으로 CSP 계정 존재 여부 확인 +func (r *CspAccountRepository) ExistsByNameAndCspType(name string, cspType string) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspAccount{}).Where("name = ? AND csp_type = ?", name, cspType).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP account existence: %w", err) + } + return count > 0, nil +} + +// GetActiveAccounts 활성 CSP 계정 목록 조회 +func (r *CspAccountRepository) GetActiveAccounts() ([]*model.CspAccount, error) { + var accounts []*model.CspAccount + if err := r.db.Where("is_active = ?", true).Find(&accounts).Error; err != nil { + return nil, fmt.Errorf("failed to get active CSP accounts: %w", err) + } + return accounts, nil +} + +// GetByCspType CSP 타입으로 계정 목록 조회 +func (r *CspAccountRepository) GetByCspType(cspType string) ([]*model.CspAccount, error) { + var accounts []*model.CspAccount + if err := r.db.Where("csp_type = ?", cspType).Find(&accounts).Error; err != nil { + return nil, fmt.Errorf("failed to get CSP accounts by type: %w", err) + } + return accounts, nil +} diff --git a/src/repository/csp_idp_config_repository.go b/src/repository/csp_idp_config_repository.go new file mode 100644 index 00000000..40ea243c --- /dev/null +++ b/src/repository/csp_idp_config_repository.go @@ -0,0 +1,180 @@ +package repository + +import ( + "fmt" + + "github.com/m-cmp/mc-iam-manager/model" + "gorm.io/gorm" +) + +// CspIdpConfigRepository CSP IDP 설정 레포지토리 +type CspIdpConfigRepository struct { + db *gorm.DB +} + +// NewCspIdpConfigRepository 새 CspIdpConfigRepository 인스턴스 생성 +func NewCspIdpConfigRepository(db *gorm.DB) *CspIdpConfigRepository { + return &CspIdpConfigRepository{db: db} +} + +// Create CSP IDP 설정 생성 +func (r *CspIdpConfigRepository) Create(config *model.CspIdpConfig) error { + if err := r.db.Create(config).Error; err != nil { + return fmt.Errorf("failed to create CSP IDP config: %w", err) + } + return nil +} + +// GetByID ID로 CSP IDP 설정 조회 +func (r *CspIdpConfigRepository) GetByID(id uint) (*model.CspIdpConfig, error) { + var config model.CspIdpConfig + if err := r.db.Preload("CspAccount").First(&config, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP IDP config by ID: %w", err) + } + return &config, nil +} + +// GetByName 이름으로 CSP IDP 설정 조회 +func (r *CspIdpConfigRepository) GetByName(name string) (*model.CspIdpConfig, error) { + var config model.CspIdpConfig + if err := r.db.Preload("CspAccount").Where("name = ?", name).First(&config).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP IDP config by name: %w", err) + } + return &config, nil +} + +// GetByNameAndAccountID 이름과 계정 ID로 CSP IDP 설정 조회 +func (r *CspIdpConfigRepository) GetByNameAndAccountID(name string, accountID uint) (*model.CspIdpConfig, error) { + var config model.CspIdpConfig + if err := r.db.Preload("CspAccount").Where("name = ? AND csp_account_id = ?", name, accountID).First(&config).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP IDP config: %w", err) + } + return &config, nil +} + +// List CSP IDP 설정 목록 조회 +func (r *CspIdpConfigRepository) List(filter *model.CspIdpConfigFilter) ([]*model.CspIdpConfig, error) { + var configs []*model.CspIdpConfig + query := r.db.Model(&model.CspIdpConfig{}).Preload("CspAccount") + + if filter != nil { + if filter.CspAccountID != nil { + query = query.Where("csp_account_id = ?", *filter.CspAccountID) + } + if filter.AuthMethod != "" { + query = query.Where("auth_method = ?", filter.AuthMethod) + } + if filter.IsActive != nil { + query = query.Where("is_active = ?", *filter.IsActive) + } + if filter.Name != "" { + query = query.Where("name LIKE ?", "%"+filter.Name+"%") + } + } + + if err := query.Order("created_at DESC").Find(&configs).Error; err != nil { + return nil, fmt.Errorf("failed to list CSP IDP configs: %w", err) + } + return configs, nil +} + +// Update CSP IDP 설정 수정 +func (r *CspIdpConfigRepository) Update(config *model.CspIdpConfig) error { + if err := r.db.Save(config).Error; err != nil { + return fmt.Errorf("failed to update CSP IDP config: %w", err) + } + return nil +} + +// Delete CSP IDP 설정 삭제 +func (r *CspIdpConfigRepository) Delete(id uint) error { + result := r.db.Delete(&model.CspIdpConfig{}, id) + if result.Error != nil { + return fmt.Errorf("failed to delete CSP IDP config: %w", result.Error) + } + if result.RowsAffected == 0 { + return fmt.Errorf("CSP IDP config not found") + } + return nil +} + +// ExistsByID ID로 CSP IDP 설정 존재 여부 확인 +func (r *CspIdpConfigRepository) ExistsByID(id uint) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspIdpConfig{}).Where("id = ?", id).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP IDP config existence: %w", err) + } + return count > 0, nil +} + +// ExistsByName 이름으로 CSP IDP 설정 존재 여부 확인 +func (r *CspIdpConfigRepository) ExistsByName(name string) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspIdpConfig{}).Where("name = ?", name).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP IDP config existence: %w", err) + } + return count > 0, nil +} + +// ExistsByNameAndAccountID 이름과 계정 ID로 CSP IDP 설정 존재 여부 확인 +func (r *CspIdpConfigRepository) ExistsByNameAndAccountID(name string, accountID uint) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspIdpConfig{}).Where("name = ? AND csp_account_id = ?", name, accountID).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP IDP config existence: %w", err) + } + return count > 0, nil +} + +// GetByAccountID 계정 ID로 CSP IDP 설정 목록 조회 +func (r *CspIdpConfigRepository) GetByAccountID(accountID uint) ([]*model.CspIdpConfig, error) { + var configs []*model.CspIdpConfig + if err := r.db.Preload("CspAccount").Where("csp_account_id = ?", accountID).Find(&configs).Error; err != nil { + return nil, fmt.Errorf("failed to get CSP IDP configs by account ID: %w", err) + } + return configs, nil +} + +// GetActiveConfigs 활성 CSP IDP 설정 목록 조회 +func (r *CspIdpConfigRepository) GetActiveConfigs() ([]*model.CspIdpConfig, error) { + var configs []*model.CspIdpConfig + if err := r.db.Preload("CspAccount").Where("is_active = ?", true).Find(&configs).Error; err != nil { + return nil, fmt.Errorf("failed to get active CSP IDP configs: %w", err) + } + return configs, nil +} + +// GetActiveByAccountID 특정 계정의 활성 CSP IDP 설정 목록 조회 +func (r *CspIdpConfigRepository) GetActiveByAccountID(accountID uint) ([]*model.CspIdpConfig, error) { + var configs []*model.CspIdpConfig + if err := r.db.Preload("CspAccount").Where("csp_account_id = ? AND is_active = ?", accountID, true).Find(&configs).Error; err != nil { + return nil, fmt.Errorf("failed to get active CSP IDP configs by account ID: %w", err) + } + return configs, nil +} + +// GetByAuthMethod 인증 방식으로 CSP IDP 설정 목록 조회 +func (r *CspIdpConfigRepository) GetByAuthMethod(authMethod model.AuthMethodType) ([]*model.CspIdpConfig, error) { + var configs []*model.CspIdpConfig + if err := r.db.Preload("CspAccount").Where("auth_method = ?", authMethod).Find(&configs).Error; err != nil { + return nil, fmt.Errorf("failed to get CSP IDP configs by auth method: %w", err) + } + return configs, nil +} + +// CountByAccountID 특정 계정의 CSP IDP 설정 개수 조회 +func (r *CspIdpConfigRepository) CountByAccountID(accountID uint) (int64, error) { + var count int64 + if err := r.db.Model(&model.CspIdpConfig{}).Where("csp_account_id = ?", accountID).Count(&count).Error; err != nil { + return 0, fmt.Errorf("failed to count CSP IDP configs: %w", err) + } + return count, nil +} diff --git a/src/repository/csp_policy_repository.go b/src/repository/csp_policy_repository.go new file mode 100644 index 00000000..f74dfa66 --- /dev/null +++ b/src/repository/csp_policy_repository.go @@ -0,0 +1,262 @@ +package repository + +import ( + "fmt" + + "github.com/m-cmp/mc-iam-manager/model" + "gorm.io/gorm" +) + +// CspPolicyRepository CSP 정책 레포지토리 +type CspPolicyRepository struct { + db *gorm.DB +} + +// NewCspPolicyRepository 새 CspPolicyRepository 인스턴스 생성 +func NewCspPolicyRepository(db *gorm.DB) *CspPolicyRepository { + return &CspPolicyRepository{db: db} +} + +// Create CSP 정책 생성 +func (r *CspPolicyRepository) Create(policy *model.CspPolicy) error { + if err := r.db.Create(policy).Error; err != nil { + return fmt.Errorf("failed to create CSP policy: %w", err) + } + return nil +} + +// GetByID ID로 CSP 정책 조회 +func (r *CspPolicyRepository) GetByID(id uint) (*model.CspPolicy, error) { + var policy model.CspPolicy + if err := r.db.Preload("CspAccount").First(&policy, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP policy by ID: %w", err) + } + return &policy, nil +} + +// GetByName 이름으로 CSP 정책 조회 +func (r *CspPolicyRepository) GetByName(name string) (*model.CspPolicy, error) { + var policy model.CspPolicy + if err := r.db.Preload("CspAccount").Where("name = ?", name).First(&policy).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP policy by name: %w", err) + } + return &policy, nil +} + +// GetByArn ARN으로 CSP 정책 조회 +func (r *CspPolicyRepository) GetByArn(arn string) (*model.CspPolicy, error) { + var policy model.CspPolicy + if err := r.db.Preload("CspAccount").Where("policy_arn = ?", arn).First(&policy).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP policy by ARN: %w", err) + } + return &policy, nil +} + +// GetByNameAndAccountID 이름과 계정 ID로 CSP 정책 조회 +func (r *CspPolicyRepository) GetByNameAndAccountID(name string, accountID uint) (*model.CspPolicy, error) { + var policy model.CspPolicy + if err := r.db.Preload("CspAccount").Where("name = ? AND csp_account_id = ?", name, accountID).First(&policy).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, fmt.Errorf("failed to get CSP policy: %w", err) + } + return &policy, nil +} + +// List CSP 정책 목록 조회 +func (r *CspPolicyRepository) List(filter *model.CspPolicyFilter) ([]*model.CspPolicy, error) { + var policies []*model.CspPolicy + query := r.db.Model(&model.CspPolicy{}).Preload("CspAccount") + + if filter != nil { + if filter.CspAccountID != nil { + query = query.Where("csp_account_id = ?", *filter.CspAccountID) + } + if filter.PolicyType != "" { + query = query.Where("policy_type = ?", filter.PolicyType) + } + if filter.Name != "" { + query = query.Where("name LIKE ?", "%"+filter.Name+"%") + } + } + + if err := query.Order("created_at DESC").Find(&policies).Error; err != nil { + return nil, fmt.Errorf("failed to list CSP policies: %w", err) + } + return policies, nil +} + +// Update CSP 정책 수정 +func (r *CspPolicyRepository) Update(policy *model.CspPolicy) error { + if err := r.db.Save(policy).Error; err != nil { + return fmt.Errorf("failed to update CSP policy: %w", err) + } + return nil +} + +// Delete CSP 정책 삭제 +func (r *CspPolicyRepository) Delete(id uint) error { + // 트랜잭션으로 매핑 테이블도 함께 삭제 + return r.db.Transaction(func(tx *gorm.DB) error { + // 매핑 관계 삭제 + if err := tx.Where("csp_policy_id = ?", id).Delete(&model.CspRolePolicyMapping{}).Error; err != nil { + return fmt.Errorf("failed to delete policy mappings: %w", err) + } + + // 정책 삭제 + result := tx.Delete(&model.CspPolicy{}, id) + if result.Error != nil { + return fmt.Errorf("failed to delete CSP policy: %w", result.Error) + } + if result.RowsAffected == 0 { + return fmt.Errorf("CSP policy not found") + } + return nil + }) +} + +// ExistsByID ID로 CSP 정책 존재 여부 확인 +func (r *CspPolicyRepository) ExistsByID(id uint) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspPolicy{}).Where("id = ?", id).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP policy existence: %w", err) + } + return count > 0, nil +} + +// ExistsByName 이름으로 CSP 정책 존재 여부 확인 +func (r *CspPolicyRepository) ExistsByName(name string) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspPolicy{}).Where("name = ?", name).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP policy existence: %w", err) + } + return count > 0, nil +} + +// ExistsByArn ARN으로 CSP 정책 존재 여부 확인 +func (r *CspPolicyRepository) ExistsByArn(arn string) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspPolicy{}).Where("policy_arn = ?", arn).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP policy existence: %w", err) + } + return count > 0, nil +} + +// ExistsByNameAndAccountID 이름과 계정 ID로 CSP 정책 존재 여부 확인 +func (r *CspPolicyRepository) ExistsByNameAndAccountID(name string, accountID uint) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspPolicy{}).Where("name = ? AND csp_account_id = ?", name, accountID).Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check CSP policy existence: %w", err) + } + return count > 0, nil +} + +// GetByAccountID 계정 ID로 CSP 정책 목록 조회 +func (r *CspPolicyRepository) GetByAccountID(accountID uint) ([]*model.CspPolicy, error) { + var policies []*model.CspPolicy + if err := r.db.Preload("CspAccount").Where("csp_account_id = ?", accountID).Find(&policies).Error; err != nil { + return nil, fmt.Errorf("failed to get CSP policies by account ID: %w", err) + } + return policies, nil +} + +// GetByPolicyType 정책 타입으로 CSP 정책 목록 조회 +func (r *CspPolicyRepository) GetByPolicyType(policyType model.PolicyType) ([]*model.CspPolicy, error) { + var policies []*model.CspPolicy + if err := r.db.Preload("CspAccount").Where("policy_type = ?", policyType).Find(&policies).Error; err != nil { + return nil, fmt.Errorf("failed to get CSP policies by type: %w", err) + } + return policies, nil +} + +// AttachPolicyToRole 역할에 정책 연결 +func (r *CspPolicyRepository) AttachPolicyToRole(roleID, policyID uint) error { + mapping := model.CspRolePolicyMapping{ + CspRoleID: roleID, + CspPolicyID: policyID, + } + if err := r.db.Create(&mapping).Error; err != nil { + return fmt.Errorf("failed to attach policy to role: %w", err) + } + return nil +} + +// DetachPolicyFromRole 역할에서 정책 분리 +func (r *CspPolicyRepository) DetachPolicyFromRole(roleID, policyID uint) error { + result := r.db.Where("csp_role_id = ? AND csp_policy_id = ?", roleID, policyID).Delete(&model.CspRolePolicyMapping{}) + if result.Error != nil { + return fmt.Errorf("failed to detach policy from role: %w", result.Error) + } + if result.RowsAffected == 0 { + return fmt.Errorf("policy mapping not found") + } + return nil +} + +// GetPoliciesByRoleID 역할에 연결된 정책 목록 조회 +func (r *CspPolicyRepository) GetPoliciesByRoleID(roleID uint) ([]*model.CspPolicy, error) { + var policies []*model.CspPolicy + err := r.db. + Joins("JOIN mcmp_csp_role_policy_mappings ON mcmp_csp_role_policy_mappings.csp_policy_id = mcmp_csp_policies.id"). + Where("mcmp_csp_role_policy_mappings.csp_role_id = ?", roleID). + Preload("CspAccount"). + Find(&policies).Error + if err != nil { + return nil, fmt.Errorf("failed to get policies by role ID: %w", err) + } + return policies, nil +} + +// GetRolesByPolicyID 정책이 연결된 역할 목록 조회 +func (r *CspPolicyRepository) GetRolesByPolicyID(policyID uint) ([]*model.CspRole, error) { + var roles []*model.CspRole + err := r.db. + Joins("JOIN mcmp_csp_role_policy_mappings ON mcmp_csp_role_policy_mappings.csp_role_id = mcmp_role_csp_roles.id"). + Where("mcmp_csp_role_policy_mappings.csp_policy_id = ?", policyID). + Find(&roles).Error + if err != nil { + return nil, fmt.Errorf("failed to get roles by policy ID: %w", err) + } + return roles, nil +} + +// IsPolicyAttachedToRole 역할에 정책이 연결되어 있는지 확인 +func (r *CspPolicyRepository) IsPolicyAttachedToRole(roleID, policyID uint) (bool, error) { + var count int64 + if err := r.db.Model(&model.CspRolePolicyMapping{}). + Where("csp_role_id = ? AND csp_policy_id = ?", roleID, policyID). + Count(&count).Error; err != nil { + return false, fmt.Errorf("failed to check policy attachment: %w", err) + } + return count > 0, nil +} + +// CountByAccountID 특정 계정의 CSP 정책 개수 조회 +func (r *CspPolicyRepository) CountByAccountID(accountID uint) (int64, error) { + var count int64 + if err := r.db.Model(&model.CspPolicy{}).Where("csp_account_id = ?", accountID).Count(&count).Error; err != nil { + return 0, fmt.Errorf("failed to count CSP policies: %w", err) + } + return count, nil +} + +// GetManagedPoliciesByAccountID 특정 계정의 관리형 정책 목록 조회 +func (r *CspPolicyRepository) GetManagedPoliciesByAccountID(accountID uint) ([]*model.CspPolicy, error) { + var policies []*model.CspPolicy + if err := r.db.Preload("CspAccount"). + Where("csp_account_id = ? AND policy_type = ?", accountID, model.PolicyTypeManaged). + Find(&policies).Error; err != nil { + return nil, fmt.Errorf("failed to get managed policies: %w", err) + } + return policies, nil +} diff --git a/src/repository/mcmpapi_repository.go b/src/repository/mcmpapi_repository.go index c7d74352..2a297733 100644 --- a/src/repository/mcmpapi_repository.go +++ b/src/repository/mcmpapi_repository.go @@ -19,6 +19,10 @@ type McmpApiRepository interface { UpdateService(serviceName string, updates map[string]interface{}) error GetService(serviceName string) (*mcmpapi.McmpApiService, error) GetServiceAction(serviceName, actionName string) (*mcmpapi.McmpApiAction, error) + // New methods for upsert logic and version tracking + DeleteActionsByServiceName(tx *gorm.DB, serviceName string) error + GetServiceMeta(serviceName string) (*mcmpapi.McmpApiServiceMeta, error) + UpsertServiceMeta(tx *gorm.DB, meta *mcmpapi.McmpApiServiceMeta) error } // mcmpApiRepository implements the McmpApiRepository interface. @@ -337,3 +341,47 @@ func (r *mcmpApiRepository) GetActiveService(serviceName string) (*mcmpapi.McmpA return &service, nil } + +// DeleteActionsByServiceName deletes all actions for a service within a transaction. +func (r *mcmpApiRepository) DeleteActionsByServiceName(tx *gorm.DB, serviceName string) error { + query := tx.Where("service_name = ?", serviceName).Delete(&mcmpapi.McmpApiAction{}) + if err := query.Error; err != nil { + sql := query.Statement.SQL.String() + args := query.Statement.Vars + log.Printf("DeleteActionsByServiceName SQL Query (ERROR): %s", sql) + log.Printf("DeleteActionsByServiceName SQL Args (ERROR): %v", args) + return fmt.Errorf("failed to delete actions for service %s: %w", serviceName, err) + } + log.Printf("Deleted %d actions for service %s", query.RowsAffected, serviceName) + return nil +} + +// GetServiceMeta retrieves version metadata for a service. +func (r *mcmpApiRepository) GetServiceMeta(serviceName string) (*mcmpapi.McmpApiServiceMeta, error) { + var meta mcmpapi.McmpApiServiceMeta + query := r.db.Where("service_name = ?", serviceName).First(&meta) + if err := query.Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + sql := query.Statement.SQL.String() + args := query.Statement.Vars + log.Printf("GetServiceMeta SQL Query (ERROR): %s", sql) + log.Printf("GetServiceMeta SQL Args (ERROR): %v", args) + } + return nil, err + } + return &meta, nil +} + +// UpsertServiceMeta creates or updates version metadata within a transaction. +func (r *mcmpApiRepository) UpsertServiceMeta(tx *gorm.DB, meta *mcmpapi.McmpApiServiceMeta) error { + // Use Save which does upsert based on primary key + query := tx.Save(meta) + if err := query.Error; err != nil { + sql := query.Statement.SQL.String() + args := query.Statement.Vars + log.Printf("UpsertServiceMeta SQL Query (ERROR): %s", sql) + log.Printf("UpsertServiceMeta SQL Args (ERROR): %v", args) + return fmt.Errorf("failed to upsert meta for service %s: %w", meta.ServiceName, err) + } + return nil +} diff --git a/src/service/csp_account_service.go b/src/service/csp_account_service.go new file mode 100644 index 00000000..23da0a8f --- /dev/null +++ b/src/service/csp_account_service.go @@ -0,0 +1,257 @@ +package service + +import ( + "fmt" + "log" + + "github.com/m-cmp/mc-iam-manager/model" + "github.com/m-cmp/mc-iam-manager/repository" + "gorm.io/gorm" +) + +// CspAccountService CSP 계정 서비스 +type CspAccountService struct { + db *gorm.DB + cspAccountRepo *repository.CspAccountRepository + cspIdpConfigRepo *repository.CspIdpConfigRepository + cspPolicyRepo *repository.CspPolicyRepository +} + +// NewCspAccountService 새 CspAccountService 인스턴스 생성 +func NewCspAccountService(db *gorm.DB) *CspAccountService { + return &CspAccountService{ + db: db, + cspAccountRepo: repository.NewCspAccountRepository(db), + cspIdpConfigRepo: repository.NewCspIdpConfigRepository(db), + cspPolicyRepo: repository.NewCspPolicyRepository(db), + } +} + +// CreateCspAccount CSP 계정 생성 +func (s *CspAccountService) CreateCspAccount(req *model.CreateCspAccountRequest) (*model.CspAccount, error) { + // 이름 중복 확인 + exists, err := s.cspAccountRepo.ExistsByNameAndCspType(req.Name, req.CspType) + if err != nil { + return nil, fmt.Errorf("failed to check CSP account existence: %w", err) + } + if exists { + return nil, fmt.Errorf("CSP account with name '%s' and type '%s' already exists", req.Name, req.CspType) + } + + // CSP 계정 생성 + account := &model.CspAccount{ + Name: req.Name, + CspType: req.CspType, + AccountInfo: req.AccountInfo, + IsActive: true, + Description: req.Description, + } + + if err := s.cspAccountRepo.Create(account); err != nil { + return nil, fmt.Errorf("failed to create CSP account: %w", err) + } + + log.Printf("Created CSP account: %s (type: %s)", account.Name, account.CspType) + return account, nil +} + +// GetCspAccountByID ID로 CSP 계정 조회 +func (s *CspAccountService) GetCspAccountByID(id uint) (*model.CspAccount, error) { + account, err := s.cspAccountRepo.GetByID(id) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return nil, fmt.Errorf("CSP account not found with ID: %d", id) + } + return account, nil +} + +// GetCspAccountByName 이름으로 CSP 계정 조회 +func (s *CspAccountService) GetCspAccountByName(name string) (*model.CspAccount, error) { + account, err := s.cspAccountRepo.GetByName(name) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + return account, nil +} + +// ListCspAccounts CSP 계정 목록 조회 +func (s *CspAccountService) ListCspAccounts(filter *model.CspAccountFilter) ([]*model.CspAccount, error) { + accounts, err := s.cspAccountRepo.List(filter) + if err != nil { + return nil, fmt.Errorf("failed to list CSP accounts: %w", err) + } + return accounts, nil +} + +// UpdateCspAccount CSP 계정 수정 +func (s *CspAccountService) UpdateCspAccount(id uint, req *model.UpdateCspAccountRequest) (*model.CspAccount, error) { + // 기존 계정 조회 + account, err := s.cspAccountRepo.GetByID(id) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return nil, fmt.Errorf("CSP account not found with ID: %d", id) + } + + // 필드 업데이트 + if req.Name != "" { + // 이름 변경 시 중복 확인 + if req.Name != account.Name { + exists, err := s.cspAccountRepo.ExistsByNameAndCspType(req.Name, account.CspType) + if err != nil { + return nil, fmt.Errorf("failed to check CSP account existence: %w", err) + } + if exists { + return nil, fmt.Errorf("CSP account with name '%s' already exists", req.Name) + } + } + account.Name = req.Name + } + if req.AccountInfo != nil { + account.AccountInfo = req.AccountInfo + } + if req.IsActive != nil { + account.IsActive = *req.IsActive + } + if req.Description != "" { + account.Description = req.Description + } + + if err := s.cspAccountRepo.Update(account); err != nil { + return nil, fmt.Errorf("failed to update CSP account: %w", err) + } + + log.Printf("Updated CSP account: %s (ID: %d)", account.Name, account.ID) + return account, nil +} + +// DeleteCspAccount CSP 계정 삭제 +func (s *CspAccountService) DeleteCspAccount(id uint) error { + // 계정 존재 확인 + exists, err := s.cspAccountRepo.ExistsByID(id) + if err != nil { + return fmt.Errorf("failed to check CSP account existence: %w", err) + } + if !exists { + return fmt.Errorf("CSP account not found with ID: %d", id) + } + + // 연관된 IDP 설정 확인 + idpCount, err := s.cspIdpConfigRepo.CountByAccountID(id) + if err != nil { + return fmt.Errorf("failed to count IDP configs: %w", err) + } + if idpCount > 0 { + return fmt.Errorf("cannot delete CSP account: %d IDP configs are associated", idpCount) + } + + // 연관된 정책 확인 + policyCount, err := s.cspPolicyRepo.CountByAccountID(id) + if err != nil { + return fmt.Errorf("failed to count policies: %w", err) + } + if policyCount > 0 { + return fmt.Errorf("cannot delete CSP account: %d policies are associated", policyCount) + } + + if err := s.cspAccountRepo.Delete(id); err != nil { + return fmt.Errorf("failed to delete CSP account: %w", err) + } + + log.Printf("Deleted CSP account with ID: %d", id) + return nil +} + +// ValidateCspAccount CSP 계정 유효성 검증 +func (s *CspAccountService) ValidateCspAccount(id uint) error { + account, err := s.cspAccountRepo.GetByID(id) + if err != nil { + return fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return fmt.Errorf("CSP account not found with ID: %d", id) + } + + // CSP 타입별 필수 필드 검증 + switch account.CspType { + case "aws": + if account.GetAccountID() == "" { + return fmt.Errorf("AWS account_id is required") + } + case "gcp": + if account.GetProjectID() == "" { + return fmt.Errorf("GCP project_id is required") + } + case "azure": + if account.GetSubscriptionID() == "" { + return fmt.Errorf("Azure subscription_id is required") + } + if account.GetTenantID() == "" { + return fmt.Errorf("Azure tenant_id is required") + } + default: + return fmt.Errorf("unsupported CSP type: %s", account.CspType) + } + + log.Printf("Validated CSP account: %s (ID: %d)", account.Name, account.ID) + return nil +} + +// GetActiveCspAccounts 활성 CSP 계정 목록 조회 +func (s *CspAccountService) GetActiveCspAccounts() ([]*model.CspAccount, error) { + accounts, err := s.cspAccountRepo.GetActiveAccounts() + if err != nil { + return nil, fmt.Errorf("failed to get active CSP accounts: %w", err) + } + return accounts, nil +} + +// GetCspAccountsByCspType CSP 타입별 계정 목록 조회 +func (s *CspAccountService) GetCspAccountsByCspType(cspType string) ([]*model.CspAccount, error) { + accounts, err := s.cspAccountRepo.GetByCspType(cspType) + if err != nil { + return nil, fmt.Errorf("failed to get CSP accounts by type: %w", err) + } + return accounts, nil +} + +// ActivateCspAccount CSP 계정 활성화 +func (s *CspAccountService) ActivateCspAccount(id uint) error { + account, err := s.cspAccountRepo.GetByID(id) + if err != nil { + return fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return fmt.Errorf("CSP account not found with ID: %d", id) + } + + account.IsActive = true + if err := s.cspAccountRepo.Update(account); err != nil { + return fmt.Errorf("failed to activate CSP account: %w", err) + } + + log.Printf("Activated CSP account: %s (ID: %d)", account.Name, account.ID) + return nil +} + +// DeactivateCspAccount CSP 계정 비활성화 +func (s *CspAccountService) DeactivateCspAccount(id uint) error { + account, err := s.cspAccountRepo.GetByID(id) + if err != nil { + return fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return fmt.Errorf("CSP account not found with ID: %d", id) + } + + account.IsActive = false + if err := s.cspAccountRepo.Update(account); err != nil { + return fmt.Errorf("failed to deactivate CSP account: %w", err) + } + + log.Printf("Deactivated CSP account: %s (ID: %d)", account.Name, account.ID) + return nil +} diff --git a/src/service/csp_idp_config_service.go b/src/service/csp_idp_config_service.go new file mode 100644 index 00000000..5ac36925 --- /dev/null +++ b/src/service/csp_idp_config_service.go @@ -0,0 +1,505 @@ +package service + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/m-cmp/mc-iam-manager/model" + "github.com/m-cmp/mc-iam-manager/repository" + "gorm.io/gorm" +) + +// CspIdpConfigService CSP IDP 설정 서비스 +type CspIdpConfigService struct { + db *gorm.DB + cspIdpConfigRepo *repository.CspIdpConfigRepository + cspAccountRepo *repository.CspAccountRepository + keycloakService KeycloakService +} + +// NewCspIdpConfigService 새 CspIdpConfigService 인스턴스 생성 +func NewCspIdpConfigService(db *gorm.DB, keycloakService KeycloakService) *CspIdpConfigService { + return &CspIdpConfigService{ + db: db, + cspIdpConfigRepo: repository.NewCspIdpConfigRepository(db), + cspAccountRepo: repository.NewCspAccountRepository(db), + keycloakService: keycloakService, + } +} + +// CreateCspIdpConfig CSP IDP 설정 생성 +func (s *CspIdpConfigService) CreateCspIdpConfig(req *model.CreateCspIdpConfigRequest) (*model.CspIdpConfig, error) { + // CSP 계정 존재 확인 + account, err := s.cspAccountRepo.GetByID(req.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return nil, fmt.Errorf("CSP account not found with ID: %d", req.CspAccountID) + } + + // 이름 중복 확인 + exists, err := s.cspIdpConfigRepo.ExistsByNameAndAccountID(req.Name, req.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to check IDP config existence: %w", err) + } + if exists { + return nil, fmt.Errorf("IDP config with name '%s' already exists for this account", req.Name) + } + + // IDP 설정 생성 + idpConfig := &model.CspIdpConfig{ + Name: req.Name, + CspAccountID: req.CspAccountID, + AuthMethod: req.AuthMethod, + Config: req.Config, + IsActive: true, + Description: req.Description, + } + + if err := s.cspIdpConfigRepo.Create(idpConfig); err != nil { + return nil, fmt.Errorf("failed to create IDP config: %w", err) + } + + log.Printf("Created CSP IDP config: %s (method: %s)", idpConfig.Name, idpConfig.AuthMethod) + return idpConfig, nil +} + +// GetCspIdpConfigByID ID로 CSP IDP 설정 조회 +func (s *CspIdpConfigService) GetCspIdpConfigByID(id uint) (*model.CspIdpConfig, error) { + idpConfig, err := s.cspIdpConfigRepo.GetByID(id) + if err != nil { + return nil, fmt.Errorf("failed to get IDP config: %w", err) + } + if idpConfig == nil { + return nil, fmt.Errorf("IDP config not found with ID: %d", id) + } + return idpConfig, nil +} + +// ListCspIdpConfigs CSP IDP 설정 목록 조회 +func (s *CspIdpConfigService) ListCspIdpConfigs(filter *model.CspIdpConfigFilter) ([]*model.CspIdpConfig, error) { + configs, err := s.cspIdpConfigRepo.List(filter) + if err != nil { + return nil, fmt.Errorf("failed to list IDP configs: %w", err) + } + return configs, nil +} + +// UpdateCspIdpConfig CSP IDP 설정 수정 +func (s *CspIdpConfigService) UpdateCspIdpConfig(id uint, req *model.UpdateCspIdpConfigRequest) (*model.CspIdpConfig, error) { + // 기존 설정 조회 + idpConfig, err := s.cspIdpConfigRepo.GetByID(id) + if err != nil { + return nil, fmt.Errorf("failed to get IDP config: %w", err) + } + if idpConfig == nil { + return nil, fmt.Errorf("IDP config not found with ID: %d", id) + } + + // 필드 업데이트 + if req.Name != "" { + // 이름 변경 시 중복 확인 + if req.Name != idpConfig.Name { + exists, err := s.cspIdpConfigRepo.ExistsByNameAndAccountID(req.Name, idpConfig.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to check IDP config existence: %w", err) + } + if exists { + return nil, fmt.Errorf("IDP config with name '%s' already exists", req.Name) + } + } + idpConfig.Name = req.Name + } + if req.Config != nil { + idpConfig.Config = req.Config + } + if req.IsActive != nil { + idpConfig.IsActive = *req.IsActive + } + if req.Description != "" { + idpConfig.Description = req.Description + } + + if err := s.cspIdpConfigRepo.Update(idpConfig); err != nil { + return nil, fmt.Errorf("failed to update IDP config: %w", err) + } + + log.Printf("Updated CSP IDP config: %s (ID: %d)", idpConfig.Name, idpConfig.ID) + return idpConfig, nil +} + +// DeleteCspIdpConfig CSP IDP 설정 삭제 +func (s *CspIdpConfigService) DeleteCspIdpConfig(id uint) error { + // 설정 존재 확인 + exists, err := s.cspIdpConfigRepo.ExistsByID(id) + if err != nil { + return fmt.Errorf("failed to check IDP config existence: %w", err) + } + if !exists { + return fmt.Errorf("IDP config not found with ID: %d", id) + } + + // TODO: 연관된 CspRole 확인 (CspRole.CspIdpConfigID 참조) + + if err := s.cspIdpConfigRepo.Delete(id); err != nil { + return fmt.Errorf("failed to delete IDP config: %w", err) + } + + log.Printf("Deleted CSP IDP config with ID: %d", id) + return nil +} + +// TestConnection IDP 연결 테스트 +func (s *CspIdpConfigService) TestConnection(ctx context.Context, id uint) error { + idpConfig, err := s.cspIdpConfigRepo.GetByID(id) + if err != nil { + return fmt.Errorf("failed to get IDP config: %w", err) + } + if idpConfig == nil { + return fmt.Errorf("IDP config not found with ID: %d", id) + } + + // CSP 계정 정보 조회 + account, err := s.cspAccountRepo.GetByID(idpConfig.CspAccountID) + if err != nil { + return fmt.Errorf("failed to get CSP account: %w", err) + } + + // 인증 방식에 따른 연결 테스트 + switch idpConfig.AuthMethod { + case model.AuthMethodOIDC: + return s.testOidcConnection(ctx, idpConfig, account) + case model.AuthMethodSAML: + return s.testSamlConnection(ctx, idpConfig, account) + case model.AuthMethodSecretKey: + return s.testSecretKeyConnection(ctx, idpConfig, account) + default: + return fmt.Errorf("unsupported auth method: %s", idpConfig.AuthMethod) + } +} + +// testOidcConnection OIDC 연결 테스트 +func (s *CspIdpConfigService) testOidcConnection(ctx context.Context, idpConfig *model.CspIdpConfig, account *model.CspAccount) error { + switch account.CspType { + case "aws": + return s.testAwsOidcConnection(ctx, idpConfig, account) + case "gcp": + // TODO: GCP Workload Identity Federation 테스트 + return fmt.Errorf("GCP OIDC connection test not implemented yet") + case "azure": + // TODO: Azure AD Workload Identity 테스트 + return fmt.Errorf("Azure OIDC connection test not implemented yet") + default: + return fmt.Errorf("unsupported CSP type: %s", account.CspType) + } +} + +// testAwsOidcConnection AWS OIDC 연결 테스트 +func (s *CspIdpConfigService) testAwsOidcConnection(ctx context.Context, idpConfig *model.CspIdpConfig, account *model.CspAccount) error { + // Keycloak에서 OIDC 토큰 획득 + token, err := s.keycloakService.GetClientCredentialsToken(ctx) + if err != nil { + return fmt.Errorf("failed to get Keycloak token: %w", err) + } + + // AWS STS 클라이언트 생성 + region := account.GetRegion() + if region == "" { + region = "ap-northeast-2" + } + + cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region)) + if err != nil { + return fmt.Errorf("failed to load AWS config: %w", err) + } + + stsClient := sts.NewFromConfig(cfg) + + // Role ARN 구성 (환경 변수 또는 IDP Config에서) + roleArn := idpConfig.Config["role_arn"] + if roleArn == "" { + roleArn = os.Getenv("IDENTITY_ROLE_ARN_AWS") + } + if roleArn == "" { + return fmt.Errorf("role_arn is not configured") + } + + // AssumeRoleWithWebIdentity 테스트 + input := &sts.AssumeRoleWithWebIdentityInput{ + RoleArn: aws.String(roleArn), + RoleSessionName: aws.String("mciam-connection-test"), + WebIdentityToken: aws.String(token.AccessToken), + DurationSeconds: aws.Int32(900), // 최소 15분 + } + + result, err := stsClient.AssumeRoleWithWebIdentity(ctx, input) + if err != nil { + return fmt.Errorf("AWS OIDC connection test failed: %w", err) + } + + log.Printf("AWS OIDC connection test successful. AssumedRoleUser: %s", *result.AssumedRoleUser.Arn) + return nil +} + +// testSamlConnection SAML 연결 테스트 +func (s *CspIdpConfigService) testSamlConnection(ctx context.Context, idpConfig *model.CspIdpConfig, account *model.CspAccount) error { + // TODO: SAML 연결 테스트 구현 + return fmt.Errorf("SAML connection test not implemented yet") +} + +// testSecretKeyConnection Secret Key 연결 테스트 +func (s *CspIdpConfigService) testSecretKeyConnection(ctx context.Context, idpConfig *model.CspIdpConfig, account *model.CspAccount) error { + switch account.CspType { + case "aws": + return s.testAwsSecretKeyConnection(ctx, idpConfig, account) + case "gcp": + // TODO: GCP 서비스 계정 키 테스트 + return fmt.Errorf("GCP Secret Key connection test not implemented yet") + case "azure": + // TODO: Azure 서비스 프린시펄 테스트 + return fmt.Errorf("Azure Secret Key connection test not implemented yet") + default: + return fmt.Errorf("unsupported CSP type: %s", account.CspType) + } +} + +// testAwsSecretKeyConnection AWS Secret Key 연결 테스트 +func (s *CspIdpConfigService) testAwsSecretKeyConnection(ctx context.Context, idpConfig *model.CspIdpConfig, account *model.CspAccount) error { + accessKeyID := idpConfig.GetAccessKeyID() + secretAccessKey := idpConfig.GetSecretAccessKey() + + if accessKeyID == "" || secretAccessKey == "" { + return fmt.Errorf("access_key_id or secret_access_key is not configured") + } + + // 암호화된 경우 복호화 필요 + if idpConfig.IsEncrypted() { + // TODO: 복호화 로직 구현 + return fmt.Errorf("encrypted secret key decryption not implemented yet") + } + + // AWS 설정 생성 + region := account.GetRegion() + if region == "" { + region = "ap-northeast-2" + } + + cfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(region), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( + accessKeyID, + secretAccessKey, + "", + )), + ) + if err != nil { + return fmt.Errorf("failed to load AWS config: %w", err) + } + + // STS GetCallerIdentity로 자격 증명 테스트 + stsClient := sts.NewFromConfig(cfg) + result, err := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{}) + if err != nil { + return fmt.Errorf("AWS Secret Key connection test failed: %w", err) + } + + log.Printf("AWS Secret Key connection test successful. Account: %s, Arn: %s", *result.Account, *result.Arn) + return nil +} + +// GetActiveIdpConfigsByAccountID 특정 계정의 활성 IDP 설정 목록 조회 +func (s *CspIdpConfigService) GetActiveIdpConfigsByAccountID(accountID uint) ([]*model.CspIdpConfig, error) { + configs, err := s.cspIdpConfigRepo.GetActiveByAccountID(accountID) + if err != nil { + return nil, fmt.Errorf("failed to get active IDP configs: %w", err) + } + return configs, nil +} + +// GetIdpConfigsByAuthMethod 인증 방식별 IDP 설정 목록 조회 +func (s *CspIdpConfigService) GetIdpConfigsByAuthMethod(authMethod model.AuthMethodType) ([]*model.CspIdpConfig, error) { + configs, err := s.cspIdpConfigRepo.GetByAuthMethod(authMethod) + if err != nil { + return nil, fmt.Errorf("failed to get IDP configs by auth method: %w", err) + } + return configs, nil +} + +// ActivateIdpConfig IDP 설정 활성화 +func (s *CspIdpConfigService) ActivateIdpConfig(id uint) error { + idpConfig, err := s.cspIdpConfigRepo.GetByID(id) + if err != nil { + return fmt.Errorf("failed to get IDP config: %w", err) + } + if idpConfig == nil { + return fmt.Errorf("IDP config not found with ID: %d", id) + } + + idpConfig.IsActive = true + if err := s.cspIdpConfigRepo.Update(idpConfig); err != nil { + return fmt.Errorf("failed to activate IDP config: %w", err) + } + + log.Printf("Activated CSP IDP config: %s (ID: %d)", idpConfig.Name, idpConfig.ID) + return nil +} + +// DeactivateIdpConfig IDP 설정 비활성화 +func (s *CspIdpConfigService) DeactivateIdpConfig(id uint) error { + idpConfig, err := s.cspIdpConfigRepo.GetByID(id) + if err != nil { + return fmt.Errorf("failed to get IDP config: %w", err) + } + if idpConfig == nil { + return fmt.Errorf("IDP config not found with ID: %d", id) + } + + idpConfig.IsActive = false + if err := s.cspIdpConfigRepo.Update(idpConfig); err != nil { + return fmt.Errorf("failed to deactivate IDP config: %w", err) + } + + log.Printf("Deactivated CSP IDP config: %s (ID: %d)", idpConfig.Name, idpConfig.ID) + return nil +} + +// AssumeRoleWithIdpConfig IDP 설정을 사용하여 임시 자격 증명 획득 +func (s *CspIdpConfigService) AssumeRoleWithIdpConfig(ctx context.Context, idpConfigID uint, roleArn string, sessionName string, durationSeconds int32) (*model.TempCredential, error) { + idpConfig, err := s.cspIdpConfigRepo.GetByID(idpConfigID) + if err != nil { + return nil, fmt.Errorf("failed to get IDP config: %w", err) + } + if idpConfig == nil { + return nil, fmt.Errorf("IDP config not found with ID: %d", idpConfigID) + } + + if !idpConfig.IsActive { + return nil, fmt.Errorf("IDP config is not active") + } + + account, err := s.cspAccountRepo.GetByID(idpConfig.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + + if account.CspType != "aws" { + return nil, fmt.Errorf("only AWS is supported for AssumeRole currently") + } + + region := account.GetRegion() + if region == "" { + region = "ap-northeast-2" + } + + switch idpConfig.AuthMethod { + case model.AuthMethodOIDC: + return s.assumeRoleWithOidc(ctx, idpConfig, roleArn, sessionName, durationSeconds, region) + case model.AuthMethodSecretKey: + return s.assumeRoleWithSecretKey(ctx, idpConfig, roleArn, sessionName, durationSeconds, region) + default: + return nil, fmt.Errorf("unsupported auth method for AssumeRole: %s", idpConfig.AuthMethod) + } +} + +// assumeRoleWithOidc OIDC를 사용하여 역할 인수 +func (s *CspIdpConfigService) assumeRoleWithOidc(ctx context.Context, idpConfig *model.CspIdpConfig, roleArn string, sessionName string, durationSeconds int32, region string) (*model.TempCredential, error) { + // Keycloak에서 OIDC 토큰 획득 + token, err := s.keycloakService.GetClientCredentialsToken(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get Keycloak token: %w", err) + } + + cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region)) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + + stsClient := sts.NewFromConfig(cfg) + + if durationSeconds < 900 { + durationSeconds = 3600 // 기본 1시간 + } + + input := &sts.AssumeRoleWithWebIdentityInput{ + RoleArn: aws.String(roleArn), + RoleSessionName: aws.String(sessionName), + WebIdentityToken: aws.String(token.AccessToken), + DurationSeconds: aws.Int32(durationSeconds), + } + + result, err := stsClient.AssumeRoleWithWebIdentity(ctx, input) + if err != nil { + return nil, fmt.Errorf("failed to assume role with OIDC: %w", err) + } + + return &model.TempCredential{ + Provider: "aws", + AuthType: "oidc", + AccessKeyId: *result.Credentials.AccessKeyId, + SecretAccessKey: *result.Credentials.SecretAccessKey, + SessionToken: *result.Credentials.SessionToken, + Region: region, + IssuedAt: time.Now(), + ExpiresAt: *result.Credentials.Expiration, + IsActive: true, + }, nil +} + +// assumeRoleWithSecretKey Secret Key를 사용하여 역할 인수 +func (s *CspIdpConfigService) assumeRoleWithSecretKey(ctx context.Context, idpConfig *model.CspIdpConfig, roleArn string, sessionName string, durationSeconds int32, region string) (*model.TempCredential, error) { + accessKeyID := idpConfig.GetAccessKeyID() + secretAccessKey := idpConfig.GetSecretAccessKey() + + if accessKeyID == "" || secretAccessKey == "" { + return nil, fmt.Errorf("access_key_id or secret_access_key is not configured") + } + + cfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(region), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( + accessKeyID, + secretAccessKey, + "", + )), + ) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + + stsClient := sts.NewFromConfig(cfg) + + if durationSeconds < 900 { + durationSeconds = 3600 // 기본 1시간 + } + + input := &sts.AssumeRoleInput{ + RoleArn: aws.String(roleArn), + RoleSessionName: aws.String(sessionName), + DurationSeconds: aws.Int32(durationSeconds), + } + + result, err := stsClient.AssumeRole(ctx, input) + if err != nil { + return nil, fmt.Errorf("failed to assume role with secret key: %w", err) + } + + return &model.TempCredential{ + Provider: "aws", + AuthType: "secret_key", + AccessKeyId: *result.Credentials.AccessKeyId, + SecretAccessKey: *result.Credentials.SecretAccessKey, + SessionToken: *result.Credentials.SessionToken, + Region: region, + IssuedAt: time.Now(), + ExpiresAt: *result.Credentials.Expiration, + IsActive: true, + }, nil +} diff --git a/src/service/csp_policy_service.go b/src/service/csp_policy_service.go new file mode 100644 index 00000000..038265a1 --- /dev/null +++ b/src/service/csp_policy_service.go @@ -0,0 +1,470 @@ +package service + +import ( + "context" + "fmt" + "log" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/iam" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/m-cmp/mc-iam-manager/model" + "github.com/m-cmp/mc-iam-manager/repository" + "gorm.io/gorm" +) + +// CspPolicyService CSP 정책 서비스 +type CspPolicyService struct { + db *gorm.DB + cspPolicyRepo *repository.CspPolicyRepository + cspAccountRepo *repository.CspAccountRepository + cspRoleRepo *repository.CspRoleRepository + cspIdpConfigService *CspIdpConfigService +} + +// NewCspPolicyService 새 CspPolicyService 인스턴스 생성 +func NewCspPolicyService(db *gorm.DB, cspIdpConfigService *CspIdpConfigService) *CspPolicyService { + return &CspPolicyService{ + db: db, + cspPolicyRepo: repository.NewCspPolicyRepository(db), + cspAccountRepo: repository.NewCspAccountRepository(db), + cspRoleRepo: repository.NewCspRoleRepository(db), + cspIdpConfigService: cspIdpConfigService, + } +} + +// CreateCspPolicy CSP 정책 생성 +func (s *CspPolicyService) CreateCspPolicy(req *model.CreateCspPolicyRequest) (*model.CspPolicy, error) { + // CSP 계정 존재 확인 + account, err := s.cspAccountRepo.GetByID(req.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return nil, fmt.Errorf("CSP account not found with ID: %d", req.CspAccountID) + } + + // 이름 중복 확인 + exists, err := s.cspPolicyRepo.ExistsByNameAndAccountID(req.Name, req.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to check policy existence: %w", err) + } + if exists { + return nil, fmt.Errorf("policy with name '%s' already exists for this account", req.Name) + } + + // 정책 생성 + policy := &model.CspPolicy{ + Name: req.Name, + CspAccountID: req.CspAccountID, + PolicyType: req.PolicyType, + PolicyArn: req.PolicyArn, + PolicyDoc: req.PolicyDoc, + Description: req.Description, + } + + if err := s.cspPolicyRepo.Create(policy); err != nil { + return nil, fmt.Errorf("failed to create policy: %w", err) + } + + log.Printf("Created CSP policy: %s (type: %s)", policy.Name, policy.PolicyType) + return policy, nil +} + +// GetCspPolicyByID ID로 CSP 정책 조회 +func (s *CspPolicyService) GetCspPolicyByID(id uint) (*model.CspPolicy, error) { + policy, err := s.cspPolicyRepo.GetByID(id) + if err != nil { + return nil, fmt.Errorf("failed to get policy: %w", err) + } + if policy == nil { + return nil, fmt.Errorf("policy not found with ID: %d", id) + } + return policy, nil +} + +// ListCspPolicies CSP 정책 목록 조회 +func (s *CspPolicyService) ListCspPolicies(filter *model.CspPolicyFilter) ([]*model.CspPolicy, error) { + policies, err := s.cspPolicyRepo.List(filter) + if err != nil { + return nil, fmt.Errorf("failed to list policies: %w", err) + } + return policies, nil +} + +// UpdateCspPolicy CSP 정책 수정 +func (s *CspPolicyService) UpdateCspPolicy(id uint, req *model.UpdateCspPolicyRequest) (*model.CspPolicy, error) { + // 기존 정책 조회 + policy, err := s.cspPolicyRepo.GetByID(id) + if err != nil { + return nil, fmt.Errorf("failed to get policy: %w", err) + } + if policy == nil { + return nil, fmt.Errorf("policy not found with ID: %d", id) + } + + // 필드 업데이트 + if req.Name != "" { + // 이름 변경 시 중복 확인 + if req.Name != policy.Name { + exists, err := s.cspPolicyRepo.ExistsByNameAndAccountID(req.Name, policy.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to check policy existence: %w", err) + } + if exists { + return nil, fmt.Errorf("policy with name '%s' already exists", req.Name) + } + } + policy.Name = req.Name + } + if req.PolicyArn != "" { + policy.PolicyArn = req.PolicyArn + } + if req.PolicyDoc != nil { + policy.PolicyDoc = req.PolicyDoc + } + if req.Description != "" { + policy.Description = req.Description + } + + if err := s.cspPolicyRepo.Update(policy); err != nil { + return nil, fmt.Errorf("failed to update policy: %w", err) + } + + log.Printf("Updated CSP policy: %s (ID: %d)", policy.Name, policy.ID) + return policy, nil +} + +// DeleteCspPolicy CSP 정책 삭제 +func (s *CspPolicyService) DeleteCspPolicy(id uint) error { + // 정책 존재 확인 + exists, err := s.cspPolicyRepo.ExistsByID(id) + if err != nil { + return fmt.Errorf("failed to check policy existence: %w", err) + } + if !exists { + return fmt.Errorf("policy not found with ID: %d", id) + } + + // 연결된 역할 확인 + roles, err := s.cspPolicyRepo.GetRolesByPolicyID(id) + if err != nil { + return fmt.Errorf("failed to get roles by policy: %w", err) + } + if len(roles) > 0 { + return fmt.Errorf("cannot delete policy: %d roles are attached", len(roles)) + } + + if err := s.cspPolicyRepo.Delete(id); err != nil { + return fmt.Errorf("failed to delete policy: %w", err) + } + + log.Printf("Deleted CSP policy with ID: %d", id) + return nil +} + +// AttachPolicyToRole 역할에 정책 연결 +func (s *CspPolicyService) AttachPolicyToRole(roleID, policyID uint) error { + // 역할 존재 확인 + roleExists, err := s.cspRoleRepo.ExistsCspRoleByID(roleID) + if err != nil { + return fmt.Errorf("failed to check role existence: %w", err) + } + if !roleExists { + return fmt.Errorf("CSP role not found with ID: %d", roleID) + } + + // 정책 존재 확인 + policyExists, err := s.cspPolicyRepo.ExistsByID(policyID) + if err != nil { + return fmt.Errorf("failed to check policy existence: %w", err) + } + if !policyExists { + return fmt.Errorf("CSP policy not found with ID: %d", policyID) + } + + // 이미 연결되어 있는지 확인 + attached, err := s.cspPolicyRepo.IsPolicyAttachedToRole(roleID, policyID) + if err != nil { + return fmt.Errorf("failed to check policy attachment: %w", err) + } + if attached { + return fmt.Errorf("policy is already attached to the role") + } + + if err := s.cspPolicyRepo.AttachPolicyToRole(roleID, policyID); err != nil { + return fmt.Errorf("failed to attach policy to role: %w", err) + } + + log.Printf("Attached policy %d to role %d", policyID, roleID) + return nil +} + +// DetachPolicyFromRole 역할에서 정책 분리 +func (s *CspPolicyService) DetachPolicyFromRole(roleID, policyID uint) error { + // 연결 여부 확인 + attached, err := s.cspPolicyRepo.IsPolicyAttachedToRole(roleID, policyID) + if err != nil { + return fmt.Errorf("failed to check policy attachment: %w", err) + } + if !attached { + return fmt.Errorf("policy is not attached to the role") + } + + if err := s.cspPolicyRepo.DetachPolicyFromRole(roleID, policyID); err != nil { + return fmt.Errorf("failed to detach policy from role: %w", err) + } + + log.Printf("Detached policy %d from role %d", policyID, roleID) + return nil +} + +// GetPoliciesByRoleID 역할에 연결된 정책 목록 조회 +func (s *CspPolicyService) GetPoliciesByRoleID(roleID uint) ([]*model.CspPolicy, error) { + policies, err := s.cspPolicyRepo.GetPoliciesByRoleID(roleID) + if err != nil { + return nil, fmt.Errorf("failed to get policies by role: %w", err) + } + return policies, nil +} + +// SyncPoliciesFromCloud CSP에서 정책 동기화 +func (s *CspPolicyService) SyncPoliciesFromCloud(ctx context.Context, req *model.SyncPoliciesRequest) ([]*model.CspPolicy, error) { + // CSP 계정 조회 + account, err := s.cspAccountRepo.GetByID(req.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + if account == nil { + return nil, fmt.Errorf("CSP account not found with ID: %d", req.CspAccountID) + } + + switch account.CspType { + case "aws": + return s.syncAwsPolicies(ctx, account, req.PolicyScope) + case "gcp": + return nil, fmt.Errorf("GCP policy sync not implemented yet") + case "azure": + return nil, fmt.Errorf("Azure policy sync not implemented yet") + default: + return nil, fmt.Errorf("unsupported CSP type: %s", account.CspType) + } +} + +// syncAwsPolicies AWS에서 정책 동기화 +func (s *CspPolicyService) syncAwsPolicies(ctx context.Context, account *model.CspAccount, scope string) ([]*model.CspPolicy, error) { + // IDP 설정을 통해 임시 자격 증명 획득 + idpConfigs, err := s.cspIdpConfigService.GetActiveIdpConfigsByAccountID(account.ID) + if err != nil { + return nil, fmt.Errorf("failed to get IDP configs: %w", err) + } + if len(idpConfigs) == 0 { + return nil, fmt.Errorf("no active IDP config found for account") + } + + // 첫 번째 활성 IDP 설정 사용 + idpConfig := idpConfigs[0] + + // 임시 자격 증명 획득 + tempCred, err := s.cspIdpConfigService.AssumeRoleWithIdpConfig(ctx, idpConfig.ID, + idpConfig.Config["role_arn"], + "mciam-policy-sync", + 3600) + if err != nil { + return nil, fmt.Errorf("failed to assume role: %w", err) + } + + // AWS IAM 클라이언트 생성 + cfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(tempCred.Region), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( + tempCred.AccessKeyId, + tempCred.SecretAccessKey, + tempCred.SessionToken, + )), + ) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + + iamClient := iam.NewFromConfig(cfg) + + // 정책 목록 조회 + var policyScope iamtypes.PolicyScopeType + switch scope { + case "All": + policyScope = iamtypes.PolicyScopeTypeAll + case "AWS": + policyScope = iamtypes.PolicyScopeTypeAws + case "Local": + policyScope = iamtypes.PolicyScopeTypeLocal + default: + policyScope = iamtypes.PolicyScopeTypeLocal + } + + input := &iam.ListPoliciesInput{ + Scope: policyScope, + } + + var syncedPolicies []*model.CspPolicy + paginator := iam.NewListPoliciesPaginator(iamClient, input) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list AWS policies: %w", err) + } + + for _, awsPolicy := range page.Policies { + // 기존 정책 확인 + existingPolicy, _ := s.cspPolicyRepo.GetByArn(*awsPolicy.Arn) + if existingPolicy != nil { + // 업데이트 + existingPolicy.Name = *awsPolicy.PolicyName + if awsPolicy.Description != nil { + existingPolicy.Description = *awsPolicy.Description + } + if err := s.cspPolicyRepo.Update(existingPolicy); err != nil { + log.Printf("Failed to update policy %s: %v", *awsPolicy.PolicyName, err) + continue + } + syncedPolicies = append(syncedPolicies, existingPolicy) + } else { + // 새로 생성 + newPolicy := &model.CspPolicy{ + Name: *awsPolicy.PolicyName, + CspAccountID: account.ID, + PolicyType: model.PolicyTypeManaged, + PolicyArn: *awsPolicy.Arn, + } + if awsPolicy.Description != nil { + newPolicy.Description = *awsPolicy.Description + } + if err := s.cspPolicyRepo.Create(newPolicy); err != nil { + log.Printf("Failed to create policy %s: %v", *awsPolicy.PolicyName, err) + continue + } + syncedPolicies = append(syncedPolicies, newPolicy) + } + } + } + + log.Printf("Synced %d policies from AWS", len(syncedPolicies)) + return syncedPolicies, nil +} + +// GetPolicyDocument CSP에서 정책 문서 조회 +func (s *CspPolicyService) GetPolicyDocument(ctx context.Context, policyID uint) (map[string]interface{}, error) { + policy, err := s.cspPolicyRepo.GetByID(policyID) + if err != nil { + return nil, fmt.Errorf("failed to get policy: %w", err) + } + if policy == nil { + return nil, fmt.Errorf("policy not found with ID: %d", policyID) + } + + // 이미 PolicyDoc가 있으면 반환 + if policy.PolicyDoc != nil { + return policy.PolicyDoc, nil + } + + // 관리형 정책이고 ARN이 있으면 CSP에서 조회 + if policy.PolicyType == model.PolicyTypeManaged && policy.PolicyArn != "" { + account, err := s.cspAccountRepo.GetByID(policy.CspAccountID) + if err != nil { + return nil, fmt.Errorf("failed to get CSP account: %w", err) + } + + if account.CspType == "aws" { + return s.getAwsPolicyDocument(ctx, account, policy.PolicyArn) + } + } + + return nil, fmt.Errorf("policy document not available") +} + +// getAwsPolicyDocument AWS에서 정책 문서 조회 +func (s *CspPolicyService) getAwsPolicyDocument(ctx context.Context, account *model.CspAccount, policyArn string) (map[string]interface{}, error) { + // IDP 설정을 통해 임시 자격 증명 획득 + idpConfigs, err := s.cspIdpConfigService.GetActiveIdpConfigsByAccountID(account.ID) + if err != nil { + return nil, fmt.Errorf("failed to get IDP configs: %w", err) + } + if len(idpConfigs) == 0 { + return nil, fmt.Errorf("no active IDP config found for account") + } + + idpConfig := idpConfigs[0] + tempCred, err := s.cspIdpConfigService.AssumeRoleWithIdpConfig(ctx, idpConfig.ID, + idpConfig.Config["role_arn"], + "mciam-policy-get", + 900) + if err != nil { + return nil, fmt.Errorf("failed to assume role: %w", err) + } + + // AWS IAM 클라이언트 생성 + cfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(tempCred.Region), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( + tempCred.AccessKeyId, + tempCred.SecretAccessKey, + tempCred.SessionToken, + )), + ) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + + iamClient := iam.NewFromConfig(cfg) + + // 정책 정보 조회 + getPolicyInput := &iam.GetPolicyInput{ + PolicyArn: aws.String(policyArn), + } + policyResult, err := iamClient.GetPolicy(ctx, getPolicyInput) + if err != nil { + return nil, fmt.Errorf("failed to get policy: %w", err) + } + + // 정책 버전 문서 조회 + getPolicyVersionInput := &iam.GetPolicyVersionInput{ + PolicyArn: aws.String(policyArn), + VersionId: policyResult.Policy.DefaultVersionId, + } + versionResult, err := iamClient.GetPolicyVersion(ctx, getPolicyVersionInput) + if err != nil { + return nil, fmt.Errorf("failed to get policy version: %w", err) + } + + // URL 디코딩 및 JSON 파싱 + if versionResult.PolicyVersion.Document != nil { + // AWS는 URL 인코딩된 JSON을 반환 + // 여기서는 간단히 string으로 반환 (실제로는 파싱 필요) + return map[string]interface{}{ + "document": *versionResult.PolicyVersion.Document, + }, nil + } + + return nil, fmt.Errorf("policy document not found") +} + +// GetManagedPoliciesByAccountID 특정 계정의 관리형 정책 목록 조회 +func (s *CspPolicyService) GetManagedPoliciesByAccountID(accountID uint) ([]*model.CspPolicy, error) { + policies, err := s.cspPolicyRepo.GetManagedPoliciesByAccountID(accountID) + if err != nil { + return nil, fmt.Errorf("failed to get managed policies: %w", err) + } + return policies, nil +} + +// GetPoliciesByAccountID 특정 계정의 모든 정책 목록 조회 +func (s *CspPolicyService) GetPoliciesByAccountID(accountID uint) ([]*model.CspPolicy, error) { + policies, err := s.cspPolicyRepo.GetByAccountID(accountID) + if err != nil { + return nil, fmt.Errorf("failed to get policies by account: %w", err) + } + return policies, nil +} diff --git a/src/service/mcmpapi_service.go b/src/service/mcmpapi_service.go index 427333e8..07101a24 100644 --- a/src/service/mcmpapi_service.go +++ b/src/service/mcmpapi_service.go @@ -1,31 +1,31 @@ package service import ( - "bytes" // For request body + "bytes" "context" - "encoding/base64" // For Basic Auth encoding + "encoding/base64" "encoding/json" "errors" "fmt" "io" "log" "net/http" - "net/http/httputil" // For dumping request - "net/url" // For query parameter mapping + "net/http/httputil" + "net/url" "os" - "path/filepath" // Ensure filepath is imported - "strings" // Ensure strings is imported - - // "encoding/json" // Removed unused import + "strings" + "time" "github.com/m-cmp/mc-iam-manager/model" - "github.com/m-cmp/mc-iam-manager/model/mcmpapi" // Updated import path + "github.com/m-cmp/mc-iam-manager/model/mcmpapi" + "github.com/m-cmp/mc-iam-manager/pkg/apiparser" "github.com/m-cmp/mc-iam-manager/repository" "gopkg.in/yaml.v3" - "gorm.io/gorm" // Needed if passing db directly, but better via repo + "gorm.io/gorm" ) -const apiYamlEnvVar = "MCADMINCLI_APIYAML" // Re-add constant definition +// Local path to service-actions.yaml file (relative to working directory) +const localServiceActionsPath = "asset/mcmpapi/service-actions.yaml" // McmpApiService defines the interface for mcmp API operations type McmpApiService interface { @@ -38,6 +38,7 @@ type McmpApiService interface { UpdateService(serviceName string, updates map[string]interface{}) error McmpApiCall(ctx context.Context, req *model.McmpApiCallRequest) (int, []byte, string, string, error) SyncMcmpAPIsFromYAML() error + ImportAPIs(req *model.ImportApiRequest) (*model.ImportApiResponse, error) } // mcmpApiService implements the McmpApiService interface. @@ -79,112 +80,50 @@ func (s *mcmpApiService) CreateAction(tx *gorm.DB, action *mcmpapi.McmpApiAction return s.repo.CreateAction(tx, action) } -// SyncMcmpAPIsFromYAML loads API definitions from the YAML URL specified by env var -// and saves them to the database via the repository. +// SyncMcmpAPIsFromYAML loads API definitions from local service-actions.yaml file +// and saves them to the database via the repository with upsert logic. func (s *mcmpApiService) SyncMcmpAPIsFromYAML() error { - // 테이블이 없으면 생성 - var count int64 - if err := s.db.Table("mcmp_api_services").Count(&count).Error; err != nil { - // 테이블이 없으면 생성 - if err := s.db.AutoMigrate(&mcmpapi.McmpApiService{}, &mcmpapi.McmpApiAction{}); err != nil { - return fmt.Errorf("failed to create mcmp API tables: %w", err) - } - log.Printf("Created mcmp API tables") - } - - yamlSource := os.Getenv(apiYamlEnvVar) - if yamlSource == "" { - err := fmt.Errorf("environment variable %s is not set", apiYamlEnvVar) - log.Printf("Error syncing mcmp APIs: %v", err) - return err + // Ensure tables exist + if err := s.ensureTables(); err != nil { + return fmt.Errorf("failed to ensure tables: %w", err) } - localYamlPath := filepath.Join("asset", "mcmpapi", "mcmp_api.yaml") - - // Check if yamlSource is a URL - if strings.HasPrefix(yamlSource, "http://") || strings.HasPrefix(yamlSource, "https://") { - log.Printf("Starting mcmp API sync: Downloading from URL %s to %s", yamlSource, localYamlPath) - - // Ensure directory exists - if err := os.MkdirAll(filepath.Dir(localYamlPath), 0755); err != nil { - err = fmt.Errorf("failed to create directory for local YAML file %s: %w", localYamlPath, err) - log.Printf("Error syncing mcmp APIs: %v", err) - return err - } - - // Download the file - resp, err := http.Get(yamlSource) - if err != nil { - err = fmt.Errorf("failed to fetch mcmp API YAML from %s: %w", yamlSource, err) - log.Printf("Error syncing mcmp APIs: %v", err) - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("failed to fetch mcmp API YAML: status code %d", resp.StatusCode) - log.Printf("Error syncing mcmp APIs: %v", err) - return err - } - - // Create the local file - outFile, err := os.Create(localYamlPath) - if err != nil { - err = fmt.Errorf("failed to create local YAML file %s: %w", localYamlPath, err) - log.Printf("Error syncing mcmp APIs: %v", err) - return err - } - defer outFile.Close() - - // Write the body to the file - _, err = io.Copy(outFile, resp.Body) - if err != nil { - err = fmt.Errorf("failed to write downloaded content to %s: %w", localYamlPath, err) - log.Printf("Error syncing mcmp APIs: %v", err) - return err - } - log.Printf("Successfully downloaded YAML to %s", localYamlPath) - } else { - // Assume yamlSource is a local file path relative to project root - log.Printf("Starting mcmp API sync: Using local file path %s", yamlSource) - localYamlPath = yamlSource - } - - // Read the local YAML file - log.Printf("Reading mcmp API definitions from %s", localYamlPath) - yamlData, err := os.ReadFile(localYamlPath) + // Read from local file only (no URL download) + log.Printf("Reading MCMP API definitions from local file: %s", localServiceActionsPath) + yamlData, err := os.ReadFile(localServiceActionsPath) if err != nil { - err = fmt.Errorf("failed to read local mcmp API YAML file %s: %w", localYamlPath, err) - log.Printf("Error syncing mcmp APIs: %v", err) - return err + return fmt.Errorf("failed to read service-actions.yaml from %s: %w", localServiceActionsPath, err) } - var defs mcmpapi.McmpApiDefinitions - err = yaml.Unmarshal(yamlData, &defs) - if err != nil { - err = fmt.Errorf("failed to unmarshal mcmp API YAML: %w", err) - log.Printf("Error syncing mcmp APIs: %v", err) - return err + // Parse the YAML structure with serviceActions containing _meta + var rawData map[string]interface{} + if err := yaml.Unmarshal(yamlData, &rawData); err != nil { + return fmt.Errorf("failed to unmarshal YAML: %w", err) } - // Save to Database - err = s.saveDefinitionsToDB(&defs) - if err != nil { - log.Printf("Error saving mcmp API definitions to database: %v", err) - return fmt.Errorf("failed to save mcmp API definitions to DB: %w", err) + serviceActionsRaw, ok := rawData["serviceActions"].(map[string]interface{}) + if !ok { + return fmt.Errorf("invalid YAML format: missing or invalid serviceActions") } - log.Println("Successfully synced mcmp API definitions to database.") - return nil + return s.syncServicesAndActions(serviceActionsRaw) } -// saveDefinitionsToDB handles the transaction and logic for saving definitions. -func (s *mcmpApiService) saveDefinitionsToDB(defs *mcmpapi.McmpApiDefinitions) error { - if defs == nil || len(defs.Services) == 0 { - log.Println("No service definitions provided to save.") - return nil // Nothing to save +// ensureTables creates MCMP API tables if they don't exist +func (s *mcmpApiService) ensureTables() error { + var count int64 + if err := s.db.Table("mcmp_api_services").Count(&count).Error; err != nil { + // Table doesn't exist, create it + if err := s.db.AutoMigrate(&mcmpapi.McmpApiService{}, &mcmpapi.McmpApiAction{}, &mcmpapi.McmpApiServiceMeta{}); err != nil { + return fmt.Errorf("failed to create mcmp API tables: %w", err) + } + log.Printf("Created mcmp API tables") } + return nil +} +// syncServicesAndActions processes service actions from parsed YAML and syncs to database +func (s *mcmpApiService) syncServicesAndActions(serviceActionsRaw map[string]interface{}) error { tx := s.db.Begin() if tx.Error != nil { return fmt.Errorf("failed to begin transaction: %w", tx.Error) @@ -192,74 +131,150 @@ func (s *mcmpApiService) saveDefinitionsToDB(defs *mcmpapi.McmpApiDefinitions) e defer func() { if r := recover(); r != nil { tx.Rollback() - panic(r) // Re-panic after rollback + panic(r) } }() - for name, serviceDef := range defs.Services { - // Check if service with the same name and version already exists (using non-transactional DB read is okay here) - _, err := s.repo.GetServiceByNameAndVersion(name, serviceDef.Version) - - if errors.Is(err, gorm.ErrRecordNotFound) { - // Service with this name and version does not exist, create it within the transaction - log.Printf("Adding new service definition: %s (Version: %s)", name, serviceDef.Version) - dbService := mcmpapi.McmpApiService{ - Name: name, - Version: serviceDef.Version, - BaseURL: serviceDef.BaseURL, - AuthType: serviceDef.Auth.Type, - AuthUser: serviceDef.Auth.Username, - AuthPass: serviceDef.Auth.Password, // Consider encryption - // IsActive defaults to false or needs explicit handling if required + for serviceName, actionsRaw := range serviceActionsRaw { + actionsMap, ok := actionsRaw.(map[string]interface{}) + if !ok { + log.Printf("Warning: invalid actions format for service %s, skipping", serviceName) + continue + } + + // Extract _meta + meta, err := s.extractMeta(serviceName, actionsMap) + if err != nil { + log.Printf("Warning: failed to extract meta for %s: %v", serviceName, err) + } + + // Check if update is needed + shouldUpdate, err := s.shouldUpdateService(serviceName, meta) + if err != nil { + log.Printf("Error checking service %s: %v", serviceName, err) + } + + if shouldUpdate { + // Upsert service meta + if meta != nil { + if err := s.repo.UpsertServiceMeta(tx, meta); err != nil { + tx.Rollback() + return fmt.Errorf("failed to upsert meta for %s: %w", serviceName, err) + } } - if createErr := s.repo.CreateService(tx, &dbService); createErr != nil { + + // Delete existing actions for this service (full replace) + if err := s.repo.DeleteActionsByServiceName(tx, serviceName); err != nil { tx.Rollback() - return fmt.Errorf("error creating service %s (Version: %s): %w", name, serviceDef.Version, createErr) + return fmt.Errorf("failed to delete actions for %s: %w", serviceName, err) } - // Add Actions ONLY for the newly created service version - if actions, ok := defs.ServiceActions[name]; ok { - log.Printf("Adding actions for new service: %s (Version: %s)", name, serviceDef.Version) - for actionName, actionDef := range actions { - dbAction := mcmpapi.McmpApiAction{ - ServiceName: name, // Link to the service name - ActionName: actionName, - Method: actionDef.Method, - ResourcePath: actionDef.ResourcePath, - Description: actionDef.Description, - // Version linking might be needed here if actions are version-specific - } - if createActionErr := s.repo.CreateAction(tx, &dbAction); createActionErr != nil { - tx.Rollback() - return fmt.Errorf("error creating action %s for service %s: %w", actionName, name, createActionErr) - } + // Create new actions + actionCount := 0 + for actionName, actionRaw := range actionsMap { + if actionName == "_meta" { + continue // Skip meta entry } + + actionDef, ok := actionRaw.(map[string]interface{}) + if !ok { + log.Printf("Warning: invalid action format for %s/%s, skipping", serviceName, actionName) + continue + } + + action := &mcmpapi.McmpApiAction{ + ServiceName: serviceName, + ActionName: actionName, + Method: s.getString(actionDef, "method"), + ResourcePath: s.getString(actionDef, "resourcePath"), + Description: s.getString(actionDef, "description"), + } + + if err := s.repo.CreateAction(tx, action); err != nil { + tx.Rollback() + return fmt.Errorf("failed to create action %s for %s: %w", actionName, serviceName, err) + } + actionCount++ } - } else if err != nil { - // Other DB error during check - tx.Rollback() - return fmt.Errorf("error checking existing service %s (Version: %s): %w", name, serviceDef.Version, err) + + version := "" + if meta != nil { + version = meta.Version + } + log.Printf("Updated service: %s (version: %s, actions: %d)", serviceName, version, actionCount) } else { - // Service with this name and version already exists, skip. - log.Printf("Skipping existing service definition: %s (Version: %s)", name, serviceDef.Version) + log.Printf("Skipping service %s - no changes detected", serviceName) } } if err := tx.Commit().Error; err != nil { - // Rollback might have already happened, but doesn't hurt to call again if needed - // tx.Rollback() return fmt.Errorf("failed to commit transaction: %w", err) } - return nil // Commit successful + log.Println("Successfully synced mcmp API definitions to database.") + return nil +} + +// extractMeta extracts _meta information from service actions map +func (s *mcmpApiService) extractMeta(serviceName string, actionsMap map[string]interface{}) (*mcmpapi.McmpApiServiceMeta, error) { + metaRaw, ok := actionsMap["_meta"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("no _meta found for service %s", serviceName) + } + + generatedAtStr := s.getString(metaRaw, "generatedAt") + var generatedAt time.Time + if generatedAtStr != "" { + parsed, err := time.Parse(time.RFC3339, generatedAtStr) + if err != nil { + log.Printf("Warning: failed to parse generatedAt for %s: %v", serviceName, err) + } else { + generatedAt = parsed + } + } + + return &mcmpapi.McmpApiServiceMeta{ + ServiceName: serviceName, + Version: s.getString(metaRaw, "version"), + Repository: s.getString(metaRaw, "repository"), + GeneratedAt: generatedAt, + }, nil } -// Implement other methods like GetMcmpService, GetMcmpAction if needed, -// likely by calling corresponding repository methods. -// Example: -// func (s *mcmpApiService) GetMcmpService(name string) (*mcmpapi.McmpApiService, error) { // Renamed -// return s.repo.GetService(name) // Assumes repo method exists -// } +// shouldUpdateService checks if a service needs to be updated based on version metadata +func (s *mcmpApiService) shouldUpdateService(serviceName string, newMeta *mcmpapi.McmpApiServiceMeta) (bool, error) { + if newMeta == nil { + return true, nil // No meta, always update + } + + existingMeta, err := s.repo.GetServiceMeta(serviceName) + if errors.Is(err, gorm.ErrRecordNotFound) { + return true, nil // New service + } + if err != nil { + return false, err + } + + // Compare version and generatedAt + if existingMeta.Version != newMeta.Version { + log.Printf("Service %s version changed: %s -> %s", serviceName, existingMeta.Version, newMeta.Version) + return true, nil + } + if !existingMeta.GeneratedAt.Equal(newMeta.GeneratedAt) { + log.Printf("Service %s generatedAt changed: %v -> %v", serviceName, existingMeta.GeneratedAt, newMeta.GeneratedAt) + return true, nil + } + + return false, nil // No changes +} + +// getString safely extracts a string value from a map +func (s *mcmpApiService) getString(m map[string]interface{}, key string) string { + if v, ok := m[key].(string); ok { + return v + } + return "" +} // SetActiveVersion sets the specified version of a service as active. func (s *mcmpApiService) SetActiveVersion(serviceName, version string) error { @@ -446,4 +461,106 @@ func (s *mcmpApiService) McmpApiCall(ctx context.Context, req *model.McmpApiCall return statusCode, respBody, serviceVersion, calledURL, err } -// Removed ServiceApiCall function implementation +// ImportAPIs fetches API specifications from remote URLs and imports them to the database +func (s *mcmpApiService) ImportAPIs(req *model.ImportApiRequest) (*model.ImportApiResponse, error) { + // Ensure tables exist + if err := s.ensureTables(); err != nil { + return nil, fmt.Errorf("failed to ensure tables: %w", err) + } + + processor := apiparser.NewProcessor(30) // 30 second timeout + + response := &model.ImportApiResponse{ + TotalFrameworks: len(req.Frameworks), + FrameworkResults: make([]model.ImportApiFrameworkResult, 0, len(req.Frameworks)), + } + + for _, fw := range req.Frameworks { + result := model.ImportApiFrameworkResult{ + Name: fw.Name, + Version: fw.Version, + } + + // Process the framework + fwResult := processor.ProcessFramework(fw.Name, fw.Version, fw.Repository, fw.SourceType, fw.SourceURL) + + if fwResult.Error != nil { + result.Success = false + result.ErrorMessage = fwResult.Error.Error() + response.FailureCount++ + log.Printf("Failed to import framework %s: %v", fw.Name, fwResult.Error) + } else { + // Sync to database + err := s.syncFrameworkToDatabase(fw.Name, fw.Version, fw.Repository, fwResult.Actions) + if err != nil { + result.Success = false + result.ErrorMessage = fmt.Sprintf("failed to save to database: %v", err) + response.FailureCount++ + log.Printf("Failed to save framework %s to database: %v", fw.Name, err) + } else { + result.Success = true + result.ActionCount = fwResult.ActionCount + response.SuccessCount++ + log.Printf("Successfully imported framework %s (version: %s, actions: %d)", fw.Name, fw.Version, fwResult.ActionCount) + } + } + + response.FrameworkResults = append(response.FrameworkResults, result) + } + + return response, nil +} + +// syncFrameworkToDatabase saves a single framework's actions to the database +func (s *mcmpApiService) syncFrameworkToDatabase(serviceName, version, repository string, actions map[string]apiparser.ServiceAction) error { + tx := s.db.Begin() + if tx.Error != nil { + return fmt.Errorf("failed to begin transaction: %w", tx.Error) + } + defer func() { + if r := recover(); r != nil { + tx.Rollback() + panic(r) + } + }() + + // Upsert service meta + meta := &mcmpapi.McmpApiServiceMeta{ + ServiceName: serviceName, + Version: version, + Repository: repository, + GeneratedAt: time.Now(), + } + if err := s.repo.UpsertServiceMeta(tx, meta); err != nil { + tx.Rollback() + return fmt.Errorf("failed to upsert meta: %w", err) + } + + // Delete existing actions for this service (full replace) + if err := s.repo.DeleteActionsByServiceName(tx, serviceName); err != nil { + tx.Rollback() + return fmt.Errorf("failed to delete existing actions: %w", err) + } + + // Create new actions + for actionName, actionDef := range actions { + action := &mcmpapi.McmpApiAction{ + ServiceName: serviceName, + ActionName: actionName, + Method: actionDef.Method, + ResourcePath: actionDef.ResourcePath, + Description: actionDef.Description, + } + + if err := s.repo.CreateAction(tx, action); err != nil { + tx.Rollback() + return fmt.Errorf("failed to create action %s: %w", actionName, err) + } + } + + if err := tx.Commit().Error; err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + + return nil +} From f9ec15e342d7febbcbae260903e51a2d0f1928c0 Mon Sep 17 00:00:00 2001 From: dogfootman Date: Mon, 12 Jan 2026 15:13:33 +0900 Subject: [PATCH 12/14] Regenerate swagger documentation --- src/docs/docs.go | 305 ++++++++++++++++++++++++++++++++---------- src/docs/swagger.json | 305 ++++++++++++++++++++++++++++++++---------- src/docs/swagger.yaml | 130 ++++++++++++++++-- 3 files changed, 586 insertions(+), 154 deletions(-) diff --git a/src/docs/docs.go b/src/docs/docs.go index fc7dea8c..3efe9df1 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -3719,7 +3719,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Create a new project with the specified information.", + "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", "consumes": [ "application/json" ], @@ -3738,7 +3738,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.CreateProjectRequest" } } ], @@ -3758,6 +3758,15 @@ const docTemplate = `{ } } }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -3770,6 +3779,138 @@ const docTemplate = `{ } } }, + "/api/projects/assign/workspaces": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "프로젝트에 워크스페이스를 연결합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: 잘못된 ID 형식", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/projects/id/{projectId}/workspaces": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve list of workspaces that the project is assigned to", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Get workspaces assigned to project", + "operationId": "getProjectWorkspaces", + "parameters": [ + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "400": { + "description": "error: Invalid project ID", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Project not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/list": { "post": { "security": [ @@ -3867,6 +4008,61 @@ const docTemplate = `{ } } }, + "/api/projects/unassign/workspaces": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a workspace from a project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Remove workspace from project", + "operationId": "removeWorkspaceFromProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: Invalid request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/{id}": { "get": { "security": [ @@ -4046,75 +4242,6 @@ const docTemplate = `{ } } }, - "/api/projects/{id}/workspaces/{workspaceId}": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "프로젝트에 워크스페이스를 연결합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "operationId": "addWorkspaceToProject", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/api/resource-types/cloud-resources": { "post": { "security": [ @@ -9274,6 +9401,24 @@ const docTemplate = `{ } } }, + "model.CreateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "workspaceId": { + "description": "optional workspace to assign project to", + "type": "string" + } + } + }, "model.CreateRoleRequest": { "type": "object", "required": [ @@ -10342,6 +10487,24 @@ const docTemplate = `{ } } }, + "model.WorkspaceProjectMappingRequest": { + "type": "object", + "required": [ + "projectIds", + "workspaceId" + ], + "properties": { + "projectIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "workspaceId": { + "type": "string" + } + } + }, "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 1dc86bfe..c22c0df0 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -3713,7 +3713,7 @@ "BearerAuth": [] } ], - "description": "Create a new project with the specified information.", + "description": "Create a new project with the specified information. Optionally specify a workspace to assign the project to.", "consumes": [ "application/json" ], @@ -3732,7 +3732,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.Project" + "$ref": "#/definitions/model.CreateProjectRequest" } } ], @@ -3752,6 +3752,15 @@ } } }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -3764,6 +3773,138 @@ } } }, + "/api/projects/assign/workspaces": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "프로젝트에 워크스페이스를 연결합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "프로젝트에 워크스페이스 연결", + "operationId": "addWorkspaceToProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: 잘못된 ID 형식", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: 서버 내부 오류", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/projects/id/{projectId}/workspaces": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve list of workspaces that the project is assigned to", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Get workspaces assigned to project", + "operationId": "getProjectWorkspaces", + "parameters": [ + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Workspace" + } + } + }, + "400": { + "description": "error: Invalid project ID", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "404": { + "description": "error: Project not found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/list": { "post": { "security": [ @@ -3861,6 +4002,61 @@ } } }, + "/api/projects/unassign/workspaces": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a workspace from a project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "projects" + ], + "summary": "Remove workspace from project", + "operationId": "removeWorkspaceFromProject", + "parameters": [ + { + "description": "Workspace and Project IDs", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.WorkspaceProjectMappingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "error: Invalid request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "error: Internal server error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, "/api/projects/{id}": { "get": { "security": [ @@ -4040,75 +4236,6 @@ } } }, - "/api/projects/{id}/workspaces/{workspaceId}": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "프로젝트에 워크스페이스를 연결합니다.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "projects" - ], - "summary": "프로젝트에 워크스페이스 연결", - "operationId": "addWorkspaceToProject", - "parameters": [ - { - "type": "integer", - "description": "프로젝트 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "워크스페이스 ID", - "name": "workspaceId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "error: 잘못된 ID 형식", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "404": { - "description": "error: 프로젝트 또는 워크스페이스를 찾을 수 없습니다", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: 서버 내부 오류", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, "/api/resource-types/cloud-resources": { "post": { "security": [ @@ -9268,6 +9395,24 @@ } } }, + "model.CreateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "workspaceId": { + "description": "optional workspace to assign project to", + "type": "string" + } + } + }, "model.CreateRoleRequest": { "type": "object", "required": [ @@ -10336,6 +10481,24 @@ } } }, + "model.WorkspaceProjectMappingRequest": { + "type": "object", + "required": [ + "projectIds", + "workspaceId" + ], + "properties": { + "projectIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "workspaceId": { + "type": "string" + } + } + }, "model.WorkspaceWithUsersAndRoles": { "type": "object", "properties": { diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 969050e0..e319f4c5 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -299,6 +299,18 @@ definitions: - menuIds - roleId type: object + model.CreateProjectRequest: + properties: + description: + type: string + name: + type: string + workspaceId: + description: optional workspace to assign project to + type: string + required: + - name + type: object model.CreateRoleRequest: properties: cspRoles: @@ -1017,6 +1029,18 @@ definitions: updated_at: type: string type: object + model.WorkspaceProjectMappingRequest: + properties: + projectIds: + items: + type: string + type: array + workspaceId: + type: string + required: + - projectIds + - workspaceId + type: object model.WorkspaceWithUsersAndRoles: properties: created_at: @@ -3452,7 +3476,8 @@ paths: post: consumes: - application/json - description: Create a new project with the specified information. + description: Create a new project with the specified information. Optionally + specify a workspace to assign the project to. operationId: createProject parameters: - description: Project Info @@ -3460,7 +3485,7 @@ paths: name: project required: true schema: - $ref: '#/definitions/model.Project' + $ref: '#/definitions/model.CreateProjectRequest' produces: - application/json responses: @@ -3474,6 +3499,12 @@ paths: additionalProperties: type: string type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object "500": description: Internal Server Error schema: @@ -3601,23 +3632,19 @@ paths: summary: Update project tags: - projects - /api/projects/{id}/workspaces/{workspaceId}: + /api/projects/assign/workspaces: post: consumes: - application/json description: 프로젝트에 워크스페이스를 연결합니다. operationId: addWorkspaceToProject parameters: - - description: 프로젝트 ID - in: path - name: id - required: true - type: integer - - description: 워크스페이스 ID - in: path - name: workspaceId + - description: Workspace and Project IDs + in: body + name: request required: true - type: integer + schema: + $ref: '#/definitions/model.WorkspaceProjectMappingRequest' produces: - application/json responses: @@ -3646,6 +3673,50 @@ paths: summary: 프로젝트에 워크스페이스 연결 tags: - projects + /api/projects/id/{projectId}/workspaces: + get: + consumes: + - application/json + description: Retrieve list of workspaces that the project is assigned to + operationId: getProjectWorkspaces + parameters: + - description: Project ID + in: path + name: projectId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.Workspace' + type: array + "400": + description: 'error: Invalid project ID' + schema: + additionalProperties: + type: string + type: object + "404": + description: 'error: Project not found' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Internal server error' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Get workspaces assigned to project + tags: + - projects /api/projects/list: post: consumes: @@ -3708,6 +3779,41 @@ paths: summary: Get project by name tags: - projects + /api/projects/unassign/workspaces: + delete: + consumes: + - application/json + description: Remove a workspace from a project + operationId: removeWorkspaceFromProject + parameters: + - description: Workspace and Project IDs + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.WorkspaceProjectMappingRequest' + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: 'error: Invalid request' + schema: + additionalProperties: + type: string + type: object + "500": + description: 'error: Internal server error' + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Remove workspace from project + tags: + - projects /api/resource-types/cloud-resources: post: consumes: From fc7e990d1c2533c7cde049e33e6a8bbd6b7df6c7 Mon Sep 17 00:00:00 2001 From: dogfootman Date: Mon, 12 Jan 2026 15:21:15 +0900 Subject: [PATCH 13/14] modify gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f19de1f4..cd4389a1 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,8 @@ old dockerfiles/nginx/nginx.conf # Documentation (local only) -doc/ \ No newline at end of file +doc/ +# Spec reference (local only) +# Developers copy SPEC.md.template to SPEC.md for local reference +# SPEC.md is not committed to avoid conflicts +SPEC.md From a10e3b19011edf519c0326819df8fba5fa67ed55 Mon Sep 17 00:00:00 2001 From: dogfootman Date: Fri, 23 Jan 2026 14:52:07 +0900 Subject: [PATCH 14/14] Add support for base URL and authentication in API import functionality --- src/handler/mcmpapi_handler.go | 4 +- src/model/request.go | 4 ++ src/repository/mcmpapi_repository.go | 16 ++++++ src/service/mcmpapi_service.go | 82 +++++++++++++++++++++++++++- 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/src/handler/mcmpapi_handler.go b/src/handler/mcmpapi_handler.go index 487eb8b4..652f1e76 100644 --- a/src/handler/mcmpapi_handler.go +++ b/src/handler/mcmpapi_handler.go @@ -51,11 +51,11 @@ func (h *McmpApiHandler) SyncMcmpAPIs(c echo.Context) error { // ImportAPIs godoc // @Summary Import MCMP APIs from Remote Sources -// @Description Fetches API specifications from remote URLs and imports them to the database. Supports swagger and openapi source types. +// @Description Fetches API specifications from remote URLs and imports them to the database. Supports swagger and openapi source types. Optionally accepts baseUrl and authentication info to populate the mcmp_api_services table. // @Tags McmpAPI // @Accept json // @Produce json -// @Param request body model.ImportApiRequest true "Frameworks to import" +// @Param request body model.ImportApiRequest true "Frameworks to import (with optional baseUrl, authType, authUser, authPass)" // @Success 200 {object} model.ImportApiResponse "Import results" // @Failure 400 {object} map[string]string "error: Invalid request body" // @Failure 500 {object} map[string]string "error: Failed to import APIs" diff --git a/src/model/request.go b/src/model/request.go index a6ee8d37..ec3d64b1 100644 --- a/src/model/request.go +++ b/src/model/request.go @@ -236,6 +236,10 @@ type ImportApiFramework struct { Repository string `json:"repository,omitempty"` // Repository URL (e.g., "https://github.com/...") SourceType string `json:"sourceType" validate:"required"` // Source type: "swagger" or "openapi" SourceURL string `json:"sourceUrl" validate:"required"` // URL to fetch the API specification from + BaseURL string `json:"baseUrl,omitempty"` // Base URL for the service (e.g., "http://localhost:1323/tumblebug") + AuthType string `json:"authType,omitempty"` // Authentication type: "none", "basic", "bearer" + AuthUser string `json:"authUser,omitempty"` // Username for basic auth + AuthPass string `json:"authPass,omitempty"` // Password for basic auth or token for bearer auth } // ImportApiRequest represents the request body for importing APIs from remote sources diff --git a/src/repository/mcmpapi_repository.go b/src/repository/mcmpapi_repository.go index 2a297733..f3c2b929 100644 --- a/src/repository/mcmpapi_repository.go +++ b/src/repository/mcmpapi_repository.go @@ -23,6 +23,7 @@ type McmpApiRepository interface { DeleteActionsByServiceName(tx *gorm.DB, serviceName string) error GetServiceMeta(serviceName string) (*mcmpapi.McmpApiServiceMeta, error) UpsertServiceMeta(tx *gorm.DB, meta *mcmpapi.McmpApiServiceMeta) error + UpsertService(tx *gorm.DB, service *mcmpapi.McmpApiService) error } // mcmpApiRepository implements the McmpApiRepository interface. @@ -385,3 +386,18 @@ func (r *mcmpApiRepository) UpsertServiceMeta(tx *gorm.DB, meta *mcmpapi.McmpApi } return nil } + +// UpsertService creates or updates a service record within a transaction. +func (r *mcmpApiRepository) UpsertService(tx *gorm.DB, service *mcmpapi.McmpApiService) error { + // Use Save which does upsert based on primary key (Name) + query := tx.Save(service) + if err := query.Error; err != nil { + sql := query.Statement.SQL.String() + args := query.Statement.Vars + log.Printf("UpsertService SQL Query (ERROR): %s", sql) + log.Printf("UpsertService SQL Args (ERROR): %v", args) + return fmt.Errorf("failed to upsert service %s: %w", service.Name, err) + } + log.Printf("Upserted service: %s (version: %s, baseURL: %s)", service.Name, service.Version, service.BaseURL) + return nil +} diff --git a/src/service/mcmpapi_service.go b/src/service/mcmpapi_service.go index 07101a24..65e1dc40 100644 --- a/src/service/mcmpapi_service.go +++ b/src/service/mcmpapi_service.go @@ -490,8 +490,17 @@ func (s *mcmpApiService) ImportAPIs(req *model.ImportApiRequest) (*model.ImportA response.FailureCount++ log.Printf("Failed to import framework %s: %v", fw.Name, fwResult.Error) } else { - // Sync to database - err := s.syncFrameworkToDatabase(fw.Name, fw.Version, fw.Repository, fwResult.Actions) + // Sync to database - use the new method if service info is provided + var err error + if fw.BaseURL != "" { + // Use the new method that saves service info + err = s.syncFrameworkWithServiceInfo(fw.Name, fw.Version, fw.Repository, fw.BaseURL, fw.AuthType, fw.AuthUser, fw.AuthPass, fwResult.Actions) + } else { + // Use the old method (meta + actions only) + err = s.syncFrameworkToDatabase(fw.Name, fw.Version, fw.Repository, fwResult.Actions) + log.Printf("Warning: Framework '%s' imported without service info (baseUrl not provided)", fw.Name) + } + if err != nil { result.Success = false result.ErrorMessage = fmt.Sprintf("failed to save to database: %v", err) @@ -564,3 +573,72 @@ func (s *mcmpApiService) syncFrameworkToDatabase(serviceName, version, repositor return nil } + +// syncFrameworkWithServiceInfo saves a framework's actions and service info to the database +func (s *mcmpApiService) syncFrameworkWithServiceInfo(serviceName, version, repository, baseURL, authType, authUser, authPass string, actions map[string]apiparser.ServiceAction) error { + tx := s.db.Begin() + if tx.Error != nil { + return fmt.Errorf("failed to begin transaction: %w", tx.Error) + } + defer func() { + if r := recover(); r != nil { + tx.Rollback() + panic(r) + } + }() + + // Upsert service meta + meta := &mcmpapi.McmpApiServiceMeta{ + ServiceName: serviceName, + Version: version, + Repository: repository, + GeneratedAt: time.Now(), + } + if err := s.repo.UpsertServiceMeta(tx, meta); err != nil { + tx.Rollback() + return fmt.Errorf("failed to upsert meta: %w", err) + } + + // Upsert service (with BaseURL and Auth info) + service := &mcmpapi.McmpApiService{ + Name: serviceName, + Version: version, + BaseURL: baseURL, + AuthType: authType, + AuthUser: authUser, + AuthPass: authPass, + IsActive: true, + } + if err := s.repo.UpsertService(tx, service); err != nil { + tx.Rollback() + return fmt.Errorf("failed to upsert service: %w", err) + } + + // Delete existing actions for this service (full replace) + if err := s.repo.DeleteActionsByServiceName(tx, serviceName); err != nil { + tx.Rollback() + return fmt.Errorf("failed to delete existing actions: %w", err) + } + + // Create new actions + for actionName, actionDef := range actions { + action := &mcmpapi.McmpApiAction{ + ServiceName: serviceName, + ActionName: actionName, + Method: actionDef.Method, + ResourcePath: actionDef.ResourcePath, + Description: actionDef.Description, + } + + if err := s.repo.CreateAction(tx, action); err != nil { + tx.Rollback() + return fmt.Errorf("failed to create action %s: %w", actionName, err) + } + } + + if err := tx.Commit().Error; err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + + return nil +}