feat(系统设置): 组织日志接口&认证配置测试接口&表单密码框禁止自动填充

This commit is contained in:
baiqi 2023-08-23 16:05:35 +08:00 committed by 刘瑞斌
parent 3c38b27c1a
commit 6f996e991b
17 changed files with 298 additions and 64 deletions

View File

@ -13,6 +13,8 @@ import {
UpdateAuthStatusUrl,
AddAuthUrl,
DeleteAuthUrl,
TestLdapConnectUrl,
TestLdapLoginUrl,
} from '@/api/requrls/setting/config';
import type { CommonList, TableQueryParams } from '@/models/common';
@ -26,6 +28,8 @@ import type {
AuthItem,
AuthParams,
UpdateAuthStatusParams,
LDAPConfig,
LDAPConnectConfig,
} from '@/models/setting/config';
// 测试邮箱连接
@ -92,3 +96,13 @@ export function updateAuthStatus(data: UpdateAuthStatusParams) {
export function deleteAuth(id: string) {
return MSR.get({ url: DeleteAuthUrl, params: id });
}
// 测试ldap连接
export function testLdapConnect(data: LDAPConnectConfig) {
return MSR.post({ url: TestLdapConnectUrl, data });
}
// 测试ldap登录
export function testLdapLogin(data: LDAPConfig) {
return MSR.post({ url: TestLdapLoginUrl, data });
}

View File

@ -1,20 +1,42 @@
import MSR from '@/api/http/index';
import { GetLogListUrl, GetLogOptionsUrl, GetLogUserUrl } from '@/api/requrls/setting/log';
import {
GetSystemLogListUrl,
GetSystemLogOptionsUrl,
GetSystemLogUserUrl,
GetOrgLogListUrl,
GetOrgLogOptionsUrl,
GetOrgLogUserUrl,
} from '@/api/requrls/setting/log';
import type { CommonList } from '@/models/common';
import type { LogOptions, LogItem, UserItem } from '@/models/setting/log';
// 获取日志列表
export function getLogList(data: any) {
return MSR.post<CommonList<LogItem>>({ url: GetLogListUrl, data });
// 获取系统日志列表
export function getSystemLogList(data: any) {
return MSR.post<CommonList<LogItem>>({ url: GetSystemLogListUrl, data });
}
// 获取日志操作范围选项
export function getLogOptions() {
return MSR.get<LogOptions>({ url: GetLogOptionsUrl });
// 获取系统日志-操作范围选项
export function getSystemLogOptions() {
return MSR.get<LogOptions>({ url: GetSystemLogOptionsUrl });
}
// 获取日志操作用户列表
export function getLogUsers() {
return MSR.get<UserItem[]>({ url: GetLogUserUrl });
// 获取系统日志-操作用户列表
export function getSystemLogUsers() {
return MSR.get<UserItem[]>({ url: GetSystemLogUserUrl });
}
// 获取组织日志列表
export function getOrgLogList(data: any) {
return MSR.post<CommonList<LogItem>>({ url: GetOrgLogListUrl, data });
}
// 获取组织日志-操作范围选项
export function getOrgLogOptions(id: string) {
return MSR.get<LogOptions>({ url: GetOrgLogOptionsUrl, params: id });
}
// 获取组织日志-操作用户列表
export function getOrgLogUsers(id: string) {
return MSR.get<UserItem[]>({ url: GetOrgLogUserUrl, params: id });
}

View File

@ -1,10 +0,0 @@
import MSR from '@/api/http/index';
import { OrganizationListItem } from '@/models/setting/orgnization';
import { GetAllOrgUrl } from '@/api/requrls/setting/orgnization';
// 获取全部组织列表
export function getAllOrgList() {
return MSR.post<OrganizationListItem[]>({ url: GetAllOrgUrl });
}
export function other() {}

View File

@ -24,6 +24,11 @@ export const AddAuthUrl = '/system/authsource/add';
export const GetAuthDetailUrl = '/system/authsource/get';
// 删除认证源
export const DeleteAuthUrl = '/system/authsource/delete';
// 测试ldap连接
export const TestLdapConnectUrl = '/system/authsource/ldap/test-connect';
// 测试ldap登录
export const TestLdapLoginUrl = '/system/authsource/ldap/test-login';
// 获取系统主页左上角图片
export const GetTitleImgUrl = `${import.meta.env.VITE_API_BASE_URL}/base-display/get/logo-platform`;
// 获取登录 logo

View File

@ -1,3 +1,9 @@
export const GetLogListUrl = '/operation/log/list';
export const GetLogOptionsUrl = '/operation/log/get/options'; // 获取组织/项目级联下拉框选项
export const GetLogUserUrl = '/operation/log/user/list'; // 搜索操作用户
// 系统级别日志
export const GetSystemLogListUrl = '/operation/log/list';
export const GetSystemLogOptionsUrl = '/operation/log/get/options'; // 获取组织/项目级联下拉框选项
export const GetSystemLogUserUrl = '/operation/log/user/list'; // 搜索操作用户
// 组织级别日志
export const GetOrgLogListUrl = '/organization/log/list';
export const GetOrgLogOptionsUrl = '/organization/log/get/options'; // 获取组织/项目级联下拉框选项
export const GetOrgLogUserUrl = '/organization/log/user/list'; // 搜索操作用户

View File

@ -1,2 +0,0 @@
export const GetAllOrgUrl = '/system/organization/option/all';
export const Other = '';

View File

@ -33,6 +33,7 @@ export default function usePathMap() {
/**
* key routeQuery routeQuery
* TODO: 权限校验待补充
* @param key
*/
const jumpRouteByMapKey = (key: typeof RouteEnum, routeQuery?: Record<string, any>) => {

View File

@ -130,3 +130,17 @@ export interface UpdateAuthStatusParams {
id: string;
enable: boolean;
}
// ldap 连接配置
export interface LDAPConnectConfig {
ldapUrl: string;
ldapDn: string;
ldapPassword: string;
}
// ldap登录配置
export interface LDAPConfig extends LDAPConnectConfig {
username: string;
password: string;
ldapUserFilter: string;
ldapUserOu: string;
ldapUserMapping: string;
}

View File

@ -61,10 +61,7 @@ const useUserStore = defineStore('user', {
const res = await userLogin(loginForm);
setToken(res.sessionId, res.csrfToken);
const appStore = useAppStore();
if (appStore.currentOrgId === '') {
// 第一次进系统才设置组织 ID后续已经持久化存储了
appStore.setCurrentOrgId(res.lastOrganizationId || '');
}
appStore.setCurrentOrgId(res.lastOrganizationId || '');
this.setInfo(res);
} catch (err) {
clearToken();

View File

@ -15,6 +15,7 @@
v-model="form.password"
:placeholder="t('invite.passwordPlaceholder')"
allow-clear
autocomplete="new-password"
@input="validatePsw"
@clear="validatePsw(form.password)"
/>
@ -39,7 +40,12 @@
</a-popover>
</a-form-item>
<a-form-item field="repassword" class="hidden-item">
<a-input-password v-model="form.repassword" :placeholder="t('invite.repasswordPlaceholder')" allow-clear />
<a-input-password
v-model="form.repassword"
:placeholder="t('invite.repasswordPlaceholder')"
autocomplete="new-password"
allow-clear
/>
</a-form-item>
<a-button type="primary" html-type="submit">{{ t('invite.confirm') }}</a-button>
</a-form>

View File

@ -402,7 +402,6 @@
v-model:model-value="activeAuthForm.configuration.dn"
:max-length="250"
:placeholder="t('system.config.auth.DNPlaceholder')"
:input-attrs="{ autocomplete: 'off' }"
allow-clear
></a-input>
</a-form-item>
@ -417,9 +416,9 @@
v-model:model-value="activeAuthForm.configuration.password"
:max-length="250"
:placeholder="t('system.config.auth.LDAPPasswordPlaceholder')"
:input-attrs="{ autocomplete: 'off' }"
allow-clear
></a-input-password>
autocomplete="new-password"
/>
</a-form-item>
<a-form-item
:label="t('system.config.auth.OU')"
@ -467,14 +466,59 @@
<MsFormItemSub :text="t('system.config.auth.LDAPPropertyMapTip')" :show-fill-icon="false" />
</a-form-item>
<div>
<a-button type="outline" class="mr-[16px]" @click="testLink">
<a-button type="outline" class="mr-[16px]" :loading="LDAPTestLoading" @click="testLink">
{{ t('system.config.auth.testLink') }}
</a-button>
<a-button type="outline" @click="testLogin">{{ t('system.config.auth.testLogin') }}</a-button>
<a-button type="outline" :loading="LDAPTestLoading" @click="beforeTestLogin">{{
t('system.config.auth.testLogin')
}}</a-button>
</div>
</template>
</a-form>
</MsDrawer>
<a-modal
v-model:visible="testLoginModalVisible"
:title="t('system.config.auth.testLogin')"
title-align="start"
class="ms-modal-form ms-modal-medium"
:mask-closable="false"
@close="handleTestLoginModalClose"
>
<a-form ref="LDAPFormRef" class="rounded-[4px]" :model="LDAPForm" layout="vertical">
<a-form-item
field="username"
:label="t('system.config.auth.testLoginName')"
:rules="[{ required: true, message: t('system.config.auth.testLoginNameNotNull') }]"
asterisk-position="end"
>
<a-input
v-model:model-value="LDAPForm.username"
:placeholder="t('system.config.auth.testLoginNamePlaceholder')"
:max-length="250"
></a-input>
</a-form-item>
<a-form-item
field="password"
:label="t('system.config.auth.testLoginPassword')"
:rules="[{ required: true, message: t('system.config.auth.testLoginPasswordNotNull') }]"
asterisk-position="end"
>
<a-input-password
v-model:model-value="LDAPForm.password"
:placeholder="t('system.config.auth.testLoginPasswordPlaceholder')"
autocomplete="new-password"
/>
</a-form-item>
</a-form>
<template #footer>
<a-button type="secondary" :disabled="LDAPTestLoading" @click="handleTestLoginModalClose">
{{ t('system.config.auth.testLoginCancel') }}
</a-button>
<a-button type="primary" :loading="LDAPTestLoading" @click="testLogin">
{{ t('system.config.auth.testLogin') }}
</a-button>
</template>
</a-modal>
</div>
</template>
@ -498,6 +542,8 @@
updateAuth,
updateAuthStatus,
deleteAuth,
testLdapConnect,
testLdapLogin,
} from '@/api/modules/setting/config';
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
import { scrollIntoView } from '@/utils/dom';
@ -890,8 +936,86 @@
activeAuthForm.value = { ...defaultAuth };
}
async function testLink() {}
async function testLogin() {}
const LDAPTestLoading = ref(false);
const testLoginModalVisible = ref(false);
const LDAPForm = ref({
username: '',
password: '',
});
const LDAPFormRef = ref<FormInstance>();
function handleTestLoginModalClose() {
LDAPFormRef.value?.resetFields();
testLoginModalVisible.value = false;
}
function testLink() {
authFormRef.value?.validateField(
['configuration.url', 'configuration.dn', 'configuration.password'],
async (res) => {
if (!res) {
try {
LDAPTestLoading.value = true;
await testLdapConnect({
ldapUrl: activeAuthForm.value.configuration.url,
ldapDn: activeAuthForm.value.configuration.dn,
ldapPassword: activeAuthForm.value.configuration.password,
});
Message.success(t('system.config.auth.testLinkSuccess'));
} catch (error) {
console.log(error);
} finally {
LDAPTestLoading.value = false;
}
}
}
);
}
function testLogin() {
LDAPFormRef.value?.validate(async (res) => {
if (!res) {
try {
LDAPTestLoading.value = true;
await testLdapLogin({
ldapUrl: activeAuthForm.value.configuration.url,
ldapDn: activeAuthForm.value.configuration.dn,
ldapPassword: activeAuthForm.value.configuration.password,
username: LDAPForm.value.username,
password: LDAPForm.value.password,
ldapUserFilter: activeAuthForm.value.configuration.filter,
ldapUserOu: activeAuthForm.value.configuration.ou,
ldapUserMapping: activeAuthForm.value.configuration.mapping,
});
Message.success(t('system.config.auth.testLinkSuccess'));
} catch (error) {
console.log(error);
} finally {
LDAPTestLoading.value = false;
}
}
});
}
function beforeTestLogin() {
authFormRef.value?.validateField(
[
'configuration.url',
'configuration.dn',
'configuration.password',
'configuration.filter',
'configuration.ou',
'configuration.mapping',
],
async (errors: Record<string, ValidatedError> | undefined) => {
if (!errors) {
testLoginModalVisible.value = true;
} else {
scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
}
}
);
}
/**
* 保存认证信息
@ -984,7 +1108,7 @@
if (!errors) {
saveAuth(isContinue);
} else {
scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
scrollIntoView(authFormRef.value?.$el.querySelector('.arco-form-item-message'), { block: 'center' });
}
});
}

View File

@ -149,9 +149,9 @@
v-model:model-value="emailConfigForm.password"
:max-length="250"
:placeholder="t('system.config.email.passwordPlaceholder')"
autocomplete="off"
autocomplete="new-password"
allow-clear
></a-input-password>
/>
</a-form-item>
<a-form-item :label="t('system.config.email.from')" field="from" asterisk-position="end" :rules="[emailRule]">
<a-input
@ -391,7 +391,7 @@
{ paramKey: 'smtp.host', paramValue: host, type: 'text' },
{ paramKey: 'smtp.port', paramValue: port, type: 'text' },
{ paramKey: 'smtp.account', paramValue: account, type: 'text' },
{ paramKey: 'smtp.password', paramValue: password, type: 'text' },
{ paramKey: 'smtp.password', paramValue: password, type: 'password' },
{ paramKey: 'smtp.from', paramValue: from, type: 'text' },
{ paramKey: 'smtp.recipient', paramValue: recipient, type: 'text' },
{ paramKey: 'smtp.ssl', paramValue: ssl?.toString(), type: 'text' },

View File

@ -194,6 +194,15 @@ export default {
'system.config.auth.LDAPPropertyMapPlaceholder': 'Please enter',
'system.config.auth.LDAPPropertyMapTip':
'The left key is the MeterSphere user attribute, and the right value is the authentication platform user attribute',
'system.config.auth.testLink': 'Test connection',
'system.config.auth.testLogin': 'Test login',
'system.config.auth.testLink': 'Test Connection',
'system.config.auth.testLinkSuccess': 'LDAP connection succeeded',
'system.config.auth.testLogin': 'Test Login',
'system.config.auth.testLoginName': 'Test login username',
'system.config.auth.testLoginNamePlaceholder': 'Please enter LDAP login username',
'system.config.auth.testLoginNameNotNull': 'LDAP login username cannot be empty',
'system.config.auth.testLoginPassword': 'Test login password',
'system.config.auth.testLoginPasswordPlaceholder': 'Please enter LDAP login password',
'system.config.auth.testLoginPasswordNotNull': 'LDAP login password cannot be empty',
'system.config.auth.testLoginSuccess': 'LDAP login successful',
'system.config.auth.testLoginCancel': 'Cancel',
};

View File

@ -191,5 +191,14 @@ export default {
'system.config.auth.LDAPPropertyMapPlaceholder': '请输入',
'system.config.auth.LDAPPropertyMapTip': '左侧键为 MeterSphere 用户属性,右侧值为认证平台用户属性',
'system.config.auth.testLink': '测试连接',
'system.config.auth.testLinkSuccess': 'LDAP 连接成功',
'system.config.auth.testLogin': '测试登录',
'system.config.auth.testLoginName': '测试登录用户名',
'system.config.auth.testLoginNamePlaceholder': '请输入 LDAP 登录用户名',
'system.config.auth.testLoginNameNotNull': 'LDAP 登录用户名不能为空',
'system.config.auth.testLoginPassword': '测试登录密码',
'system.config.auth.testLoginPasswordPlaceholder': '请输入 LDAP 登录密码',
'system.config.auth.testLoginPasswordNotNull': 'LDAP 登录密码不能为空',
'system.config.auth.testLoginCancel': '取消测试',
'system.config.auth.testLoginSuccess': 'LDAP 登录成功',
};

View File

@ -97,7 +97,14 @@
import MsCard from '@/components/pure/ms-card/index.vue';
import { useI18n } from '@/hooks/useI18n';
import usePathMap from '@/hooks/usePathMap';
import { getLogList, getLogOptions, getLogUsers } from '@/api/modules/setting/log';
import {
getSystemLogList,
getSystemLogOptions,
getSystemLogUsers,
getOrgLogList,
getOrgLogOptions,
getOrgLogUsers,
} from '@/api/modules/setting/log';
import MsCascader from '@/components/business/ms-cascader/index.vue';
import useTableStore from '@/store/modules/ms-table';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -106,23 +113,51 @@
import MsButton from '@/components/pure/ms-button/index.vue';
import { MENU_LEVEL } from '@/config/pathMap';
import MsSearchSelect from '@/components/business/ms-search-select/index';
import useAppStore from '@/store/modules/app';
import type { CascaderOption, SelectOptionData } from '@arco-design/web-vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import type { LogItem } from '@/models/setting/log';
import type { LogItem, LogOptions, UserItem } from '@/models/setting/log';
import type { CommonList } from '@/models/common';
const props = defineProps<{
mode: (typeof MENU_LEVEL)[number]; // //
}>();
const { t } = useI18n();
const appStore = useAppStore();
const requestFuncMap: Record<
(typeof MENU_LEVEL)[number],
{
listFunc: (data: any) => Promise<CommonList<LogItem>>;
optionsFunc: (id: any) => Promise<LogOptions>;
usersFunc: any;
}
> = {
[MENU_LEVEL[0]]: {
listFunc: getSystemLogList,
optionsFunc: getSystemLogOptions,
usersFunc: getSystemLogUsers,
},
[MENU_LEVEL[1]]: {
listFunc: getOrgLogList,
optionsFunc: getOrgLogOptions,
usersFunc: getOrgLogUsers,
},
[MENU_LEVEL[2]]: {
listFunc: getOrgLogList,
optionsFunc: getOrgLogOptions,
usersFunc: getOrgLogUsers,
},
};
const operUser = ref(''); //
const userList = ref<SelectOptionData[]>([]); //
async function initUserList() {
try {
const res = await getLogUsers();
userList.value = res.map((e) => ({
const res = await requestFuncMap[props.mode].usersFunc(appStore.currentOrgId);
userList.value = res.map((e: UserItem) => ({
id: e.id,
value: e.id,
label: e.name,
@ -133,8 +168,8 @@
}
}
const defaultLevelIndex = MENU_LEVEL.findIndex((e) => e === props.mode); // mode
const operateRange = ref<(string | number | Record<string, any>)[]>([MENU_LEVEL[defaultLevelIndex]]); //
const defaultRange = props.mode === MENU_LEVEL[0] ? props.mode : MENU_LEVEL[2];
const operateRange = ref<(string | number | Record<string, any>)[]>([defaultRange]); //
const rangeOptions = ref<CascaderOption[]>( //
props.mode === 'SYSTEM'
? [
@ -157,9 +192,9 @@
async function initRangeOptions() {
try {
rangeLoading.value = true;
const res = await getLogOptions();
if (props.mode === 'SYSTEM' || props.mode === 'ORGANIZATION') {
//
const res = await requestFuncMap[props.mode].optionsFunc(appStore.currentOrgId);
if (props.mode === 'SYSTEM') {
//
rangeOptions.value.push({
value: {
level: 0,
@ -200,7 +235,7 @@
}
}
const level = ref<(typeof MENU_LEVEL)[number]>('SYSTEM'); // //
const level = ref<(typeof MENU_LEVEL)[number]>(defaultRange); // //
const type = ref(''); //
const _module = ref(''); //
const content = ref(''); //
@ -375,12 +410,15 @@
];
const tableStore = useTableStore();
tableStore.initColumn(TableKeyEnum.SYSTEM_LOG, columns, 'drawer');
const { propsRes, propsEvent, loadList, setLoadListParams, resetPagination } = useTable(getLogList, {
tableKey: TableKeyEnum.SYSTEM_LOG,
columns,
selectable: false,
showSelectAll: false,
});
const { propsRes, propsEvent, loadList, setLoadListParams, resetPagination } = useTable(
requestFuncMap[props.mode].listFunc,
{
tableKey: TableKeyEnum.SYSTEM_LOG,
columns,
selectable: false,
showSelectAll: false,
}
);
function searchLog() {
const ranges = operateRange.value.map((e) => e);

View File

@ -163,7 +163,7 @@
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import { getPluginList, deletePluginReq, updatePlugin, getScriptDetail } from '@/api/modules/setting/pluginManger';
import { getAllOrgList } from '@/api/modules/setting/orgnization';
import { getSystemOrgOption } from '@/api/modules/setting/system/organizationAndProject';
import MsButton from '@/components/pure/ms-button/index.vue';
import UploadModel from './uploadModel.vue';
import UpdatePluginModal from './updatePluginModal.vue';
@ -385,7 +385,7 @@
const originizeList = ref<SelectOptionData>([]);
onBeforeMount(async () => {
loadData();
originizeList.value = await getAllOrgList();
originizeList.value = await getSystemOrgOption();
});
</script>

View File

@ -231,7 +231,8 @@
v-model:model-value="form.testResourceDTO.token"
:placeholder="t('system.resourcePool.testResourceDTO.tokenPlaceholder')"
:max-length="250"
></a-input-password>
autocomplete="new-password"
/>
</a-form-item>
<a-form-item
:label="t('system.resourcePool.testResourceDTO.nameSpaces')"
@ -345,7 +346,7 @@
import { downloadStringFile, sleep } from '@/utils';
import { scrollIntoView } from '@/utils/dom';
import { addPool, getPoolInfo, updatePoolInfo } from '@/api/modules/setting/resourcePool';
import { getAllOrgList } from '@/api/modules/setting/orgnization';
import { getSystemOrgOption } from '@/api/modules/setting/system/organizationAndProject';
import useAppStore from '@/store/modules/app';
import type { MsBatchFormInstance, FormItemModel } from '@/components/business/ms-batch-form/types';
@ -404,7 +405,7 @@
const defaultGrid = 'http://selenium-hub:4444';
onBeforeMount(async () => {
orgOptions.value = await getAllOrgList();
orgOptions.value = await getSystemOrgOption();
});
async function initPoolInfo() {