mirror of
https://github.com/certd/certd.git
synced 2026-04-14 20:40:53 +08:00
perf: 支持授权给管理员查看和下载用户证书
This commit is contained in:
@@ -71,7 +71,7 @@ function createService() {
|
||||
// @ts-ignore
|
||||
response.config.onError(err);
|
||||
}
|
||||
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
|
||||
errorCreate(`${errorMessage} (请求接口: ${response.config.url})`, showErrorNotify, dataAxios);
|
||||
}
|
||||
},
|
||||
error => {
|
||||
@@ -113,7 +113,7 @@ function createService() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
error.message += `: ${error.response?.config?.url}`;
|
||||
error.message += `( 请求接口:${error.response?.config?.url})`;
|
||||
errorLog(error, error?.response?.config?.showErrorNotify);
|
||||
if (status === 401) {
|
||||
const userStore = useUserStore();
|
||||
|
||||
@@ -52,6 +52,7 @@ export default {
|
||||
siteMonitorSetting: "Site Monitor Settings",
|
||||
userSecurity: "Security Settings",
|
||||
userProfile: "Account Info",
|
||||
userGrant: "Grant Delegation",
|
||||
suite: "Suite",
|
||||
mySuite: "My Suite",
|
||||
suiteBuy: "Suite Purchase",
|
||||
@@ -62,6 +63,12 @@ export default {
|
||||
greeting: "Hello",
|
||||
profile: "Account Info",
|
||||
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: {
|
||||
greeting: "Hello, {name}, welcome to 【{site}】",
|
||||
|
||||
@@ -57,6 +57,7 @@ export default {
|
||||
siteMonitorSetting: "站点监控设置",
|
||||
userSecurity: "认证安全设置",
|
||||
userProfile: "账号信息",
|
||||
userGrant: "授权委托",
|
||||
suite: "套餐",
|
||||
mySuite: "我的套餐",
|
||||
suiteBuy: "套餐购买",
|
||||
@@ -68,6 +69,13 @@ export default {
|
||||
greeting: "您好",
|
||||
profile: "账号信息",
|
||||
logout: "注销登录",
|
||||
|
||||
setting: {
|
||||
grantSetting: "授权委托设置",
|
||||
saveSuccess: "保存成功",
|
||||
allowAdminViewCerts: "授权管理员查看和下载证书",
|
||||
allowAdminViewCertsHelper: "允许管理员查看和下载我的所有证书",
|
||||
},
|
||||
},
|
||||
dashboard: {
|
||||
greeting: "您好,{name},欢迎使用 【{site}】",
|
||||
|
||||
@@ -204,6 +204,17 @@ export const certdResources = [
|
||||
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",
|
||||
name: "UserProfile",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a-button v-if="showButton" type="primary" @click="open">
|
||||
{{ $t("authentication.changePasswordButton") }}
|
||||
{{ t("authentication.changePasswordButton") }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
|
||||
25
packages/ui/certd-client/src/views/certd/mine/grant/api.ts
Normal file
25
packages/ui/certd-client/src/views/certd/mine/grant/api.ts
Normal file
@@ -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 { UserSettingsService } from "../../../modules/mine/service/user-settings-service.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());
|
||||
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 { BaseController, Constants } from '@certd/lib-server';
|
||||
import { BaseController, Constants, PermissionException } from '@certd/lib-server';
|
||||
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()
|
||||
@Controller('/api/pi/cert')
|
||||
@@ -12,10 +14,32 @@ export class CertController extends BaseController {
|
||||
@Inject()
|
||||
storeService: StorageService;
|
||||
|
||||
|
||||
@Inject()
|
||||
userSettingsService: UserSettingsService;
|
||||
|
||||
|
||||
@Post('/get', { summary: Constants.per.authOnly })
|
||||
async getCert(@Query('id') id: number) {
|
||||
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);
|
||||
return this.ok(privateVars.cert);
|
||||
}
|
||||
@@ -24,7 +48,7 @@ export class CertController extends BaseController {
|
||||
@Post('/readCertDetail', { summary: Constants.per.authOnly })
|
||||
async readCertDetail(@Body('crt') crt: string) {
|
||||
if (!crt) {
|
||||
throw new Error('crt is required');
|
||||
throw new Error('crt is required');
|
||||
}
|
||||
const certDetail = CertReader.readCertDetail(crt)
|
||||
return this.ok(certDetail);
|
||||
|
||||
@@ -10,6 +10,8 @@ import * as fs from "fs";
|
||||
import { logger } from "@certd/basic";
|
||||
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
|
||||
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()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
userSettingsService: UserSettingsService;
|
||||
|
||||
getService(): HistoryService {
|
||||
return this.service;
|
||||
}
|
||||
@@ -77,7 +82,7 @@ export class HistoryController extends CrudController<HistoryService> {
|
||||
|
||||
@Post('/list', { summary: Constants.per.authOnly })
|
||||
async list(@Body(ALL) body) {
|
||||
const isAdmin = await this.authService.isAdmin(this.ctx);
|
||||
const isAdmin = this.authService.isAdmin(this.ctx);
|
||||
if (!isAdmin) {
|
||||
body.userId = this.getUserId();
|
||||
}
|
||||
@@ -89,7 +94,7 @@ export class HistoryController extends CrudController<HistoryService> {
|
||||
};
|
||||
const withDetail = body.withDetail;
|
||||
delete body.withDetail;
|
||||
let select:any = null
|
||||
let select: any = null
|
||||
if (!withDetail) {
|
||||
select = {
|
||||
pipeline: true, // 后面这里改成false
|
||||
@@ -193,7 +198,6 @@ export class HistoryController extends CrudController<HistoryService> {
|
||||
|
||||
@Post('/files', { summary: Constants.per.authOnly })
|
||||
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);
|
||||
return this.ok(files);
|
||||
}
|
||||
@@ -210,14 +214,24 @@ export class HistoryController extends CrudController<HistoryService> {
|
||||
throw new CommonException('historyId is null');
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@Get('/download', { summary: Constants.per.authOnly })
|
||||
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 file = files.find(f => f.id === fileId);
|
||||
if (file == null) {
|
||||
|
||||
@@ -37,3 +37,12 @@ export class UserEmailSetting extends BaseSettings {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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