refactor(系统设置): 优化apiKey

This commit is contained in:
wxg0103 2024-04-26 11:25:44 +08:00 committed by 刘瑞斌
parent 4f5463c01a
commit 28b759dedb
10 changed files with 102 additions and 17 deletions

View File

@ -0,0 +1,9 @@
-- set innodb lock wait timeout
SET SESSION innodb_lock_wait_timeout = 7200;
ALTER TABLE user_key MODIFY COLUMN description VARCHAR(1000);
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -530,3 +530,5 @@ api_import_schedule=接口定义-定时导入任务
project.description.length_range=项目描述长度必须在{min}和{max}之间 project.description.length_range=项目描述长度必须在{min}和{max}之间
api_test_environment_datasource_connect_failed=数据源连接失败 api_test_environment_datasource_connect_failed=数据源连接失败
ms_url_not_available=资源池无法访问当前站点 ms_url_not_available=资源池无法访问当前站点
api_key_not_exist=ApiKey不存在
current_user_can_not_operation_api_key=当前用户无操作该ApiKey的权限

View File

@ -568,3 +568,5 @@ api_test_environment_datasource_connect_failed=Data source connection failed
permission.api_definition.delete_and_recover=Delete/Recover permission.api_definition.delete_and_recover=Delete/Recover
permission.service_integration.reset=Reset permission.service_integration.reset=Reset
ms_url_not_available=The resource pool cannot access the current site ms_url_not_available=The resource pool cannot access the current site
api_key_not_exist=API key does not exist
current_user_can_not_operation_api_key=The current user cannot operate the API key

View File

@ -565,3 +565,5 @@ api_test_environment_datasource_connect_failed=数据源连接失败
permission.api_definition.delete_and_recover=删除/恢复 permission.api_definition.delete_and_recover=删除/恢复
permission.service_integration.reset=重置 permission.service_integration.reset=重置
ms_url_not_available=资源池无法访问当前站点 ms_url_not_available=资源池无法访问当前站点
api_key_not_exist=ApiKey不存在
current_user_can_not_operation_api_key=当前用户无操作该ApiKey的权限

View File

@ -565,3 +565,5 @@ project.description.length_range=項目描述長度必須在{min}和{max}之間
permission.api_definition.delete_and_recover=刪除/恢復 permission.api_definition.delete_and_recover=刪除/恢復
permission.service_integration.reset=重置 permission.service_integration.reset=重置
ms_url_not_available=資源池無法訪問當前站點 ms_url_not_available=資源池無法訪問當前站點
api_key_not_exist=ApiKey不存在
current_user_can_not_operation_api_key=當前用戶無法操作ApiKey

View File

@ -54,6 +54,7 @@ public class UserApiKeysController {
@Operation(summary = "系统设置-个人中心-我的设置-Api Keys-删除Api Keys") @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-删除Api Keys")
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = UserKeyLogService.class) @Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = UserKeyLogService.class)
public void delete(@PathVariable String id) { public void delete(@PathVariable String id) {
userKeyService.checkUserKeyOwner(id, SessionUtils.getUserId());
userKeyService.deleteUserKey(id); userKeyService.deleteUserKey(id);
} }
@ -62,22 +63,25 @@ public class UserApiKeysController {
@RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE) @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = UserKeyLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = UserKeyLogService.class)
public void update(@Validated @RequestBody UserKeyDTO request) { public void update(@Validated @RequestBody UserKeyDTO request) {
userKeyService.checkUserKeyOwner(request.getId(), SessionUtils.getUserId());
userKeyService.updateUserKey(request); userKeyService.updateUserKey(request);
} }
@GetMapping("/enable/{id}") @GetMapping("/enable/{id}")
@Operation(summary = "系统设置-个人中心-我的设置-Api Keys-开启Api Keys") @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-开启Api Keys")
@RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE) @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.enableLog(#id)", msClass = UserKeyLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.enableLog(#id)", msClass = UserKeyLogService.class)
public void enable(@PathVariable String id) { public void enable(@PathVariable String id) {
userKeyService.checkUserKeyOwner(id, SessionUtils.getUserId());
userKeyService.enableUserKey(id); userKeyService.enableUserKey(id);
} }
@GetMapping("/disable/{id}") @GetMapping("/disable/{id}")
@Operation(summary = "系统设置-个人中心-我的设置-Api Keys-关闭Api Keys") @Operation(summary = "系统设置-个人中心-我的设置-Api Keys-关闭Api Keys")
@RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE) @RequiresPermissions(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.disableLog(#id)", msClass = UserKeyLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.disableLog(#id)", msClass = UserKeyLogService.class)
public void disabledUserKey(@PathVariable String id) { public void disabledUserKey(@PathVariable String id) {
userKeyService.checkUserKeyOwner(id, SessionUtils.getUserId());
userKeyService.disableUserKey(id); userKeyService.disableUserKey(id);
} }
} }

