From d22890795e468181fbcda37af79690972f5fe201 Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Fri, 24 Nov 2023 17:03:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E4=B8=AA=E4=BA=BA=E8=AE=BE=E7=BD=AE):=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0Api=20Keys=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/metersphere/system/domain/UserKey.java | 27 ++- .../system/domain/UserKeyExample.java | 190 ++++++++++++++++ .../system/mapper/UserKeyMapper.xml | 73 ++++++- .../3.0.0/ddl/V3.0.0_11__system_setting.sql | 7 +- .../migration/3.0.0/dml/V3.0.0_11_1__data.sql | 37 +++- .../sdk/constants/PermissionConstants.java | 7 + .../main/resources/i18n/commons.properties | 4 + .../resources/i18n/commons_en_US.properties | 6 +- .../resources/i18n/commons_zh_CN.properties | 5 +- .../resources/i18n/commons_zh_TW.properties | 6 +- .../src/main/resources/i18n/system.properties | 3 + .../src/main/resources/permission.json | 21 ++ .../controller/UserApiKeysController.java | 83 +++++++ .../io/metersphere/system/dto/UserKeyDTO.java | 24 +++ .../system/service/UserKeyLogService.java | 100 +++++++++ .../system/service/UserKeyService.java | 56 ++++- .../src/main/resources/permission.json | 31 +++ .../UserApiKeysControllerTests.java | 204 ++++++++++++++++++ 18 files changed, 859 insertions(+), 25 deletions(-) create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserApiKeysController.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/dto/UserKeyDTO.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyLogService.java create mode 100644 backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserApiKeysControllerTests.java diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKey.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKey.java index 99922261a7..8815d429ba 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKey.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKey.java @@ -10,30 +10,40 @@ import lombok.Data; @Data public class UserKey implements Serializable { - @Schema(description = "user_key ID", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "user_key ID", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(message = "{user_key.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{user_key.id.length_range}", groups = {Created.class, Updated.class}) private String id; - @Schema(description = "用户ID") + @Schema(description = "用户ID") private String createUser; - @Schema(description = "access_key", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "access_key", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(message = "{user_key.access_key.not_blank}", groups = {Created.class}) @Size(min = 1, max = 50, message = "{user_key.access_key.length_range}", groups = {Created.class, Updated.class}) private String accessKey; - @Schema(description = "secret key", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "secret key", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(message = "{user_key.secret_key.not_blank}", groups = {Created.class}) @Size(min = 1, max = 50, message = "{user_key.secret_key.length_range}", groups = {Created.class, Updated.class}) private String secretKey; - @Schema(description = "创建时间") + @Schema(description = "创建时间") private Long createTime; - @Schema(description = "状态") + @Schema(description = "状态") private Boolean enable; + @Schema(description = "是否永久有效", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{user_key.forever.not_blank}", groups = {Created.class}) + private Boolean forever; + + @Schema(description = "到期时间") + private Long expireTime; + + @Schema(description = "描述") + private String description; + private static final long serialVersionUID = 1L; public enum Column { @@ -42,7 +52,10 @@ public class UserKey implements Serializable { accessKey("access_key", "accessKey", "VARCHAR", false), secretKey("secret_key", "secretKey", "VARCHAR", false), createTime("create_time", "createTime", "BIGINT", false), - enable("enable", "enable", "BIT", true); + enable("enable", "enable", "BIT", true), + forever("forever", "forever", "BIT", false), + expireTime("expire_time", "expireTime", "BIGINT", false), + description("description", "description", "VARCHAR", false); private static final String BEGINNING_DELIMITER = "`"; diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKeyExample.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKeyExample.java index 7334b36f60..2ff5f2624c 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKeyExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/UserKeyExample.java @@ -503,6 +503,196 @@ public class UserKeyExample { addCriterion("`enable` not between", value1, value2, "enable"); return (Criteria) this; } + + public Criteria andForeverIsNull() { + addCriterion("forever is null"); + return (Criteria) this; + } + + public Criteria andForeverIsNotNull() { + addCriterion("forever is not null"); + return (Criteria) this; + } + + public Criteria andForeverEqualTo(Boolean value) { + addCriterion("forever =", value, "forever"); + return (Criteria) this; + } + + public Criteria andForeverNotEqualTo(Boolean value) { + addCriterion("forever <>", value, "forever"); + return (Criteria) this; + } + + public Criteria andForeverGreaterThan(Boolean value) { + addCriterion("forever >", value, "forever"); + return (Criteria) this; + } + + public Criteria andForeverGreaterThanOrEqualTo(Boolean value) { + addCriterion("forever >=", value, "forever"); + return (Criteria) this; + } + + public Criteria andForeverLessThan(Boolean value) { + addCriterion("forever <", value, "forever"); + return (Criteria) this; + } + + public Criteria andForeverLessThanOrEqualTo(Boolean value) { + addCriterion("forever <=", value, "forever"); + return (Criteria) this; + } + + public Criteria andForeverIn(List values) { + addCriterion("forever in", values, "forever"); + return (Criteria) this; + } + + public Criteria andForeverNotIn(List values) { + addCriterion("forever not in", values, "forever"); + return (Criteria) this; + } + + public Criteria andForeverBetween(Boolean value1, Boolean value2) { + addCriterion("forever between", value1, value2, "forever"); + return (Criteria) this; + } + + public Criteria andForeverNotBetween(Boolean value1, Boolean value2) { + addCriterion("forever not between", value1, value2, "forever"); + return (Criteria) this; + } + + public Criteria andExpireTimeIsNull() { + addCriterion("expire_time is null"); + return (Criteria) this; + } + + public Criteria andExpireTimeIsNotNull() { + addCriterion("expire_time is not null"); + return (Criteria) this; + } + + public Criteria andExpireTimeEqualTo(Long value) { + addCriterion("expire_time =", value, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeNotEqualTo(Long value) { + addCriterion("expire_time <>", value, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeGreaterThan(Long value) { + addCriterion("expire_time >", value, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeGreaterThanOrEqualTo(Long value) { + addCriterion("expire_time >=", value, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeLessThan(Long value) { + addCriterion("expire_time <", value, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeLessThanOrEqualTo(Long value) { + addCriterion("expire_time <=", value, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeIn(List values) { + addCriterion("expire_time in", values, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeNotIn(List values) { + addCriterion("expire_time not in", values, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeBetween(Long value1, Long value2) { + addCriterion("expire_time between", value1, value2, "expireTime"); + return (Criteria) this; + } + + public Criteria andExpireTimeNotBetween(Long value1, Long value2) { + addCriterion("expire_time not between", value1, value2, "expireTime"); + return (Criteria) this; + } + + public Criteria andDescriptionIsNull() { + addCriterion("description is null"); + return (Criteria) this; + } + + public Criteria andDescriptionIsNotNull() { + addCriterion("description is not null"); + return (Criteria) this; + } + + public Criteria andDescriptionEqualTo(String value) { + addCriterion("description =", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotEqualTo(String value) { + addCriterion("description <>", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionGreaterThan(String value) { + addCriterion("description >", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionGreaterThanOrEqualTo(String value) { + addCriterion("description >=", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLessThan(String value) { + addCriterion("description <", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLessThanOrEqualTo(String value) { + addCriterion("description <=", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLike(String value) { + addCriterion("description like", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotLike(String value) { + addCriterion("description not like", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionIn(List values) { + addCriterion("description in", values, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotIn(List values) { + addCriterion("description not in", values, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionBetween(String value1, String value2) { + addCriterion("description between", value1, value2, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotBetween(String value1, String value2) { + addCriterion("description not between", value1, value2, "description"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/UserKeyMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/UserKeyMapper.xml index 2e644d6bdc..15898a1a53 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/UserKeyMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/UserKeyMapper.xml @@ -8,6 +8,9 @@ + + + @@ -68,7 +71,8 @@ - id, create_user, access_key, secret_key, create_time, `enable` + id, create_user, access_key, secret_key, create_time, `enable`, forever, expire_time, + description @@ -178,6 +202,15 @@ `enable` = #{record.enable,jdbcType=BIT}, + + forever = #{record.forever,jdbcType=BIT}, + + + expire_time = #{record.expireTime,jdbcType=BIGINT}, + + + description = #{record.description,jdbcType=VARCHAR}, + @@ -190,7 +223,10 @@ access_key = #{record.accessKey,jdbcType=VARCHAR}, secret_key = #{record.secretKey,jdbcType=VARCHAR}, create_time = #{record.createTime,jdbcType=BIGINT}, - `enable` = #{record.enable,jdbcType=BIT} + `enable` = #{record.enable,jdbcType=BIT}, + forever = #{record.forever,jdbcType=BIT}, + expire_time = #{record.expireTime,jdbcType=BIGINT}, + description = #{record.description,jdbcType=VARCHAR} @@ -213,6 +249,15 @@ `enable` = #{enable,jdbcType=BIT}, + + forever = #{forever,jdbcType=BIT}, + + + expire_time = #{expireTime,jdbcType=BIGINT}, + + + description = #{description,jdbcType=VARCHAR}, + where id = #{id,jdbcType=VARCHAR} @@ -222,16 +267,21 @@ access_key = #{accessKey,jdbcType=VARCHAR}, secret_key = #{secretKey,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=BIGINT}, - `enable` = #{enable,jdbcType=BIT} + `enable` = #{enable,jdbcType=BIT}, + forever = #{forever,jdbcType=BIT}, + expire_time = #{expireTime,jdbcType=BIGINT}, + description = #{description,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR} insert into user_key - (id, create_user, access_key, secret_key, create_time, `enable`) + (id, create_user, access_key, secret_key, create_time, `enable`, forever, expire_time, + description) values (#{item.id,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR}, #{item.accessKey,jdbcType=VARCHAR}, - #{item.secretKey,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.enable,jdbcType=BIT} + #{item.secretKey,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.enable,jdbcType=BIT}, + #{item.forever,jdbcType=BIT}, #{item.expireTime,jdbcType=BIGINT}, #{item.description,jdbcType=VARCHAR} ) @@ -263,6 +313,15 @@ #{item.enable,jdbcType=BIT} + + #{item.forever,jdbcType=BIT} + + + #{item.expireTime,jdbcType=BIGINT} + + + #{item.description,jdbcType=VARCHAR} + ) diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql index c00763385d..151d9c31bc 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql @@ -270,14 +270,17 @@ CREATE TABLE IF NOT EXISTS user_role_permission CREATE INDEX idx_group_id ON user_role_permission (`role_id`); CREATE INDEX idx_permission_id ON user_role_permission (`permission_id`); -CREATE TABLE IF NOT EXISTS user_key +CREATE TABLE user_key ( `id` VARCHAR(50) NOT NULL COMMENT 'user_key ID', `create_user` VARCHAR(50) NOT NULL COMMENT '用户ID', `access_key` VARCHAR(50) NOT NULL COMMENT 'access_key', `secret_key` VARCHAR(50) NOT NULL COMMENT 'secret key', `create_time` BIGINT NOT NULL COMMENT '创建时间', - `enable` BIT COMMENT '状态', + `enable` BIT NOT NULL DEFAULT 1 COMMENT '状态', + `forever` BIT NOT NULL DEFAULT 1 COMMENT '是否永久有效', + `expire_time` BIGINT COMMENT '到期时间', + `description` VARCHAR(255) COMMENT '描述', PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql index 5d91e425bc..045296c1f0 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql @@ -127,7 +127,24 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+UPDATE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_BASE_INFO:READ+UPDATE'); - +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ+IMPORT'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ+EXECUTE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION:READ+IMPORT'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION:READ+EXPORT'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION:READ+EXECUTE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION_CASE:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION_CASE:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION_CASE:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION_CASE:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION_CASE:READ+EXECUTE'); -- 项目成员权限 INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BASE_INFO:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_USER:READ'); @@ -181,6 +198,24 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'BUG:READ+UPDATE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'BUG:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BASE_INFO:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+IMPORT'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+EXECUTE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION:READ+IMPORT'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION:READ+EXPORT'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION:READ+EXECUTE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION_CASE:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION_CASE:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION_CASE:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION_CASE:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEFINITION_CASE:READ+EXECUTE'); -- 初始化当前站点配置 INSERT into system_parameter values('base.url', 'http://127.0.0.1:8081', 'text'); -- 初始化prometheus站点配置 diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java index a37f66941f..7e7c79ce36 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java @@ -258,6 +258,13 @@ public class PermissionConstants { public static final String PROJECT_API_DEFINITION_CASE_UPDATE = "PROJECT_API_DEFINITION_CASE:READ+UPDATE"; public static final String PROJECT_API_DEFINITION_CASE_DELETE = "PROJECT_API_DEFINITION_CASE:READ+DELETE"; public static final String PROJECT_API_DEFINITION_CASE_RECOVER = "PROJECT_API_DEFINITION_CASE:READ+RECOVER"; + public static final String PROJECT_API_DEFINITION_CASE_EXECUTE = "PROJECT_API_DEFINITION_CASE:READ+EXECUTE"; /*------ end: API_MANAGEMENT ------*/ + //个人中心 + /*------ start: PERSONAL_CENTER ------*/ + public static final String SYSTEM_PERSONAL_API_KEY_ADD = "SYSTEM_PERSONAL_API_KEY_ADD:READ+ADD"; + public static final String SYSTEM_PERSONAL_API_KEY_DELETE = "SYSTEM_PERSONAL_API_KEY_DELETE:READ+DELETE"; + public static final String SYSTEM_PERSONAL_API_KEY_READ = "SYSTEM_PERSONAL_API_KEY_READ:READ"; + public static final String SYSTEM_PERSONAL_API_KEY_UPDATE = "SYSTEM_PERSONAL_API_KEY_UPDATE:READ+UPDATE"; } diff --git a/backend/framework/sdk/src/main/resources/i18n/commons.properties b/backend/framework/sdk/src/main/resources/i18n/commons.properties index 1e8d583b1f..cc71861192 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons.properties @@ -469,6 +469,7 @@ status_item.bug_rejected=已拒绝 permission.api_test.name=接口测试 permission.api_debug.name=接口调试 permission.api_definition.name=接口管理 +permission.api_case.name=接口用例 permission.api_definition.import=导入 permission.api_definition.export=导出 permission.api_definition.execute=执行 @@ -484,4 +485,7 @@ environment_id_is_null=环境ID不能为空 environment_is_not_exist=环境不存在 status_is_null=状态不能为空 priority_is_null=用例等级不能为空 +apikey_has_expired=ApiKey 已过期 +user_key.id.not_blank=ApiKey ID不能为空 +expire_time_not_null=过期时间不能为空 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties index ecd8d2d279..5eed95c971 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties @@ -479,6 +479,7 @@ status_item.bug_rejected=REJECTED permission.api_test.name=API Test permission.api_debug.name=API Debug permission.api_definition.name=API Management +permission.api_case.name=API Case permission.api_definition.import=Import permission.api_definition.export=Export permission.api_definition.execute=Execute @@ -493,4 +494,7 @@ batch_edit_type_error=Batch edit type error environment_id_is_null=Environment ID is null environment_is_not_exist=Environment is not exist status_is_null=Status is null -priority_is_null=Priority is null \ No newline at end of file +priority_is_null=Priority is null +apikey_has_expired=ApiKey has expired +user_key.id.not_blank=User key id can not blank +expire_time_not_null=Expire time can not null \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties index 5d36daf7bb..3045e53b02 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties @@ -489,4 +489,7 @@ batch_edit_type_error=批量编辑类型错误 environment_id_is_null=环境ID不能为空 environment_is_not_exist=环境不存在 status_is_null=状态不能为空 -priority_is_null=用例等级不能为空 \ No newline at end of file +priority_is_null=用例等级不能为空 +apikey_has_expired=ApiKey 已过期 +user_key.id.not_blank=ApiKey ID不能为空 +expire_time_not_null=过期时间不能为空 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties index 2db130d5e9..4e32f3a701 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties @@ -475,6 +475,7 @@ status_item.bug_rejected=已拒絕 permission.api_test.name=接口測試 permission.api_debug.name=接口調試 permission.api_definition.name=接口管理 +permission.api_case.name=接口用例 permission.api_definition.import=導入 permission.api_definition.export=導出 permission.api_definition.execute=執行 @@ -489,4 +490,7 @@ batch_edit_type_error=批量編輯類型錯誤 environment_id_is_null=環境ID不能為空 environment_is_not_exist=環境不存在 status_is_null=狀態不能為空 -priority_is_null=優先級不能為空 \ No newline at end of file +priority_is_null=優先級不能為空 +apikey_has_expired=ApiKey 已過期 +user_key.id.not_blank=ApiKey ID不能为空 +expire_time_not_null=過期時間不能為空 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/system.properties b/backend/framework/sdk/src/main/resources/i18n/system.properties index a2baeb2f37..886fbcfb54 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system.properties @@ -274,6 +274,9 @@ status_flow.to_id.length_range=目的状态ID长度必须在{min}和{max}之间 # message user.remove=已被移除 alert_others=通知人 +personal_settings=个人设置 +my_settings=我的设置 +api_keys=Api Keys diff --git a/backend/services/api-test/src/main/resources/permission.json b/backend/services/api-test/src/main/resources/permission.json index 5dd4f3bd7b..44c5506af3 100644 --- a/backend/services/api-test/src/main/resources/permission.json +++ b/backend/services/api-test/src/main/resources/permission.json @@ -54,6 +54,27 @@ "id": "PROJECT_API_DEFINITION:READ+EXPORT" } ] + }, + { + "id": "PROJECT_API_DEFINITION_CASE", + "name": "permission.api_case.name", + "permissions": [ + { + "id": "PROJECT_API_DEFINITION_CASE:READ" + }, + { + "id": "PROJECT_API_DEFINITION_CASE:READ+ADD" + }, + { + "id": "PROJECT_API_DEFINITION_CASE:READ+UPDATE" + }, + { + "id": "PROJECT_API_DEFINITION_CASE:READ+DELETE" + }, + { + "id": "PROJECT_API_DEFINITION_CASE:READ+EXECUTE" + } + ] } ] } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserApiKeysController.java b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserApiKeysController.java new file mode 100644 index 0000000000..5ee25f9c70 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserApiKeysController.java @@ -0,0 +1,83 @@ +package io.metersphere.system.controller; + + +import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.system.domain.UserKey; +import io.metersphere.system.dto.UserKeyDTO; +import io.metersphere.system.log.annotation.Log; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.security.ApiKeyHandler; +import io.metersphere.system.service.UserKeyLogService; +import io.metersphere.system.service.UserKeyService; +import io.metersphere.system.utils.SessionUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.ServletRequest; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.apache.shiro.web.util.WebUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/user/api/key") +@Tag(name = "系统设置-个人中心-我的设置-Api Keys") +public class UserApiKeysController { + + @Resource + private UserKeyService userKeyService; + + @GetMapping("/list") + @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_READ) + @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-获取Api Keys列表") + public List getUserKeysInfo() { + return userKeyService.getUserKeysInfo(SessionUtils.getUserId()); + } + + @GetMapping("/validate") + @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-验证Api Keys") + public String validate(ServletRequest request) { + return ApiKeyHandler.getUser(WebUtils.toHttp(request)); + } + + @GetMapping("/add") + @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_ADD) + @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-生成Api Keys") + public void add() { + userKeyService.add(SessionUtils.getUserId()); + } + + @GetMapping("/delete/{id}") + @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_DELETE) + @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-删除Api Keys") + @Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = UserKeyLogService.class) + public void delete(@PathVariable String id) { + userKeyService.deleteUserKey(id); + } + + @PostMapping("/update") + @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-修改Api Keys") + @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE) + @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = UserKeyLogService.class) + public void update(@Validated @RequestBody UserKeyDTO request) { + userKeyService.updateUserKey(request); + } + + @GetMapping("/enable/{id}") + @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-开启Api Keys") + @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE) + @Log(type = OperationLogType.DELETE, expression = "#msClass.enableLog(#id)", msClass = UserKeyLogService.class) + public void enable(@PathVariable String id) { + userKeyService.enableUserKey(id); + } + + @GetMapping("/disable/{id}") + @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-关闭Api Keys") + @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE) + @Log(type = OperationLogType.DELETE, expression = "#msClass.disableLog(#id)", msClass = UserKeyLogService.class) + public void disabledUserKey(@PathVariable String id) { + userKeyService.disableUserKey(id); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/UserKeyDTO.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/UserKeyDTO.java new file mode 100644 index 0000000000..28bf6cd084 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/UserKeyDTO.java @@ -0,0 +1,24 @@ +package io.metersphere.system.dto; + +import io.metersphere.validation.groups.Updated; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class UserKeyDTO implements Serializable { + + private static final long serialVersionUID = 1L; + @Schema(description = "user_key ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{user_key.id.not_blank}", groups = {Updated.class}) + private String id; + @Schema(description = "是否永久有效", requiredMode = Schema.RequiredMode.REQUIRED) + private Boolean forever; + @Schema(description = "到期时间") + private Long expireTime; + @Schema(description = "描述") + private String description; + +} \ No newline at end of file diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyLogService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyLogService.java new file mode 100644 index 0000000000..2407eac4a8 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyLogService.java @@ -0,0 +1,100 @@ +package io.metersphere.system.service; + +import io.metersphere.sdk.constants.HttpMethodConstants; +import io.metersphere.sdk.constants.OperationLogConstants; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.domain.UserKey; +import io.metersphere.system.dto.UserKeyDTO; +import io.metersphere.system.dto.builder.LogDTOBuilder; +import io.metersphere.system.log.constants.OperationLogModule; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.log.dto.LogDTO; +import io.metersphere.system.mapper.UserKeyMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(rollbackFor = Exception.class) +public class UserKeyLogService { + + @Resource + private UserKeyMapper userKeyMapper; + + public LogDTO deleteLog(String id) { + UserKey userkey = userKeyMapper.selectByPrimaryKey(id); + if (userkey != null) { + LogDTO dto = LogDTOBuilder.builder() + .projectId(OperationLogConstants.SYSTEM) + .organizationId(OperationLogConstants.SYSTEM) + .type(OperationLogType.DELETE.name()) + .module(OperationLogModule.PERSONAL_INFORMATION_APIKEYS) + .method(HttpMethodConstants.GET.name()) + .path("/user/api/key/delete") + .sourceId(id) + .content(userkey.getAccessKey()) + .originalValue(JSON.toJSONBytes(userkey)) + .build().getLogDTO(); + return dto; + } + return null; + } + + public LogDTO updateLog(UserKeyDTO userKeyDTO) { + UserKey userkey = userKeyMapper.selectByPrimaryKey(userKeyDTO.getId()); + if (userkey != null) { + LogDTO dto = LogDTOBuilder.builder() + .projectId(OperationLogConstants.SYSTEM) + .organizationId(OperationLogConstants.SYSTEM) + .type(OperationLogType.UPDATE.name()) + .module(OperationLogModule.PERSONAL_INFORMATION_APIKEYS) + .method(HttpMethodConstants.POST.name()) + .path("/user/api/key/update") + .sourceId(userkey.getId()) + .content(userkey.getAccessKey()) + .originalValue(JSON.toJSONBytes(userkey)) + .build().getLogDTO(); + return dto; + } + return null; + } + + public LogDTO enableLog(String id) { + UserKey userkey = userKeyMapper.selectByPrimaryKey(id); + if (userkey != null) { + LogDTO dto = LogDTOBuilder.builder() + .projectId(OperationLogConstants.SYSTEM) + .organizationId(OperationLogConstants.SYSTEM) + .type(OperationLogType.UPDATE.name()) + .module(OperationLogModule.PERSONAL_INFORMATION_APIKEYS) + .method(HttpMethodConstants.GET.name()) + .path("/user/api/key/enable") + .sourceId(id) + .content(userkey.getAccessKey()) + .originalValue(JSON.toJSONBytes(userkey)) + .build().getLogDTO(); + return dto; + } + return null; + } + + public LogDTO disableLog(String id) { + UserKey userkey = userKeyMapper.selectByPrimaryKey(id); + if (userkey != null) { + LogDTO dto = LogDTOBuilder.builder() + .projectId(OperationLogConstants.SYSTEM) + .organizationId(OperationLogConstants.SYSTEM) + .type(OperationLogType.UPDATE.name()) + .module(OperationLogModule.PERSONAL_INFORMATION_APIKEYS) + .method(HttpMethodConstants.GET.name()) + .path("/user/api/key/disable") + .sourceId(id) + .content(userkey.getAccessKey()) + .originalValue(JSON.toJSONBytes(userkey)) + .build().getLogDTO(); + return dto; + } + return null; + } + +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyService.java index 3ed6ae8bfd..17dd00ffc1 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/UserKeyService.java @@ -1,11 +1,22 @@ package io.metersphere.system.service; +import com.alibaba.excel.util.BooleanUtils; +import io.metersphere.sdk.constants.HttpMethodConstants; +import io.metersphere.sdk.constants.OperationLogConstants; import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.Translator; import io.metersphere.system.domain.UserKey; import io.metersphere.system.domain.UserKeyExample; +import io.metersphere.system.dto.UserKeyDTO; +import io.metersphere.system.dto.builder.LogDTOBuilder; +import io.metersphere.system.log.constants.OperationLogModule; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.log.dto.LogDTO; +import io.metersphere.system.log.service.OperationLogService; import io.metersphere.system.mapper.UserKeyMapper; +import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.stereotype.Service; @@ -13,7 +24,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.util.List; -import io.metersphere.system.uid.IDGenerator; @Service @Transactional(rollbackFor = Exception.class) @@ -24,6 +34,8 @@ public class UserKeyService { @Resource private UserLoginService userLoginService; + @Resource + private OperationLogService operationLogService; public List getUserKeysInfo(String userId) { UserKeyExample userKeysExample = new UserKeyExample(); @@ -32,7 +44,7 @@ public class UserKeyService { return userKeyMapper.selectByExample(userKeysExample); } - public UserKey generateUserKey(String userId) { + public void add(String userId) { if (userLoginService.getUserDTO(userId) == null) { throw new MSException(Translator.get("user_not_exist") + userId); } @@ -51,15 +63,28 @@ public class UserKeyService { userKeys.setAccessKey(RandomStringUtils.randomAlphanumeric(16)); userKeys.setSecretKey(RandomStringUtils.randomAlphanumeric(16)); userKeys.setCreateTime(System.currentTimeMillis()); + userKeys.setForever(true); userKeyMapper.insert(userKeys); - return userKeyMapper.selectByPrimaryKey(userKeys.getId()); + + LogDTO dto = LogDTOBuilder.builder() + .projectId(OperationLogConstants.SYSTEM) + .organizationId(OperationLogConstants.SYSTEM) + .type(OperationLogType.ADD.name()) + .module(OperationLogModule.PERSONAL_INFORMATION_APIKEYS) + .method(HttpMethodConstants.GET.name()) + .path("/user/api/key/add") + .sourceId(userKeys.getId()) + .content(userKeys.getAccessKey()) + .originalValue(JSON.toJSONBytes(userKeys)) + .build().getLogDTO(); + operationLogService.add(dto); } public void deleteUserKey(String id) { userKeyMapper.deleteByPrimaryKey(id); } - public void activeUserKey(String id) { + public void enableUserKey(String id) { UserKey userKeys = new UserKey(); userKeys.setId(id); userKeys.setEnable(true); @@ -78,8 +103,29 @@ public class UserKeyService { userKeyExample.createCriteria().andAccessKeyEqualTo(accessKey).andEnableEqualTo(true); List userKeysList = userKeyMapper.selectByExample(userKeyExample); if (!CollectionUtils.isEmpty(userKeysList)) { - return userKeysList.get(0); + //校验是否过期 + if (BooleanUtils.isTrue(userKeysList.get(0).getForever()) || userKeysList.get(0).getExpireTime() > System.currentTimeMillis()) { + return userKeysList.get(0); + } else { + throw new MSException(Translator.get("apikey_has_expired") + ": " + userKeysList.get(0).getAccessKey()); + } } return null; } + + public void updateUserKey(UserKeyDTO userKeyDTO) { + UserKey userKeys = new UserKey(); + userKeys.setId(userKeyDTO.getId()); + userKeys.setForever(userKeyDTO.getForever()); + if (BooleanUtils.isFalse(userKeyDTO.getForever())) { + if (userKeyDTO.getExpireTime() == null) { + throw new MSException(Translator.get("expire_time_not_null")); + } + userKeys.setExpireTime(userKeyDTO.getExpireTime()); + } else { + userKeys.setExpireTime(null); + } + userKeys.setDescription(userKeyDTO.getDescription()); + userKeyMapper.updateByPrimaryKeySelective(userKeys); + } } diff --git a/backend/services/system-setting/src/main/resources/permission.json b/backend/services/system-setting/src/main/resources/permission.json index c3c0ef6e3e..89adcaf938 100644 --- a/backend/services/system-setting/src/main/resources/permission.json +++ b/backend/services/system-setting/src/main/resources/permission.json @@ -290,5 +290,36 @@ ] } ] + }, + { + "id": "PERSONAL", + "name": "personal_settings", + "type": "PERSONAL", + "children": [ + { + "id": "MY_SETTINGS", + "name": "my_settings", + "children": [ + { + "id": "API_KEYS", + "name": "permission.project_member.read", + "permissions": [ + { + "id": "SYSTEM_PERSONAL_API_KEY_ADD:READ+ADD" + }, + { + "id": "SYSTEM_PERSONAL_API_KEY_ADD:READ" + }, + { + "id": "SYSTEM_PERSONAL_API_KEY_ADD:READ+UPDATE" + }, + { + "id": "SYSTEM_PERSONAL_API_KEY_ADD:READ+DELETE" + } + ] + } + ] + } + ] } ] diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserApiKeysControllerTests.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserApiKeysControllerTests.java new file mode 100644 index 0000000000..281f906ac5 --- /dev/null +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserApiKeysControllerTests.java @@ -0,0 +1,204 @@ +package io.metersphere.system.controller; + +import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.util.CodingUtils; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.base.BaseTest; +import io.metersphere.system.controller.handler.ResultHolder; +import io.metersphere.system.domain.UserKey; +import io.metersphere.system.domain.UserKeyExample; +import io.metersphere.system.dto.UserKeyDTO; +import io.metersphere.system.dto.sdk.SessionUser; +import io.metersphere.system.dto.user.UserDTO; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.mapper.UserKeyMapper; +import jakarta.annotation.Resource; +import org.apache.http.HttpHeaders; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.UUID; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class UserApiKeysControllerTests extends BaseTest { + + private static String userKeyId; + private final String BASE_URL = "/user/api/key"; + private final String ADD = BASE_URL + "/add"; + private final String DELETE = BASE_URL + "/delete/%s"; + private final String UPDATE = BASE_URL + "/update"; + private final String LIST = BASE_URL + "/list"; + private final String VALIDATE = BASE_URL + "/validate"; + private final String ENABLE = BASE_URL + "/enable/%s"; + private final String DISABLE = BASE_URL + "/disable/%s"; + @Resource + private MockMvc mockMvc; + + @Resource + private UserKeyMapper userKeyMapper; + + public static T parseObjectFromMvcResult(MvcResult mvcResult, Class parseClass) { + try { + String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); + //返回请求正常 + Assertions.assertNotNull(resultHolder); + return JSON.parseObject(JSON.toJSONString(resultHolder.getData()), parseClass); + } catch (Exception ignore) { + } + return null; + } + + + @Test + @Order(1) + public void testAdd() throws Exception { + requestGet(ADD); + UserKeyExample userKeyExample = new UserKeyExample(); + userKeyExample.createCriteria().andCreateUserEqualTo("admin"); + List userKeys = userKeyMapper.selectByExample(userKeyExample); + userKeyId = userKeys.get(0).getId(); + Assertions.assertEquals(1, userKeys.size()); + //校验日志 + checkLog(userKeys.get(0).getId(), OperationLogType.ADD); + //校验只能加五条的限制 + requestGet(ADD); + requestGet(ADD); + requestGet(ADD); + requestGet(ADD); + Assertions.assertEquals(5, userKeyMapper.countByExample(userKeyExample)); + requestGet(ADD, status().is5xxServerError()); + //用户不存在的 + UserDTO userDTO = new UserDTO(); + userDTO.setId("test-api-keys"); + SessionUser sessionUser = SessionUser.fromUser(userDTO, sessionId); + mockMvc.perform(MockMvcRequestBuilders.get(ADD).header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(SessionConstants.ATTR_USER, sessionUser) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + //校验权限 + requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_ADD, ADD); + + } + + @Test + @Order(2) + public void testDelete() throws Exception { + UserKeyExample userKeyExample = new UserKeyExample(); + userKeyExample.createCriteria().andCreateUserEqualTo("admin"); + List userKeys = userKeyMapper.selectByExample(userKeyExample); + //选一个id不等于userKeyId的 只要id + //取所有的id + List list = userKeys.stream().map(UserKey::getId).filter(id -> !id.equals(userKeyId)).toList(); + requestGet(String.format(DELETE, list.get(0))); + Assertions.assertEquals(4, userKeyMapper.countByExample(userKeyExample)); + //校验日志 + checkLog(list.get(0), OperationLogType.DELETE); + //校验权限 + requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_DELETE, DELETE); + } + + @Test + @Order(3) + public void testUpdate() throws Exception { + UserKeyDTO userKeyDTO = new UserKeyDTO(); + userKeyDTO.setId(userKeyId); + userKeyDTO.setDescription("test"); + userKeyDTO.setForever(true); + this.requestPost(UPDATE, userKeyDTO); + UserKey org = userKeyMapper.selectByPrimaryKey(userKeyId); + Assertions.assertEquals("test", org.getDescription()); + Assertions.assertEquals(true, org.getForever()); + userKeyDTO.setForever(false); + //到期时间为空 + this.requestPost(UPDATE, userKeyDTO, status().is5xxServerError()); + userKeyDTO.setExpireTime(System.currentTimeMillis() - 1000000); + this.requestPost(UPDATE, userKeyDTO); + + //校验日志 + checkLog(userKeyId, OperationLogType.UPDATE); + } + + @Test + @Order(4) + public void testList() throws Exception { + MvcResult mvcResult = this.requestGetAndReturn(LIST); + List userKeys = parseObjectFromMvcResult(mvcResult, List.class); + assert userKeys != null; + Assertions.assertEquals(4, userKeys.size()); + requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_READ, LIST); + } + + @Test + @Order(5) + public void testDisable() throws Exception { + requestGet(String.format(DISABLE, userKeyId)); + UserKey userKey = userKeyMapper.selectByPrimaryKey(userKeyId); + Assertions.assertEquals(false, userKey.getEnable()); + //校验日志 + checkLog(userKeyId, OperationLogType.UPDATE); + requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE, DISABLE); + } + + @Test + @Order(6) + public void testEnable() throws Exception { + requestGet(String.format(ENABLE, userKeyId)); + UserKey userKey = userKeyMapper.selectByPrimaryKey(userKeyId); + Assertions.assertEquals(true, userKey.getEnable()); + //校验日志 + checkLog(userKeyId, OperationLogType.UPDATE); + requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE, ENABLE); + } + + @Test + @Order(7) + public void testValidateError() throws Exception { + MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(VALIDATE); + UserKey userKey = userKeyMapper.selectByPrimaryKey(userKeyId); + String signature = CodingUtils.aesEncrypt(userKey.getAccessKey() + "|" + UUID.randomUUID().toString() + "|" + System.currentTimeMillis(), userKey.getSecretKey(), userKey.getAccessKey()); + requestBuilder + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN") + .header(SessionConstants.ACCESS_KEY, userKey.getAccessKey()) + .header(SessionConstants.SIGNATURE, signature); + mockMvc.perform(requestBuilder).andExpect(status().is5xxServerError()); + } + + @Test + @Order(9) + public void testValidate() throws Exception { + List userKeys = userKeyMapper.selectByExample(new UserKeyExample()); + List list = userKeys.stream().map(UserKey::getId).filter(id -> !id.equals(userKeyId)).toList(); + UserKey userKey1 = userKeyMapper.selectByPrimaryKey(list.get(0)); + MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(VALIDATE); + String signature = CodingUtils.aesEncrypt(userKey1.getAccessKey() + "|" + UUID.randomUUID().toString() + "|" + System.currentTimeMillis(), userKey1.getSecretKey(), userKey1.getAccessKey()); + requestBuilder + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN") + .header(SessionConstants.ACCESS_KEY, userKey1.getAccessKey()) + .header(SessionConstants.SIGNATURE, signature); + mockMvc.perform(requestBuilder).andExpect(status().isOk()); + + this.adminAuthInfo = null; + + } + + +}