mirror of
https://github.com/certd/certd.git
synced 2026-05-18 06:17:31 +08:00
perf: 支持授权给管理员查看和下载用户证书
This commit is contained in:
@@ -71,7 +71,7 @@ function createService() {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
response.config.onError(err);
|
response.config.onError(err);
|
||||||
}
|
}
|
||||||
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
|
errorCreate(`${errorMessage} (请求接口: ${response.config.url})`, showErrorNotify, dataAxios);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
@@ -113,7 +113,7 @@ function createService() {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
error.message += `: ${error.response?.config?.url}`;
|
error.message += `( 请求接口:${error.response?.config?.url})`;
|
||||||
errorLog(error, error?.response?.config?.showErrorNotify);
|
errorLog(error, error?.response?.config?.showErrorNotify);
|
||||||
if (status === 401) {
|
if (status === 401) {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export default {
|
|||||||
siteMonitorSetting: "Site Monitor Settings",
|
siteMonitorSetting: "Site Monitor Settings",
|
||||||
userSecurity: "Security Settings",
|
userSecurity: "Security Settings",
|
||||||
userProfile: "Account Info",
|
userProfile: "Account Info",
|
||||||
|
userGrant: "Grant Delegation",
|
||||||
suite: "Suite",
|
suite: "Suite",
|
||||||
mySuite: "My Suite",
|
mySuite: "My Suite",
|
||||||
suiteBuy: "Suite Purchase",
|
suiteBuy: "Suite Purchase",
|
||||||
@@ -62,6 +63,12 @@ export default {
|
|||||||
greeting: "Hello",
|
greeting: "Hello",
|
||||||
profile: "Account Info",
|
profile: "Account Info",
|
||||||
logout: "Logout",
|
logout: "Logout",
|
||||||
|
setting: {
|
||||||
|
grantSetting: "Grant Settings",
|
||||||
|
saveSuccess: "Save Success",
|
||||||
|
allowAdminViewCerts: "Allow Admin view and download Certs",
|
||||||
|
allowAdminViewCertsHelper: "Allow admin view and download all certificates",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
greeting: "Hello, {name}, welcome to 【{site}】",
|
greeting: "Hello, {name}, welcome to 【{site}】",
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export default {
|
|||||||
siteMonitorSetting: "站点监控设置",
|
siteMonitorSetting: "站点监控设置",
|
||||||
userSecurity: "认证安全设置",
|
userSecurity: "认证安全设置",
|
||||||
userProfile: "账号信息",
|
userProfile: "账号信息",
|
||||||
|
userGrant: "授权委托",
|
||||||
suite: "套餐",
|
suite: "套餐",
|
||||||
mySuite: "我的套餐",
|
mySuite: "我的套餐",
|
||||||
suiteBuy: "套餐购买",
|
suiteBuy: "套餐购买",
|
||||||
@@ -68,6 +69,13 @@ export default {
|
|||||||
greeting: "您好",
|
greeting: "您好",
|
||||||
profile: "账号信息",
|
profile: "账号信息",
|
||||||
logout: "注销登录",
|
logout: "注销登录",
|
||||||
|
|
||||||
|
setting: {
|
||||||
|
grantSetting: "授权委托设置",
|
||||||
|
saveSuccess: "保存成功",
|
||||||
|
allowAdminViewCerts: "授权管理员查看和下载证书",
|
||||||
|
allowAdminViewCertsHelper: "允许管理员查看和下载我的所有证书",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
greeting: "您好,{name},欢迎使用 【{site}】",
|
greeting: "您好,{name},欢迎使用 【{site}】",
|
||||||
|
|||||||
@@ -204,6 +204,17 @@ export const certdResources = [
|
|||||||
isMenu: true,
|
isMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "certd.userGrant",
|
||||||
|
name: "UserGrantSetting",
|
||||||
|
path: "/certd/mine/grant",
|
||||||
|
component: "/certd/mine/grant/index.vue",
|
||||||
|
meta: {
|
||||||
|
icon: "mi:user-check",
|
||||||
|
auth: true,
|
||||||
|
isMenu: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "certd.userProfile",
|
title: "certd.userProfile",
|
||||||
name: "UserProfile",
|
name: "UserProfile",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-button v-if="showButton" type="primary" @click="open">
|
<a-button v-if="showButton" type="primary" @click="open">
|
||||||
{{ $t("authentication.changePasswordButton") }}
|
{{ t("authentication.changePasswordButton") }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { request } from "/@/api/service";
|
||||||
|
const apiPrefix = "/user/settings";
|
||||||
|
export type UserGrantSetting = {
|
||||||
|
allowAdminViewCerts: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function GrantSettingsGet() {
|
||||||
|
const res = await request({
|
||||||
|
url: apiPrefix + "/grant/get",
|
||||||
|
method: "post",
|
||||||
|
});
|
||||||
|
if (!res) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return res as UserGrantSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function UserSettingSave(req: any) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/grant/save",
|
||||||
|
method: "post",
|
||||||
|
data: req,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<fs-page class="page-user-settings page-grant">
|
||||||
|
<template #header>
|
||||||
|
<div class="title">{{ t("certd.user.setting.grantSetting") }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="user-settings-form settings-form">
|
||||||
|
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off">
|
||||||
|
<a-form-item :label="t('certd.user.setting.allowAdminViewCerts')" :name="['allowAdminViewCerts']">
|
||||||
|
<div class="flex mt-5">
|
||||||
|
<a-switch v-model:checked="formState.allowAdminViewCerts" :disabled="!settingsStore.isPlus" />
|
||||||
|
<vip-button class="ml-5" mode="button"></vip-button>
|
||||||
|
</div>
|
||||||
|
<div class="helper">{{ t("certd.user.setting.allowAdminViewCertsHelper") }}</div>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||||
|
<loading-button type="primary" html-type="button" :click="doSave">{{ t("certd.confirm") }}</loading-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</fs-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { computed, reactive, watch } from "vue";
|
||||||
|
import * as api from "./api";
|
||||||
|
import { UserGrantSetting } from "./api";
|
||||||
|
import { Modal, notification } from "ant-design-vue";
|
||||||
|
import { merge } from "lodash-es";
|
||||||
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
import { useI18n } from "/src/locales";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const settingsStore = useSettingStore();
|
||||||
|
defineOptions({
|
||||||
|
name: "UserSecurity",
|
||||||
|
});
|
||||||
|
|
||||||
|
const formState = reactive<Partial<UserGrantSetting>>({
|
||||||
|
allowAdminViewCerts: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadUserSettings() {
|
||||||
|
const data: any = await api.GrantSettingsGet();
|
||||||
|
merge(formState, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUserSettings();
|
||||||
|
const doSave = async () => {
|
||||||
|
await api.UserSettingSave({
|
||||||
|
...formState,
|
||||||
|
});
|
||||||
|
notification.success({
|
||||||
|
message: t("certd.user.setting.saveSuccess"),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.page-user-settings {
|
||||||
|
.user-settings-form {
|
||||||
|
width: 600px;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,6 +2,9 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
|
|||||||
import { Constants, CrudController } from "@certd/lib-server";
|
import { Constants, CrudController } from "@certd/lib-server";
|
||||||
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
|
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
|
||||||
import { UserSettingsEntity } from "../../../modules/mine/entity/user-settings.js";
|
import { UserSettingsEntity } from "../../../modules/mine/entity/user-settings.js";
|
||||||
|
import { UserGrantSetting } from "../../../modules/mine/service/models.js";
|
||||||
|
import { isPlus } from "@certd/plus-core";
|
||||||
|
import { merge } from "lodash-es";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
@@ -65,6 +68,26 @@ export class UserSettingsController extends CrudController<UserSettingsService>
|
|||||||
const entity = await this.service.getByKey(key, this.getUserId());
|
const entity = await this.service.getByKey(key, this.getUserId());
|
||||||
return this.ok(entity);
|
return this.ok(entity);
|
||||||
}
|
}
|
||||||
|
@Post("/grant/get", { summary: Constants.per.authOnly })
|
||||||
|
async grantSettingsGet() {
|
||||||
|
const userId = this.getUserId();
|
||||||
|
const setting = await this.service.getSetting<UserGrantSetting>(userId, UserGrantSetting);
|
||||||
|
return this.ok(setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("/grant/save", { summary: Constants.per.authOnly })
|
||||||
|
async grantSettingsSave(@Body(ALL) bean: UserGrantSetting) {
|
||||||
|
if (!isPlus()) {
|
||||||
|
throw new Error('本功能需要开通专业版')
|
||||||
|
}
|
||||||
|
const userId = this.getUserId();
|
||||||
|
const setting = new UserGrantSetting();
|
||||||
|
merge(setting, bean);
|
||||||
|
|
||||||
|
await this.service.saveSetting(userId, setting);
|
||||||
|
return this.ok({});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import {Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core';
|
import { Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||||
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
|
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
|
||||||
import { BaseController, Constants } from '@certd/lib-server';
|
import { BaseController, Constants, PermissionException } from '@certd/lib-server';
|
||||||
import { StorageService } from '../../../modules/pipeline/service/storage-service.js';
|
import { StorageService } from '../../../modules/pipeline/service/storage-service.js';
|
||||||
import {CertReader} from "@certd/plugin-cert";
|
import { CertReader } from "@certd/plugin-cert";
|
||||||
|
import { UserSettingsService } from '../../../modules/mine/service/user-settings-service.js';
|
||||||
|
import { UserGrantSetting } from '../../../modules/mine/service/models.js';
|
||||||
|
|
||||||
@Provide()
|
@Provide()
|
||||||
@Controller('/api/pi/cert')
|
@Controller('/api/pi/cert')
|
||||||
@@ -12,10 +14,32 @@ export class CertController extends BaseController {
|
|||||||
@Inject()
|
@Inject()
|
||||||
storeService: StorageService;
|
storeService: StorageService;
|
||||||
|
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
userSettingsService: UserSettingsService;
|
||||||
|
|
||||||
|
|
||||||
@Post('/get', { summary: Constants.per.authOnly })
|
@Post('/get', { summary: Constants.per.authOnly })
|
||||||
async getCert(@Query('id') id: number) {
|
async getCert(@Query('id') id: number) {
|
||||||
const userId = this.getUserId();
|
const userId = this.getUserId();
|
||||||
await this.pipelineService.checkUserId(id, userId);
|
|
||||||
|
|
||||||
|
|
||||||
|
const pipleinUserId = await this.pipelineService.getPipelineUserId(id);
|
||||||
|
|
||||||
|
if (pipleinUserId !== userId) {
|
||||||
|
// 如果是管理员,检查用户是否有授权管理员查看
|
||||||
|
const isAdmin = await this.isAdmin()
|
||||||
|
if (!isAdmin) {
|
||||||
|
throw new PermissionException();
|
||||||
|
}
|
||||||
|
// 是否允许管理员查看
|
||||||
|
const setting = await this.userSettingsService.getSetting<UserGrantSetting>(pipleinUserId, UserGrantSetting, false);
|
||||||
|
if (setting?.allowAdminViewCerts !== true) {
|
||||||
|
//不允许管理员查看
|
||||||
|
throw new PermissionException("该流水线的用户还未授权管理员查看证书,请先让用户在”设置->授权委托“中打开开关");
|
||||||
|
}
|
||||||
|
}
|
||||||
const privateVars = await this.storeService.getPipelinePrivateVars(id);
|
const privateVars = await this.storeService.getPipelinePrivateVars(id);
|
||||||
return this.ok(privateVars.cert);
|
return this.ok(privateVars.cert);
|
||||||
}
|
}
|
||||||
@@ -24,7 +48,7 @@ export class CertController extends BaseController {
|
|||||||
@Post('/readCertDetail', { summary: Constants.per.authOnly })
|
@Post('/readCertDetail', { summary: Constants.per.authOnly })
|
||||||
async readCertDetail(@Body('crt') crt: string) {
|
async readCertDetail(@Body('crt') crt: string) {
|
||||||
if (!crt) {
|
if (!crt) {
|
||||||
throw new Error('crt is required');
|
throw new Error('crt is required');
|
||||||
}
|
}
|
||||||
const certDetail = CertReader.readCertDetail(crt)
|
const certDetail = CertReader.readCertDetail(crt)
|
||||||
return this.ok(certDetail);
|
return this.ok(certDetail);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import * as fs from "fs";
|
|||||||
import { logger } from "@certd/basic";
|
import { logger } from "@certd/basic";
|
||||||
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
|
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
|
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
|
||||||
|
import { UserGrantSetting } from "../../../modules/mine/service/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 证书
|
* 证书
|
||||||
@@ -30,6 +32,9 @@ export class HistoryController extends CrudController<HistoryService> {
|
|||||||
@Inject()
|
@Inject()
|
||||||
sysSettingsService: SysSettingsService;
|
sysSettingsService: SysSettingsService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
userSettingsService: UserSettingsService;
|
||||||
|
|
||||||
getService(): HistoryService {
|
getService(): HistoryService {
|
||||||
return this.service;
|
return this.service;
|
||||||
}
|
}
|
||||||
@@ -77,7 +82,7 @@ export class HistoryController extends CrudController<HistoryService> {
|
|||||||
|
|
||||||
@Post('/list', { summary: Constants.per.authOnly })
|
@Post('/list', { summary: Constants.per.authOnly })
|
||||||
async list(@Body(ALL) body) {
|
async list(@Body(ALL) body) {
|
||||||
const isAdmin = await this.authService.isAdmin(this.ctx);
|
const isAdmin = this.authService.isAdmin(this.ctx);
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
body.userId = this.getUserId();
|
body.userId = this.getUserId();
|
||||||
}
|
}
|
||||||
@@ -89,7 +94,7 @@ export class HistoryController extends CrudController<HistoryService> {
|
|||||||
};
|
};
|
||||||
const withDetail = body.withDetail;
|
const withDetail = body.withDetail;
|
||||||
delete body.withDetail;
|
delete body.withDetail;
|
||||||
let select:any = null
|
let select: any = null
|
||||||
if (!withDetail) {
|
if (!withDetail) {
|
||||||
select = {
|
select = {
|
||||||
pipeline: true, // 后面这里改成false
|
pipeline: true, // 后面这里改成false
|
||||||
@@ -193,7 +198,6 @@ export class HistoryController extends CrudController<HistoryService> {
|
|||||||
|
|
||||||
@Post('/files', { summary: Constants.per.authOnly })
|
@Post('/files', { summary: Constants.per.authOnly })
|
||||||
async files(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number) {
|
async files(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number) {
|
||||||
await this.authService.checkEntityUserId(this.ctx, this.service, historyId);
|
|
||||||
const files = await this.getFiles(historyId, pipelineId);
|
const files = await this.getFiles(historyId, pipelineId);
|
||||||
return this.ok(files);
|
return this.ok(files);
|
||||||
}
|
}
|
||||||
@@ -210,14 +214,24 @@ export class HistoryController extends CrudController<HistoryService> {
|
|||||||
throw new CommonException('historyId is null');
|
throw new CommonException('historyId is null');
|
||||||
}
|
}
|
||||||
if (history.userId !== this.getUserId()) {
|
if (history.userId !== this.getUserId()) {
|
||||||
throw new PermissionException();
|
// 如果是管理员,检查用户是否有授权管理员查看
|
||||||
|
const isAdmin = await this.isAdmin()
|
||||||
|
if (!isAdmin) {
|
||||||
|
throw new PermissionException();
|
||||||
|
}
|
||||||
|
// 是否允许管理员查看
|
||||||
|
const setting = await this.userSettingsService.getSetting<UserGrantSetting>(history.userId, UserGrantSetting, false);
|
||||||
|
if (setting?.allowAdminViewCerts!==true) {
|
||||||
|
//不允许管理员查看
|
||||||
|
throw new PermissionException("该流水线的用户还未授权管理员下载证书,请先让用户在”设置->授权委托“中打开开关");
|
||||||
|
}
|
||||||
|
//允许管理员查看
|
||||||
}
|
}
|
||||||
return await this.service.getFiles(history);
|
return await this.service.getFiles(history);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/download', { summary: Constants.per.authOnly })
|
@Get('/download', { summary: Constants.per.authOnly })
|
||||||
async download(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number, @Query('fileId') fileId: string) {
|
async download(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number, @Query('fileId') fileId: string) {
|
||||||
await this.authService.checkEntityUserId(this.ctx, this.service, historyId);
|
|
||||||
const files = await this.getFiles(historyId, pipelineId);
|
const files = await this.getFiles(historyId, pipelineId);
|
||||||
const file = files.find(f => f.id === fileId);
|
const file = files.find(f => f.id === fileId);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
|
|||||||
@@ -37,3 +37,12 @@ export class UserEmailSetting extends BaseSettings {
|
|||||||
|
|
||||||
list:string[] = [];
|
list:string[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class UserGrantSetting extends BaseSettings {
|
||||||
|
static __title__ = "用户授权设置";
|
||||||
|
static __key__ = "user.grant";
|
||||||
|
|
||||||
|
allowAdminViewCerts:boolean = false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1072,4 +1072,16 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||||||
});
|
});
|
||||||
return res?.status;
|
return res?.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getPipelineUserId(pipelineId: number) {
|
||||||
|
const res = await this.repository.findOne({
|
||||||
|
select: {
|
||||||
|
userId: true
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
id: pipelineId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res?.userId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user