View File

@ -19,6 +19,7 @@ import io.metersphere.system.mapper.UserKeyMapper;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -81,10 +82,12 @@ public class UserKeyService {
} }
public void deleteUserKey(String id) { public void deleteUserKey(String id) {
checkUserKey(id);
userKeyMapper.deleteByPrimaryKey(id); userKeyMapper.deleteByPrimaryKey(id);
} }
public void enableUserKey(String id) { public void enableUserKey(String id) {
checkUserKey(id);
UserKey userKeys = new UserKey(); UserKey userKeys = new UserKey();
userKeys.setId(id); userKeys.setId(id);
userKeys.setEnable(true); userKeys.setEnable(true);
@ -92,6 +95,7 @@ public class UserKeyService {
} }
public void disableUserKey(String id) { public void disableUserKey(String id) {
checkUserKey(id);
UserKey userKeys = new UserKey(); UserKey userKeys = new UserKey();
userKeys.setId(id); userKeys.setId(id);
userKeys.setEnable(false); userKeys.setEnable(false);
@ -109,18 +113,33 @@ public class UserKeyService {
} }
public void updateUserKey(UserKeyDTO userKeyDTO) { public void updateUserKey(UserKeyDTO userKeyDTO) {
UserKey userKeys = new UserKey(); UserKey userKey = checkUserKey(userKeyDTO.getId());
userKeys.setId(userKeyDTO.getId()); userKey.setId(userKeyDTO.getId());
userKeys.setForever(userKeyDTO.getForever()); userKey.setForever(userKeyDTO.getForever());
if (BooleanUtils.isFalse(userKeyDTO.getForever())) { if (BooleanUtils.isFalse(userKeyDTO.getForever())) {
if (userKeyDTO.getExpireTime() == null) { if (userKeyDTO.getExpireTime() == null) {
throw new MSException(Translator.get("expire_time_not_null")); throw new MSException(Translator.get("expire_time_not_null"));
} }
userKeys.setExpireTime(userKeyDTO.getExpireTime()); userKey.setExpireTime(userKeyDTO.getExpireTime());
} else { } else {
userKeys.setExpireTime(null); userKey.setExpireTime(null);
}
userKey.setDescription(userKeyDTO.getDescription());
userKeyMapper.updateByPrimaryKeySelective(userKey);
}
public UserKey checkUserKey(String id) {
UserKey userKey = userKeyMapper.selectByPrimaryKey(id);
if (userKey == null) {
throw new MSException(Translator.get("api_key_not_exist"));
}
return userKey;
}
public void checkUserKeyOwner(String id, String userId) {
UserKey userKey = checkUserKey(id);
if (!StringUtils.equals(userKey.getCreateUser(), userId)) {
throw new MSException(Translator.get("current_user_can_not_operation_api_key"));
} }
userKeys.setDescription(userKeyDTO.getDescription());
userKeyMapper.updateByPrimaryKeySelective(userKeys);
} }
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.system.controller; package io.metersphere.system.controller;
import com.jayway.jsonpath.JsonPath;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.SessionConstants; import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.util.CodingUtils; import io.metersphere.sdk.util.CodingUtils;
@ -19,6 +20,8 @@ import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
@ -28,6 +31,7 @@ import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ -65,6 +69,9 @@ public class UserApiKeysControllerTests extends BaseTest {
@Test @Test
@Order(1) @Order(1)
@Sql(scripts = {"/dml/init_project.sql"},
config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED),
executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public void testAdd() throws Exception { public void testAdd() throws Exception {
requestGet(ADD); requestGet(ADD);
UserKeyExample userKeyExample = new UserKeyExample(); UserKeyExample userKeyExample = new UserKeyExample();
@ -108,6 +115,8 @@ public class UserApiKeysControllerTests extends BaseTest {
Assertions.assertEquals(4, userKeyMapper.countByExample(userKeyExample)); Assertions.assertEquals(4, userKeyMapper.countByExample(userKeyExample));
//校验日志 //校验日志
checkLog(list.get(0), OperationLogType.DELETE); checkLog(list.get(0), OperationLogType.DELETE);
//处理不存在的
requestGet(String.format(DELETE, UUID.randomUUID().toString()), status().is5xxServerError());
//校验权限 //校验权限
requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_DELETE, DELETE); requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_DELETE, DELETE);
} }
@ -128,7 +137,13 @@ public class UserApiKeysControllerTests extends BaseTest {
this.requestPost(UPDATE, userKeyDTO, status().is5xxServerError()); this.requestPost(UPDATE, userKeyDTO, status().is5xxServerError());
userKeyDTO.setExpireTime(System.currentTimeMillis() - 1000000); userKeyDTO.setExpireTime(System.currentTimeMillis() - 1000000);
this.requestPost(UPDATE, userKeyDTO); this.requestPost(UPDATE, userKeyDTO);
//描述为空
userKeyDTO.setDescription(null);
this.requestPost(UPDATE, userKeyDTO);
//校验不存在的
userKeyDTO.setId(UUID.randomUUID().toString());
this.requestPost(UPDATE, userKeyDTO, status().is5xxServerError());
//校验日志 //校验日志
checkLog(userKeyId, OperationLogType.UPDATE); checkLog(userKeyId, OperationLogType.UPDATE);
} }
@ -151,6 +166,24 @@ public class UserApiKeysControllerTests extends BaseTest {
Assertions.assertEquals(false, userKey.getEnable()); Assertions.assertEquals(false, userKey.getEnable());
//校验日志 //校验日志
checkLog(userKeyId, OperationLogType.UPDATE); checkLog(userKeyId, OperationLogType.UPDATE);
//校验不存在的
requestGet(String.format(DISABLE, UUID.randomUUID().toString()), status().is5xxServerError());
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login")
.content(String.format("{\"username\":\"%s\",\"password\":\"%s\"}", "test-user-key", "test-user-key@metersphere.io"))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
String sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
String csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
mockMvc.perform(MockMvcRequestBuilders.get(String.format(DISABLE, userKeyId))
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE, DISABLE); requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE, DISABLE);
} }
@ -162,6 +195,8 @@ public class UserApiKeysControllerTests extends BaseTest {
Assertions.assertEquals(true, userKey.getEnable()); Assertions.assertEquals(true, userKey.getEnable());
//校验日志 //校验日志
checkLog(userKeyId, OperationLogType.UPDATE); checkLog(userKeyId, OperationLogType.UPDATE);
//校验不存在的
requestGet(String.format(ENABLE, UUID.randomUUID().toString()), status().is5xxServerError());
requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE, ENABLE); requestGetPermissionTest(PermissionConstants.SYSTEM_PERSONAL_API_KEY_UPDATE, ENABLE);
} }

