think/packages/server/src/services/auth.service.ts

515 lines
16 KiB
TypeScript

import { AuthDto } from '@dtos/auth.dto';
import { AuthEntity } from '@entities/auth.entity';
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserService } from '@services/user.service';
import { AuthEnum, IDocument, IOrganization, IPagination, IUser, IWiki } from '@think/domains';
import * as lodash from 'lodash';
import { Repository } from 'typeorm';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(AuthEntity)
private readonly authRepo: Repository<AuthEntity>,
@Inject(forwardRef(() => UserService))
private readonly userService: UserService
) {}
/**
* 获取用户权限
* @param user
* @param auth
* @returns
*/
public async getAuth(userId: IUser['id'], dto: Omit<AuthDto, 'auth'>) {
const conditions = { userId, ...dto };
const userAuth = await this.authRepo.findOne(conditions);
return userAuth;
}
/**
* 创建或更新用户权限
* @param user
* @param auth
* @returns
*/
public async createOrUpdateAuth(userId: IUser['id'], auth: AuthDto) {
const targetAuth = auth.auth;
delete auth.auth;
const wrappedAuth = { userId, ...auth };
const oldAuth = await this.authRepo.findOne(wrappedAuth);
let newAuth: AuthEntity;
if (oldAuth) {
newAuth = await this.authRepo.save(await this.authRepo.merge(oldAuth, wrappedAuth, { auth: targetAuth }));
} else {
newAuth = await this.authRepo.save(await this.authRepo.create({ ...wrappedAuth, auth: targetAuth }));
}
if (newAuth.organizationId && !newAuth.wikiId && !newAuth.documentId) {
// 用户被添加到组织,在组织内添加对应权限
const wikisAuth = await this.getWikisInOrganization(newAuth.organizationId);
await Promise.all(
wikisAuth.map((wikiAuth) => {
return this.createOrUpdateAuth(newAuth.userId, {
auth: newAuth.auth,
organizationId: newAuth.organizationId,
wikiId: wikiAuth.wikiId,
documentId: null,
});
})
);
} else if (newAuth.organizationId && newAuth.wikiId && !newAuth.documentId) {
// 用户被添加到知识库,在知识库内添加对应权限
const docsAuth = await this.getDocumentsInWiki(newAuth.organizationId, newAuth.wikiId);
await Promise.all(
docsAuth.map((auth) => {
return this.createOrUpdateAuth(newAuth.userId, {
auth: newAuth.auth,
organizationId: newAuth.organizationId,
wikiId: newAuth.wikiId,
documentId: auth.documentId,
});
})
);
}
}
/**
* 删除组织
* 注意:该方法是直接删除,可调用 canDelete 判断是否可删除
* @param organizationId
*/
async deleteOrganization(organizationId: IOrganization['id']) {
const res = await this.authRepo.find({
organizationId,
});
await this.authRepo.remove(res);
}
/**
* 删除知识库
* 注意:该方法是直接删除,可调用 canDelete 判断是否可删除
* @param organizationId
* @param wikiId
*/
async deleteWiki(organizationId: IOrganization['id'], wikiId: IWiki['id']) {
const res = await this.authRepo.find({
organizationId,
wikiId,
});
await this.authRepo.remove(res);
}
/**
* 删除文档
* 注意:该方法是直接删除,可调用 canDelete 判断是否可删除
* @param organizationId
* @param wikiId
* @param documentId
*/
async deleteDocument(organizationId: IOrganization['id'], wikiId: IWiki['id'], documentId: IDocument['id']) {
const res = await this.authRepo.find({
organizationId,
wikiId,
documentId,
});
await this.authRepo.remove(res);
}
/**
* 用户是否可查看目标
* @param userId
* @param dto
* @returns
*/
async canView(userId: IUser['id'], dto: Omit<AuthDto, 'auth'>) {
const conditions: Partial<AuthEntity> = {
userId,
organizationId: dto.organizationId,
wikiId: dto.wikiId || null,
documentId: dto.documentId || null,
};
const userAuth = await this.authRepo.findOne(conditions);
if (!userAuth || userAuth.auth === AuthEnum.noAccess) {
throw new HttpException('您没有权限', HttpStatus.FORBIDDEN);
}
return userAuth;
}
/**
* 用户是否可编辑目标
* @param userId
* @param dto
* @returns
*/
async canEdit(userId: IUser['id'], dto: Omit<AuthDto, 'auth'>) {
const conditions: Partial<AuthEntity> = {
userId,
organizationId: dto.organizationId,
wikiId: dto.wikiId || null,
documentId: dto.documentId || null,
};
const userAuth = await this.authRepo.findOne(conditions);
if (!userAuth || ![AuthEnum.creator, AuthEnum.admin].includes(userAuth.auth)) {
throw new HttpException('您没有权限', HttpStatus.FORBIDDEN);
}
return userAuth;
}
/**
* 用户是否可删除目标
* @param userId
* @param dto
* @returns
*/
async canDelete(userId: IUser['id'], dto: Omit<AuthDto, 'auth'>) {
const conditions: Partial<AuthEntity> = {
userId,
organizationId: dto.organizationId,
wikiId: dto.wikiId || null,
documentId: dto.documentId || null,
};
const userAuth = await this.authRepo.findOne(conditions);
if (!userAuth || ![AuthEnum.creator].includes(userAuth.auth)) {
throw new HttpException('您没有权限', HttpStatus.FORBIDDEN);
}
return userAuth;
}
/**
* 操作他人权限
* @param currentUserId
* @param targetUserId
* @param dto
*/
private async operateOtherUserAuth(currentUserId: IUser['id'], targetUserId: IUser['id'], dto: AuthDto) {
const targetUser = await this.userService.findOne({ id: targetUserId });
if (!targetUser) {
throw new HttpException('用户不存在', HttpStatus.NOT_FOUND);
}
const conditions: Partial<AuthEntity> = {
organizationId: dto.organizationId,
wikiId: dto.wikiId || null,
documentId: dto.documentId || null,
};
const currentUserAuth = await this.authRepo.findOne({
userId: currentUserId,
...conditions,
});
if (!currentUserAuth) {
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
}
// 仅创建者、管理员可操作他人权限
if (![AuthEnum.creator, AuthEnum.admin].includes(currentUserAuth.auth)) {
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
}
// 仅创建者可赋予他人创建者、管理员权限
if ([AuthEnum.creator, AuthEnum.admin].includes(dto.auth) && currentUserAuth.auth !== AuthEnum.creator) {
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
}
const maybeTargetUserAuth = await this.authRepo.findOne({
userId: targetUserId,
...conditions,
});
if (maybeTargetUserAuth) {
// 对方是创建者,无权操作
if (maybeTargetUserAuth.auth === AuthEnum.creator) {
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
}
// 对方是管理员,仅创建者可操作
if (maybeTargetUserAuth.auth === AuthEnum.admin && currentUserAuth.auth !== AuthEnum.creator) {
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
}
}
}
/**
* 为他人创建或更新权限
* @param currentUserId
* @param targetUserId
* @param dto
*/
async createOrUpdateOtherUserAuth(currentUserId: IUser['id'], targetUserId: IUser['id'], dto: AuthDto) {
await this.operateOtherUserAuth(currentUserId, targetUserId, dto);
await this.createOrUpdateAuth(targetUserId, dto);
}
/**
* 删除他人权限
* @param currentUserId
* @param targetUserId
* @param dto
*/
async deleteOtherUserAuth(currentUserId: IUser['id'], targetUserId: IUser['id'], dto: AuthDto) {
await this.operateOtherUserAuth(currentUserId, targetUserId, dto);
const conditions: Partial<AuthEntity> = {
userId: targetUserId,
auth: dto.auth,
organizationId: dto.organizationId,
wikiId: dto.wikiId || null,
documentId: dto.documentId || null,
};
const targetUserAuth = await this.authRepo.findOne(conditions);
await this.authRepo.remove(targetUserAuth);
if (targetUserAuth.organizationId && !targetUserAuth.wikiId && !targetUserAuth.documentId) {
// 用户被从组织删除,需要删除在组织内的所有权限
const res = await this.authRepo.find({
userId: targetUserAuth.userId,
organizationId: targetUserAuth.organizationId,
});
await this.authRepo.remove(res);
} else if (targetUserAuth.organizationId && targetUserAuth.wikiId && !targetUserAuth.documentId) {
// 用户被从知识库删除,需要删除在知识库的所有权限
const res = await this.authRepo.find({
userId: targetUserAuth.userId,
organizationId: targetUserAuth.organizationId,
wikiId: targetUserAuth.wikiId,
});
await this.authRepo.remove(res);
}
}
/**
* 获取用户可查看的组织 id 列表
* @param userId
*/
async getUserCanViewOrganizationIds(userId: IUser['id']) {
const data = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth IN (:...types)', { types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member] })
.andWhere('auth.userId=:userId')
.andWhere('auth.wikiId is NULL')
.andWhere('auth.documentId is NULL')
.setParameter('userId', userId)
.getMany();
return (data || []).map((d) => d.organizationId);
}
/**
* 获取指定组织内所有知识库
* @param userId
*/
async getWikisInOrganization(organizationId: IOrganization['id']) {
const data = await this.authRepo
.createQueryBuilder('auth')
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.wikiId is NOT NULL')
.andWhere('auth.documentId is NULL')
.setParameter('organizationId', organizationId)
.getMany();
return lodash.uniqBy(data || [], (w) => w.wikiId);
}
/**
* 获取指定知识库内所有文档
* @param userId
*/
async getDocumentsInWiki(organizationId: IOrganization['id'], wikiId: IWiki['id']) {
const data = await this.authRepo
.createQueryBuilder('auth')
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.wikiId=:wikiId')
.andWhere('auth.documentId IS NOT NULL')
.setParameter('organizationId', organizationId)
.setParameter('wikiId', wikiId)
.getMany();
return lodash.uniqBy(data || [], (w) => w.documentId);
}
/**
* 获取指定组织的所有用户权限
* @param organizationId
* @param pagination 分页参数,不传获取所有
* @returns
*/
async getUsersAuthInOrganization(organizationId: IOrganization['id'], pagination: IPagination | null) {
const query = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth IN (:...types)', {
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
})
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.wikiId is NULL')
.andWhere('auth.documentId is NULL')
.setParameter('organizationId', organizationId);
if (pagination) {
const { page = 1, pageSize = 12 } = pagination;
query.skip((+page - 1) * +pageSize);
query.take(+pageSize);
}
const [data, total] = await query.getManyAndCount();
return { data: data || [], total };
}
/**
* 获取指定知识库的所有用户权限
* @param organizationId
* @param wikiId
* @param pagination 分页参数,不传获取所有
* @returns
*/
async getUsersAuthInWiki(organizationId: IOrganization['id'], wikiId: IWiki['id'], pagination: IPagination | null) {
const query = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth IN (:...types)', {
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
})
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.wikiId=:wikiId')
.andWhere('auth.documentId is NULL')
.setParameter('organizationId', organizationId)
.setParameter('wikiId', wikiId);
if (pagination) {
const { page = 1, pageSize = 12 } = pagination;
query.skip((+page - 1) * +pageSize);
query.take(+pageSize);
}
const [data, total] = await query.getManyAndCount();
return { data: data || [], total };
}
/**
* 获取用户在指定组织可查看的知识库列表
* @param userId
*/
async getUserCanViewWikisInOrganization(userId: IUser['id'], organizationId: IOrganization['id']) {
const [data, total] = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth IN (:...types)', {
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
})
.andWhere('auth.userId=:userId')
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.documentId is NULL')
.setParameter('userId', userId)
.setParameter('organizationId', organizationId)
.getManyAndCount();
return { data: data || [], total };
}
/**
* 获取用户在指定组织创建的知识库列表
* @param userId
*/
async getUserCreateWikisInOrganization(userId: IUser['id'], organizationId: IOrganization['id']) {
const [data, total] = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth=:auth')
.andWhere('auth.userId=:userId')
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.documentId is NULL')
.setParameter('auth', AuthEnum.creator)
.setParameter('userId', userId)
.setParameter('organizationId', organizationId)
.getManyAndCount();
return { data: data || [], total };
}
/**
* 获取用户在指定组织参与的知识库列表
* @param userId
*/
async getUserJoinWikisInOrganization(userId: IUser['id'], organizationId: IOrganization['id']) {
const [data, total] = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth IN (:...types)', { types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member] })
.andWhere('auth.userId=:userId')
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.documentId is NULL')
.setParameter('userId', userId)
.setParameter('organizationId', organizationId)
.getManyAndCount();
return { data: data || [], total };
}
/**
* 获取用户在指定知识库可查看的所有文档
* @param userId
*/
async getUserCanViewDocumentsInWiki(organizationId: IOrganization['id'], wikiId: IWiki['id']) {
const data = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth IN (:...types)', { types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member] })
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.wikiId=:wikiId')
.andWhere('auth.documentId IS NOT NULL')
.setParameter('organizationId', organizationId)
.setParameter('wikiId', wikiId)
.getMany();
return data;
}
/**
* 获取指定文档的所有用户权限
* @param organizationId
* @param wikiId
* @param documentId
* @param pagination 分页参数,不传获取所有
* @returns
*/
async getUsersAuthInDocument(
organizationId: IOrganization['id'],
wikiId: IWiki['id'],
documentId: IDocument['id'],
pagination: IPagination | null
) {
const query = await this.authRepo
.createQueryBuilder('auth')
.where('auth.auth IN (:...types)', {
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
})
.andWhere('auth.organizationId=:organizationId')
.andWhere('auth.wikiId=:wikiId')
.andWhere('auth.documentId=:documentId')
.setParameter('organizationId', organizationId)
.setParameter('wikiId', wikiId)
.setParameter('documentId', documentId);
if (pagination) {
const { page = 1, pageSize = 12 } = pagination;
query.skip((+page - 1) * +pageSize);
query.take(+pageSize);
}
const [data, total] = await query.getManyAndCount();
return { data: data || [], total };
}
}