diff --git a/backend/src/main/java/io/metersphere/service/GroupService.java b/backend/src/main/java/io/metersphere/service/GroupService.java index 8d7afcf3f0256ff5a1a62228a8efc5b2bf4c1428..d3b5f2e3bc57116d5aef8548824bd33b1a032b8a 100644 --- a/backend/src/main/java/io/metersphere/service/GroupService.java +++ b/backend/src/main/java/io/metersphere/service/GroupService.java @@ -262,7 +262,7 @@ public class GroupService { private List<GroupResourceDTO> getResourcePermission(List<GroupResource> resource, List<GroupPermission> permissions, String type, List<String> permissionList) { List<GroupResourceDTO> dto = new ArrayList<>(); - List<GroupResource> resources = resource.stream().filter(g -> g.getId().startsWith(type)).collect(Collectors.toList()); + List<GroupResource> resources = resource.stream().filter(g -> g.getId().startsWith(type)||g.getId().startsWith("PERSONAL")).collect(Collectors.toList()); permissions.forEach(p -> { if (permissionList.contains(p.getId())) { p.setChecked(true); diff --git a/backend/src/main/resources/db/migration/V107__v1.18_release.sql b/backend/src/main/resources/db/migration/V107__v1.18_release.sql index 047f938288194e3f8d936d955ef188ff600ef5db..7f93cdafb0476f7ef4b9c7270921f2df60275f2a 100644 --- a/backend/src/main/resources/db/migration/V107__v1.18_release.sql +++ b/backend/src/main/resources/db/migration/V107__v1.18_release.sql @@ -8,4 +8,46 @@ CREATE TABLE `operating_log_resource` PRIMARY KEY (`id`), KEY `operating_log_id_index` (`operating_log_id`), KEY `source_id_index` (`source_id`) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_general_ci; \ No newline at end of file +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_general_ci; + +-- permission +DROP PROCEDURE IF EXISTS test_personal; +DELIMITER // +CREATE PROCEDURE test_personal() + BEGIN + #澹版槑缁撴潫鏍囪瘑 + DECLARE end_flag int DEFAULT 0; + + DECLARE groupId varchar(64); + + #澹版槑娓告爣 group_curosr + DECLARE group_curosr CURSOR FOR SELECT DISTINCT group_id FROM user_group; + + #璁剧疆缁堟鏍囧織 + DECLARE CONTINUE HANDLER FOR NOT FOUND SET end_flag=1; + + #鎵撳紑娓告爣 + OPEN group_curosr; + + #閬嶅巻娓告爣 + REPEAT + #鑾峰彇褰撳墠娓告爣鎸囬拡璁板綍锛屽彇鍑哄€艰祴缁欒嚜瀹氫箟鐨勫彉閲� + FETCH group_curosr INTO groupId; + #鍒╃敤鍙栧埌鐨勫€艰繘琛屾暟鎹簱鐨勬搷浣� + INSERT INTO user_group_permission (id, group_id, permission_id, module_id) + VALUES (uuid(), groupId, 'PERSONAL_INFORMATION:READ+EDIT', 'PERSONAL_INFORMATION'), + (uuid(), groupId, 'PERSONAL_INFORMATION:READ+THIRD_ACCOUNT', 'PERSONAL_INFORMATION'), + (uuid(), groupId, 'PERSONAL_INFORMATION:READ+API_KEYS', 'PERSONAL_INFORMATION'), + (uuid(), groupId, 'PERSONAL_INFORMATION:READ+EDIT_PASSWORD', 'PERSONAL_INFORMATION'); + # 鏍规嵁 end_flag 鍒ゆ柇鏄惁缁撴潫 + UNTIL end_flag END REPEAT; + + #鍏抽棴娓告爣 + close group_curosr; + + END +// +DELIMITER ; + +CALL test_personal(); +DROP PROCEDURE IF EXISTS test_personal; diff --git a/backend/src/main/resources/permission.json b/backend/src/main/resources/permission.json index f336d5f0ed23d238dd50086a0e1763d5304579da..d75bbe03d0febc4a58c8822168cb9b5a89d94ec3 100644 --- a/backend/src/main/resources/permission.json +++ b/backend/src/main/resources/permission.json @@ -943,6 +943,30 @@ "name": "permission.project_error_report_library.delete", "resourceId": "PROJECT_ERROR_REPORT_LIBRARY", "license": true + }, + { + "id": "PERSONAL_INFORMATION:READ+EDIT", + "name": "permission.personal_information.personal_setting", + "resourceId": "PERSONAL_INFORMATION", + "license": true + }, + { + "id": "PERSONAL_INFORMATION:READ+API_KEYS", + "name": "permission.personal_information.api_keys", + "resourceId": "PERSONAL_INFORMATION", + "license": true + }, + { + "id": "PERSONAL_INFORMATION:READ+EDIT_PASSWORD", + "name": "permission.personal_information.edit_password", + "resourceId": "PERSONAL_INFORMATION", + "license": true + }, + { + "id": "PERSONAL_INFORMATION:READ+THIRD_ACCOUNT", + "name": "permission.personal_information.third_account", + "resourceId": "PERSONAL_INFORMATION", + "license": true } ], "resource": [ @@ -1101,5 +1125,10 @@ "id": "SYSTEM_PLUGIN", "name": "permission.system_plugin.name" }, + { + "id": "PERSONAL_INFORMATION", + "name": "permission.personal_information.name", + "license": true + } ] } \ No newline at end of file diff --git a/frontend/src/business/components/settings/components/PersonRouter.vue b/frontend/src/business/components/settings/components/PersonRouter.vue index 43e1de721520c3a34c1dce92ea1884a4ecd91fa4..e0f6e52a85a1b25b3919c44c0e0768a6c22413f0 100644 --- a/frontend/src/business/components/settings/components/PersonRouter.vue +++ b/frontend/src/business/components/settings/components/PersonRouter.vue @@ -1,23 +1,25 @@ <template> <div> <el-tabs v-model="activeIndex" > - <el-tab-pane v-for="menu in persons" :key = "menu.title" :name="menu.title" :label="$t(menu.title)" class="setting-item"></el-tab-pane> - <el-tab-pane name="change_password" :label="$t('member.edit_password')" class="setting-item"></el-tab-pane> - <el-tab-pane name="third_account" :label="$t('commons.third_account')" class="setting-item"></el-tab-pane> + <el-tab-pane name="commons.personal_setting" :label="$t('commons.personal_setting')" class="setting-item"></el-tab-pane> + <el-tab-pane name="commons.api_keys" :label="$t('commons.api_keys')" class="setting-item" ></el-tab-pane> + <el-tab-pane name="change_password" :label="$t('member.edit_password')" class="setting-item" ></el-tab-pane> + <el-tab-pane name="third_account" :label="$t('commons.third_account')" class="setting-item" ></el-tab-pane> </el-tabs> <ms-main-container> - <ms-person-from-setting v-if="activeIndex==='commons.personal_setting'" :form = form @getPlatformInfo = "getPlatformInfo" @cancel = "cancel" /> - <ms-api-keys v-if="activeIndex==='commons.api_keys'"/> - <password-info v-if="activeIndex==='change_password'" :rule-form = "ruleForm" @cancel="cancel"></password-info> - <el-form v-if="activeIndex==='third_account'"> - <jira-user-info @auth="handleAuth" v-if="hasJira" :data="currentPlatformInfo"/> - <tapd-user-info @auth="handleAuth" v-if="hasTapd" :data="currentPlatformInfo"/> - <zentao-user-info @auth="handleAuth" v-if="hasZentao" :data="currentPlatformInfo"/> - <azure-devops-user-info @auth="handleAuth" v-if="hasAzure" :data="currentPlatformInfo"/> - <el-form-item> + <ms-person-from-setting v-if="activeIndex==='commons.personal_setting'" :form = form @getPlatformInfo = "getPlatformInfo" @cancel = "cancel" /> + <ms-api-keys v-if="activeIndex==='commons.api_keys'" /> + <password-info v-if="activeIndex==='change_password'" :rule-form = "ruleForm" @cancel="cancel" ></password-info> + <el-form v-if="activeIndex==='third_account'" > + <jira-user-info @auth="handleAuth" v-if="hasJira" :data="currentPlatformInfo" v-permission="['PERSONAL_INFORMATION:READ+THIRD_ACCOUNT']"/> + <tapd-user-info @auth="handleAuth" v-if="hasTapd" :data="currentPlatformInfo" v-permission="['PERSONAL_INFORMATION:READ+THIRD_ACCOUNT']"/> + <zentao-user-info @auth="handleAuth" v-if="hasZentao" :data="currentPlatformInfo" v-permission="['PERSONAL_INFORMATION:READ+THIRD_ACCOUNT']"/> + <azure-devops-user-info @auth="handleAuth" v-if="hasAzure" :data="currentPlatformInfo" v-permission="['PERSONAL_INFORMATION:READ+THIRD_ACCOUNT']"/> + <el-form-item v-permission="['PERSONAL_INFORMATION:READ+THIRD_ACCOUNT']" v-if="hasJira||hasTapd||hasZentao||hasAzure"> <el-button @click="cancel">{{$t('commons.cancel')}}</el-button> <el-button type="primary" @click="updateUser('updateUserForm')" @keydown.enter.native.prevent>{{$t('commons.confirm')}}</el-button> </el-form-item> + <div v-if="!isShowText||(!hasJira&&!hasTapd&&!hasZentao&&!hasAzure)" style="width: 6%;margin: auto">{{$t('commons.no_permission')}}</div> </el-form> </ms-main-container> @@ -31,13 +33,13 @@ import MsApiKeys from "@/business/components/settings/personal/ApiKeys"; import MsMainContainer from "@/business/components/common/components/MsMainContainer"; import PasswordInfo from "@/business/components/settings/personal/PasswordInfo"; - import {getCurrentUser, getCurrentWorkspaceId} from "@/common/js/utils"; + import {getCurrentUser, getCurrentWorkspaceId, hasPermission} from "@/common/js/utils"; import ZentaoUserInfo from "@/business/components/settings/personal/ZentaoUserInfo"; import TapdUserInfo from "@/business/components/settings/personal/TapdUserInfo"; import JiraUserInfo from "@/business/components/settings/personal/JiraUserInfo"; import AzureDevopsUserInfo from "@/business/components/settings/personal/AzureDevopsUserInfo"; import {getIntegrationService} from "@/network/organization"; - import {TokenKey} from "@/common/js/constants"; + import {AZURE_DEVOPS, JIRA, TAPD, TokenKey, ZEN_TAO} from "@/common/js/constants"; export default { name: "MsPersonRouter", @@ -61,9 +63,10 @@ return menus; }; return{ - persons: getMenus('person'), + //persons: getMenus('person'), activeIndex: 'commons.personal_setting', ruleForm:{}, + isShowText:false, hasJira: false, hasTapd: false, hasZentao: false, @@ -187,7 +190,10 @@ }, }, created() { + this.isShowText = hasPermission('PERSONAL_INFORMATION:READ+THIRD_ACCOUNT'); this.initTableData(); + + } } diff --git a/frontend/src/business/components/settings/personal/ApiKeys.vue b/frontend/src/business/components/settings/personal/ApiKeys.vue index d9212398ec68d0d5cfff48b238a57e27c79650fc..a1a1f87c21b997ce39e8cd4cd46de801e93fcb62 100644 --- a/frontend/src/business/components/settings/personal/ApiKeys.vue +++ b/frontend/src/business/components/settings/personal/ApiKeys.vue @@ -1,6 +1,6 @@ <template> <div v-loading="result.loading"> - <el-card class="table-card"> + <el-card class="table-card" v-permission="['PERSONAL_INFORMATION:READ+API_KEYS']"> <template v-slot:header> <div> <el-row class="table-title" type="flex" justify="space-between" align="middle"> @@ -66,21 +66,14 @@ </el-table-column> </el-table> </el-card> -<!-- <el-dialog title="Secret Key" :visible.sync="apiKeysVisible"> - <div class="variable"> - {{ currentRow.secretKey }} - <el-tooltip :content="$t('api_test.copied')" manual v-model="currentRow.visible2" placement="top" - :visible-arrow="false"> - <i class="el-icon-copy-document copy" @click="copy(currentRow, 'secretKey', 'visible2')"/> - </el-tooltip> - </div> - </el-dialog>--> + <div v-if="!isShowText" style="width: 6%;margin: auto">{{$t('commons.no_permission')}}</div> + </div> </template> <script> import MsDialogFooter from "../../common/components/MsDialogFooter"; -import {getCurrentUser} from "../../../../common/js/utils"; +import {getCurrentUser, hasPermission} from "../../../../common/js/utils"; import MsTableOperatorButton from "../../common/components/MsTableOperatorButton"; import MsTableHeader from "../../common/components/MsTableHeader"; @@ -90,6 +83,7 @@ export default { data() { return { result: {}, + isShowText:false, updateVisible: false, editPasswordVisible: false, apiKeysVisible: false, @@ -100,6 +94,7 @@ export default { }, created() { + this.isShowText = hasPermission('PERSONAL_INFORMATION:READ+API_KEYS'); this.search(); }, diff --git a/frontend/src/business/components/settings/personal/PasswordInfo.vue b/frontend/src/business/components/settings/personal/PasswordInfo.vue index 8fc9eccd974e00dcec5fa6109242349289040145..8fa24af86f24feffdc37135d0e40784392cf5381 100644 --- a/frontend/src/business/components/settings/personal/PasswordInfo.vue +++ b/frontend/src/business/components/settings/personal/PasswordInfo.vue @@ -1,28 +1,33 @@ <template> - <el-form :model="ruleForm" :rules="rules" ref="editPasswordForm" label-width="120px" class="demo-ruleForm"> - <el-form-item :label="$t('member.old_password')" prop="password" style="margin-bottom: 29px"> - <el-input v-model="ruleForm.password" autocomplete="off" show-password/> - </el-form-item> - <el-form-item :label="$t('member.new_password')" prop="newpassword"> - <el-input v-model="ruleForm.newpassword" autocomplete="off" show-password/> - </el-form-item> - <el-form-item :label="$t('member.repeat_password')" prop="repeatPassword"> - <el-input v-model="ruleForm.repeatPassword" autocomplete="off" show-password/> - </el-form-item> - <el-form-item> - <el-button @click="cancel">{{$t('commons.cancel')}}</el-button> - <el-button type="primary" @click="updatePassword('editPasswordForm')" @keydown.enter.native.prevent>{{$t('commons.confirm')}}</el-button> - </el-form-item> - </el-form> + <div> + <el-form :model="ruleForm" :rules="rules" ref="editPasswordForm" label-width="120px" class="demo-ruleForm" v-permission="['PERSONAL_INFORMATION:READ+EDIT_PASSWORD']"> + <el-form-item :label="$t('member.old_password')" prop="password" style="margin-bottom: 29px"> + <el-input v-model="ruleForm.password" autocomplete="off" show-password/> + </el-form-item> + <el-form-item :label="$t('member.new_password')" prop="newpassword"> + <el-input v-model="ruleForm.newpassword" autocomplete="off" show-password/> + </el-form-item> + <el-form-item :label="$t('member.repeat_password')" prop="repeatPassword"> + <el-input v-model="ruleForm.repeatPassword" autocomplete="off" show-password/> + </el-form-item> + <el-form-item> + <el-button @click="cancel">{{$t('commons.cancel')}}</el-button> + <el-button type="primary" @click="updatePassword('editPasswordForm')" @keydown.enter.native.prevent>{{$t('commons.confirm')}}</el-button> + </el-form-item> + </el-form> + <div v-if="!isShowText" style="width: 6%;margin: auto">{{$t('commons.no_permission')}}</div> + </div> </template> <script> import {logout} from "@/network/user"; +import {hasPermission} from "@/common/js/utils"; export default { name:'PasswordInfo', data(){ return{ result:{}, + isShowText:false, updatePasswordPath: '/user/update/password', rules: { password: [ @@ -79,6 +84,9 @@ export default { } }); }, + }, + created() { + this.isShowText = hasPermission('PERSONAL_INFORMATION:READ+EDIT_PASSWORD'); } } diff --git a/frontend/src/business/components/settings/personal/PersonFromSetting.vue b/frontend/src/business/components/settings/personal/PersonFromSetting.vue index 48013831548ea32eb8c13d4696c79fbb326a5f2a..17d969b9905c98c85c56def1bbd8bae4ef3f6050 100644 --- a/frontend/src/business/components/settings/personal/PersonFromSetting.vue +++ b/frontend/src/business/components/settings/personal/PersonFromSetting.vue @@ -1,7 +1,7 @@ <template> <div v-loading="result.loading"> <el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule" - ref="updateUserForm"> + ref="updateUserForm" v-permission="['PERSONAL_INFORMATION:READ+EDIT']"> <el-form-item label="ID" prop="id"> <el-input v-model="form.id" autocomplete="off" :disabled="true"/> </el-form-item> @@ -14,21 +14,12 @@ <el-form-item :label="$t('commons.phone')" prop="phone"> <el-input v-model="form.phone" autocomplete="off"/> </el-form-item> -<!-- <el-form-item label="鎵€灞炲伐浣滅┖闂�" v-if="workspaceList.length>0"> - <span v-for="(item,index) in workspaceList" :key = item.id > - <span>{{item.name}}</span><span v-if="index<workspaceList.length-1"> | </span> - </span> - </el-form-item> - <el-form-item label="鎵€灞炲伐浣滈」鐩�" v-if ="projectList.length>0"> - <span v-for="(item,index) in projectList" :key = item.id > - <span>{{item.name}}</span><span v-if="index<projectList.length-1"> | </span> - </span> - </el-form-item>--> <el-form-item> <el-button @click="cancel">{{$t('commons.cancel')}}</el-button> <el-button type="primary" @click="updateUser('updateUserForm')" @keydown.enter.native.prevent>{{$t('commons.confirm')}}</el-button> </el-form-item> </el-form> + <div v-if="!isShowText" style="width: 6%;margin: auto">{{$t('commons.no_permission')}}</div> </div> </template> @@ -38,7 +29,7 @@ import MsDialogFooter from "../../common/components/MsDialogFooter"; import { fullScreenLoading, getCurrentProjectID, getCurrentUser, - getCurrentWorkspaceId, + getCurrentWorkspaceId, hasPermission, listenGoBack, removeGoBackListener, saveLocalStorage, stopFullScreenLoading } from "@/common/js/utils"; @@ -67,6 +58,7 @@ export default { return { result: {}, isLocalUser: false, + isShowText:false, updatePath: '/user/update/current', ruleForm: {}, rule: { @@ -101,7 +93,7 @@ export default { }, created() { - + this.isShowText = hasPermission('PERSONAL_INFORMATION:READ+EDIT'); }, methods: { currentUser: () => { diff --git a/frontend/src/common/js/table-constants.js b/frontend/src/common/js/table-constants.js index 9857a38f4aea983b3548c1c15b0d5b8fc5189a6d..c606d67602ddbf09bd0b0822835fa470130db73b 100644 --- a/frontend/src/common/js/table-constants.js +++ b/frontend/src/common/js/table-constants.js @@ -128,7 +128,8 @@ export function API_SCENARIO_FILTERS () { export const USER_GROUP_SCOPE = { 'SYSTEM': 'group.system', 'WORKSPACE': 'group.workspace', - 'PROJECT': 'group.project' + 'PROJECT': 'group.project', + 'PERSONAL': 'group.personal' } export const PROJECT_GROUP_SCOPE = { diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 67b7e554a5f775085786df6541944673897ce56c..6a0d1c5eed7f4def0a3ea6f9cd9841639e928bf3 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -1,6 +1,7 @@ export default { commons: { project_permission: 'Please add the project permission first', + no_permission:'No permission yet', failure_continues: "Failure continues", full_screen_editing: "Full screen editing", trash: "Trash", @@ -861,6 +862,7 @@ export default { select_type: 'please select type', view_permission: 'view permission', system: 'System', + personal: 'Personal Information', organization: 'Organization', workspace: 'Workspace', project: 'Project', @@ -3002,6 +3004,13 @@ export default { delete: "DELETE", read: "READ", }, + personal_information:{ + name:'Setting', + personal_setting: 'Personal Setting', + api_keys: 'API Keys', + edit_password: "EDIT PASSWORD", + third_account: 'Third Account', + }, other: { track: "Track", api: "API", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 774f20121926bed3a71925cce9e68b3781b93a7f..3d96d192a340a9aa36535978d0efe0f2e4e7881c 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -1,6 +1,7 @@ export default { commons: { project_permission: '璇峰厛娣诲姞璇ラ」鐩潈闄�', + no_permission:'鏆傛棤鏉冮檺', failure_continues: "澶辫触缁х画", full_screen_editing: "鍏ㄥ睆缂栬緫", trash: "鍥炴敹绔�", @@ -865,6 +866,7 @@ export default { admin_not_allow_delete: '绯荤粺鐢ㄦ埛缁勪笉鏀寔鍒犻櫎!', select_type: '璇烽€夋嫨鎵€灞炵被鍨�', system: '绯荤粺', + personal: '涓汉淇℃伅', organization: '缁勭粐', workspace: '宸ヤ綔绌洪棿', project: '椤圭洰', @@ -3006,12 +3008,19 @@ export default { delete: "鍒犻櫎", read: "鏌ョ湅鑴氭湰", }, + personal_information:{ + name:'璁剧疆', + personal_setting: '涓汉璁剧疆', + api_keys: 'API Keys', + edit_password: "淇敼瀵嗙爜", + third_account: '绗笁鏂瑰钩鍙拌处鍙�', + }, other: { track: "娴嬭瘯璺熻釜", api: "鎺ュ彛娴嬭瘯", performance: "鎬ц兘娴嬭瘯", project: "椤圭洰璁剧疆", - report: "鎶ヨ〃缁熻" + report: "鎶ヨ〃缁熻", } }, env_options: { diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 23afe4db107c501d3640b3359ff1909501ee88b7..f9fb95b9cd9c9b8ce2b27b219a894593090b2e0e 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -1,6 +1,7 @@ export default { commons: { project_permission: '璜嬪厛娣诲姞瑭查爡鐩瑠闄�', + no_permission:'鏆劇娆婇檺', failure_continues: "澶辨晽绻肩簩", full_screen_editing: "鍏ㄥ睆绶ㄨ集", trash: "鍥炴敹绔�", @@ -865,6 +866,7 @@ export default { admin_not_allow_delete: '绯荤当鐢ㄦ埗绲勪笉鏀寔鍒櫎!', select_type: '璜嬮伕鎿囨墍灞鍨�', system: '绯荤当', + personal: '鍊嬩汉淇℃伅', organization: '绲勭箶', workspace: '宸ヤ綔绌洪枔', project: '闋呯洰', @@ -3005,6 +3007,13 @@ export default { delete: "鍒櫎", read: "鏌ョ湅鑵虫湰", }, + personal_information:{ + name:'瑷疆', + personal_setting: '鍊嬩汉瑷疆', + api_keys: 'API Keys', + edit_password: "淇敼瀵嗙⒓", + third_account: '绗笁鏂瑰钩鑷鸿超铏�', + }, other: { track: "娓│璺熻工", api: "鎺ュ彛娓│",