View File

@ -43,3 +43,13 @@ values ('resourcePoolId1', 'resourcePoolName1', 'node', 'resourcePoolDescription
replace into project_test_resource_pool(project_id, test_resource_pool_id) value ('projectId', 'resourcePoolId'); replace into project_test_resource_pool(project_id, test_resource_pool_id) value ('projectId', 'resourcePoolId');
replace into project_test_resource_pool(project_id, test_resource_pool_id) value ('projectId', 'resourcePoolId1'); replace into project_test_resource_pool(project_id, test_resource_pool_id) value ('projectId', 'resourcePoolId1');
replace into test_resource_pool_organization(id , test_resource_pool_id, org_id) value (UUID_SHORT(),'resourcePoolId', '100001'); replace into test_resource_pool_organization(id , test_resource_pool_id, org_id) value (UUID_SHORT(),'resourcePoolId', '100001');
replace into user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source,
last_project_id, create_user, update_user)
VALUES ('test-user-key', 'test-user-key', 'test-user-key@metersphere.io', MD5('test-user-key@metersphere.io'),
UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, '100001', '', 'LOCAL', '100001100001', 'admin', 'admin');
replace INTO user_role_relation(id, user_id, role_id, source_id, organization_id, create_time, create_user)
VALUES ('test-user-key', 'test-user-key', 'admin', 'system',
'system', '1684747668375', 'admin');

View File

@ -51,7 +51,7 @@
v-if="item.showDescInput" v-if="item.showDescInput"
v-model:model-value="item.description" v-model:model-value="item.description"
:placeholder="t('common.pleaseInput')" :placeholder="t('common.pleaseInput')"
:max-length="255" :max-length="1000"
@blur="handleDescChange(item)" @blur="handleDescChange(item)"
></a-textarea> ></a-textarea>
<div v-else class="desc-line api-item-value"> <div v-else class="desc-line api-item-value">
@ -144,11 +144,11 @@
/> />
</a-form-item> </a-form-item>
<a-form-item field="desc" :label="t('ms.personal.accessKeyDesc')"> <a-form-item field="desc" :label="t('ms.personal.accessKeyDesc')">
<a-input <a-textarea
v-model:model-value="timeForm.desc" v-model:model-value="timeForm.desc"
:max-length="255"
:placeholder="t('ms.personal.accessKeyDescPlaceholder')" :placeholder="t('ms.personal.accessKeyDescPlaceholder')"
/> :max-length="1000"
></a-textarea>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>