@pengzi/kms 1.0.2 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -3
- package/dist/cli/cli/kms.js +1050 -0
- package/dist/cli/kms.js +1050 -0
- package/dist/cli/src/client.js +254 -0
- package/dist/cli/src/core/asymmetric-crypto.js +170 -0
- package/dist/cli/src/core/crypto.js +99 -0
- package/dist/cli/src/core/crypto.service.js +66 -0
- package/dist/cli/src/core/key-derivation.js +95 -0
- package/dist/cli/src/index.js +50 -0
- package/dist/cli/src/models/audit.model.js +82 -0
- package/dist/cli/src/models/key.model.js +119 -0
- package/dist/cli/src/models/project.model.js +53 -0
- package/dist/cli/src/models/user.model.js +140 -0
- package/dist/cli/src/repositories/audit.repository.js +115 -0
- package/dist/cli/src/repositories/base.repository.js +94 -0
- package/dist/cli/src/repositories/key.repository.js +125 -0
- package/dist/cli/src/repositories/project.repository.js +81 -0
- package/dist/cli/src/repositories/user.repository.js +101 -0
- package/dist/cli/src/services/audit.service.js +111 -0
- package/dist/cli/src/services/auth.service.js +176 -0
- package/dist/cli/src/services/key.service.js +137 -0
- package/dist/cli/src/services/permission.service.js +142 -0
- package/dist/cli/src/services/project.service.js +102 -0
- package/dist/cli/src/types/audit.types.js +54 -0
- package/dist/cli/src/types/crypto.types.js +5 -0
- package/dist/cli/src/types/index.js +90 -0
- package/dist/cli/src/types/key.types.js +27 -0
- package/dist/cli/src/types/project.types.js +15 -0
- package/dist/cli/src/types/user.types.js +48 -0
- package/dist/cli/src/utils/config-loader.js +125 -0
- package/dist/cli/src/utils/constants.js +118 -0
- package/dist/cli/src/utils/error-handler.js +108 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -1
- package/dist/models/key.model.js +1 -1
- package/dist/models/key.model.js.map +1 -1
- package/dist/services/key.service.d.ts +5 -0
- package/dist/services/key.service.d.ts.map +1 -1
- package/dist/services/key.service.js +12 -4
- package/dist/services/key.service.js.map +1 -1
- package/dist/services/permission.service.d.ts.map +1 -1
- package/dist/services/permission.service.js +6 -1
- package/dist/services/permission.service.js.map +1 -1
- package/dist/src/client.js +254 -0
- package/dist/src/core/asymmetric-crypto.js +170 -0
- package/dist/src/core/crypto.js +99 -0
- package/dist/src/core/crypto.service.js +66 -0
- package/dist/src/core/key-derivation.js +95 -0
- package/dist/src/index.js +50 -0
- package/dist/src/models/audit.model.js +82 -0
- package/dist/src/models/key.model.js +119 -0
- package/dist/src/models/project.model.js +53 -0
- package/dist/src/models/user.model.js +140 -0
- package/dist/src/repositories/audit.repository.js +115 -0
- package/dist/src/repositories/base.repository.js +94 -0
- package/dist/src/repositories/key.repository.js +125 -0
- package/dist/src/repositories/project.repository.js +81 -0
- package/dist/src/repositories/user.repository.js +101 -0
- package/dist/src/services/audit.service.js +111 -0
- package/dist/src/services/auth.service.js +176 -0
- package/dist/src/services/key.service.js +137 -0
- package/dist/src/services/permission.service.js +142 -0
- package/dist/src/services/project.service.js +102 -0
- package/dist/src/types/audit.types.js +54 -0
- package/dist/src/types/crypto.types.js +5 -0
- package/dist/src/types/index.js +90 -0
- package/dist/src/types/key.types.js +27 -0
- package/dist/src/types/project.types.js +15 -0
- package/dist/src/types/user.types.js +48 -0
- package/dist/src/utils/config-loader.js +125 -0
- package/dist/src/utils/constants.js +118 -0
- package/dist/src/utils/error-handler.js +108 -0
- package/package.json +7 -2
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 密钥仓储
|
|
4
|
+
* 负责密钥的数据访问
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.KeyRepository = void 0;
|
|
8
|
+
const base_repository_1 = require("./base.repository");
|
|
9
|
+
const types_1 = require("../types");
|
|
10
|
+
const types_2 = require("../types");
|
|
11
|
+
class KeyRepository extends base_repository_1.BaseRepository {
|
|
12
|
+
constructor(db) {
|
|
13
|
+
super(db, 'keys');
|
|
14
|
+
this.initializeIndexes();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 初始化索引
|
|
18
|
+
*/
|
|
19
|
+
async initializeIndexes() {
|
|
20
|
+
await this.createIndexes([
|
|
21
|
+
{ keyId: 1 },
|
|
22
|
+
{ projectId: 1 },
|
|
23
|
+
{ keyName: 1 },
|
|
24
|
+
{ projectId: 1, keyName: 1 },
|
|
25
|
+
{ keyType: 1 },
|
|
26
|
+
{ status: 1 },
|
|
27
|
+
{ tags: 1 },
|
|
28
|
+
{ expiresAt: 1 },
|
|
29
|
+
{ createdAt: -1 },
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 根据密钥ID查找
|
|
34
|
+
*/
|
|
35
|
+
async findByKeyId(keyId) {
|
|
36
|
+
return await this.findOne({ keyId });
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 根据密钥ID查找,如果不存在则抛出错误
|
|
40
|
+
*/
|
|
41
|
+
async getByKeyId(keyId) {
|
|
42
|
+
const key = await this.findByKeyId(keyId);
|
|
43
|
+
if (!key) {
|
|
44
|
+
throw new types_2.KeyNotFoundError(keyId);
|
|
45
|
+
}
|
|
46
|
+
return key;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 根据项目ID和密钥名称查找
|
|
50
|
+
*/
|
|
51
|
+
async findByProjectAndName(projectId, keyName) {
|
|
52
|
+
return await this.findOne({ projectId, keyName });
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 查询项目的所有密钥
|
|
56
|
+
*/
|
|
57
|
+
async findByProjectId(projectId, filters, options) {
|
|
58
|
+
const query = { projectId };
|
|
59
|
+
if (filters?.keyType) {
|
|
60
|
+
query.keyType = filters.keyType;
|
|
61
|
+
}
|
|
62
|
+
if (filters?.status) {
|
|
63
|
+
query.status = filters.status;
|
|
64
|
+
}
|
|
65
|
+
if (filters?.tags && filters.tags.length > 0) {
|
|
66
|
+
query.tags = { $in: filters.tags };
|
|
67
|
+
}
|
|
68
|
+
if (filters?.search) {
|
|
69
|
+
query.keyName = { $regex: filters.search, $options: 'i' };
|
|
70
|
+
}
|
|
71
|
+
const total = await this.count(query);
|
|
72
|
+
const skip = options?.page && options?.limit ? (options.page - 1) * options.limit : 0;
|
|
73
|
+
const limit = options?.limit || 100;
|
|
74
|
+
const keys = await this.findMany(query, {
|
|
75
|
+
sort: { createdAt: -1 },
|
|
76
|
+
skip,
|
|
77
|
+
limit,
|
|
78
|
+
});
|
|
79
|
+
return { keys, total };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 更新密钥
|
|
83
|
+
*/
|
|
84
|
+
async updateKey(keyId, updates) {
|
|
85
|
+
return await this.updateOne({ keyId }, { $set: { ...updates, updatedAt: new Date() } });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 删除密钥(软删除)
|
|
89
|
+
*/
|
|
90
|
+
async softDeleteKey(keyId) {
|
|
91
|
+
return await this.updateOne({ keyId }, { $set: { status: types_1.KeyStatus.DELETED, updatedAt: new Date() } });
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 永久删除密钥
|
|
95
|
+
*/
|
|
96
|
+
async hardDeleteKey(keyId) {
|
|
97
|
+
return await this.deleteOne({ keyId });
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 批量查询密钥
|
|
101
|
+
*/
|
|
102
|
+
async findByKeyIds(keyIds) {
|
|
103
|
+
return await this.findMany({ keyId: { $in: keyIds } });
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 更新密钥最后访问时间
|
|
107
|
+
*/
|
|
108
|
+
async updateLastAccessed(keyId) {
|
|
109
|
+
return await this.updateOne({ keyId }, { $set: { lastAccessedAt: new Date() } });
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 查询过期密钥
|
|
113
|
+
*/
|
|
114
|
+
async findExpiredKeys(projectId) {
|
|
115
|
+
const query = {
|
|
116
|
+
expiresAt: { $lt: new Date() },
|
|
117
|
+
status: { $ne: types_1.KeyStatus.DELETED },
|
|
118
|
+
};
|
|
119
|
+
if (projectId) {
|
|
120
|
+
query.projectId = projectId;
|
|
121
|
+
}
|
|
122
|
+
return await this.findMany(query);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.KeyRepository = KeyRepository;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 项目仓储
|
|
4
|
+
* 负责项目的数据访问
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ProjectRepository = void 0;
|
|
8
|
+
const base_repository_1 = require("./base.repository");
|
|
9
|
+
const types_1 = require("../types");
|
|
10
|
+
const types_2 = require("../types");
|
|
11
|
+
class ProjectRepository extends base_repository_1.BaseRepository {
|
|
12
|
+
constructor(db) {
|
|
13
|
+
super(db, 'projects');
|
|
14
|
+
this.initializeIndexes();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 初始化索引
|
|
18
|
+
*/
|
|
19
|
+
async initializeIndexes() {
|
|
20
|
+
await this.createIndexes([
|
|
21
|
+
{ projectId: 1 },
|
|
22
|
+
{ projectName: 1 },
|
|
23
|
+
{ status: 1 },
|
|
24
|
+
{ createdAt: -1 },
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 根据项目ID查找
|
|
29
|
+
*/
|
|
30
|
+
async findByProjectId(projectId) {
|
|
31
|
+
return await this.findOne({ projectId });
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 根据项目ID查找,如果不存在则抛出错误
|
|
35
|
+
*/
|
|
36
|
+
async getByProjectId(projectId) {
|
|
37
|
+
const project = await this.findByProjectId(projectId);
|
|
38
|
+
if (!project) {
|
|
39
|
+
throw new types_2.ProjectNotFoundError(projectId);
|
|
40
|
+
}
|
|
41
|
+
return project;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 根据项目名称查找
|
|
45
|
+
*/
|
|
46
|
+
async findByProjectName(projectName) {
|
|
47
|
+
return await this.findOne({ projectName });
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 查询项目列表
|
|
51
|
+
*/
|
|
52
|
+
async findProjects(filter) {
|
|
53
|
+
const query = {};
|
|
54
|
+
if (filter?.status) {
|
|
55
|
+
query.status = filter.status;
|
|
56
|
+
}
|
|
57
|
+
if (filter?.projectName) {
|
|
58
|
+
query.projectName = { $regex: filter.projectName, $options: 'i' };
|
|
59
|
+
}
|
|
60
|
+
return await this.findMany(query, { sort: { createdAt: -1 } });
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 更新项目
|
|
64
|
+
*/
|
|
65
|
+
async updateProject(projectId, updates) {
|
|
66
|
+
return await this.updateOne({ projectId }, { $set: { ...updates, updatedAt: new Date() } });
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 删除项目(软删除)
|
|
70
|
+
*/
|
|
71
|
+
async softDeleteProject(projectId) {
|
|
72
|
+
return await this.updateOne({ projectId }, { $set: { status: types_1.ProjectStatus.DELETED, updatedAt: new Date() } });
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 永久删除项目
|
|
76
|
+
*/
|
|
77
|
+
async hardDeleteProject(projectId) {
|
|
78
|
+
return await this.deleteOne({ projectId });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.ProjectRepository = ProjectRepository;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 用户仓储
|
|
4
|
+
* 负责用户的数据访问
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.UserRepository = void 0;
|
|
8
|
+
const base_repository_1 = require("./base.repository");
|
|
9
|
+
const types_1 = require("../types");
|
|
10
|
+
const types_2 = require("../types");
|
|
11
|
+
class UserRepository extends base_repository_1.BaseRepository {
|
|
12
|
+
constructor(db) {
|
|
13
|
+
super(db, 'users');
|
|
14
|
+
this.initializeIndexes();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 初始化索引
|
|
18
|
+
*/
|
|
19
|
+
async initializeIndexes() {
|
|
20
|
+
await this.createIndexes([
|
|
21
|
+
{ userId: 1 },
|
|
22
|
+
{ projectId: 1 },
|
|
23
|
+
{ username: 1 },
|
|
24
|
+
{ projectId: 1, username: 1 },
|
|
25
|
+
{ status: 1 },
|
|
26
|
+
{ apiKeyHash: 1 },
|
|
27
|
+
{ createdAt: -1 },
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 根据用户ID查找
|
|
32
|
+
*/
|
|
33
|
+
async findByUserId(userId) {
|
|
34
|
+
return await this.findOne({ userId });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 根据用户ID查找,如果不存在则抛出错误
|
|
38
|
+
*/
|
|
39
|
+
async getByUserId(userId) {
|
|
40
|
+
const user = await this.findByUserId(userId);
|
|
41
|
+
if (!user) {
|
|
42
|
+
throw new types_2.UserNotFoundError(userId);
|
|
43
|
+
}
|
|
44
|
+
return user;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 根据项目ID和用户名查找
|
|
48
|
+
*/
|
|
49
|
+
async findByProjectAndUsername(projectId, username) {
|
|
50
|
+
return await this.findOne({ projectId, username });
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 查询项目的所有用户
|
|
54
|
+
*/
|
|
55
|
+
async findByProjectId(projectId) {
|
|
56
|
+
return await this.findMany({ projectId }, { sort: { createdAt: -1 } });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 根据API密钥哈希查找用户
|
|
60
|
+
*/
|
|
61
|
+
async findByApiKeyHash(apiKeyHash) {
|
|
62
|
+
return await this.findOne({ apiKeyHash });
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 更新用户
|
|
66
|
+
*/
|
|
67
|
+
async updateUser(userId, updates) {
|
|
68
|
+
return await this.updateOne({ userId }, { $set: { ...updates, updatedAt: new Date() } });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 更新最后登录时间
|
|
72
|
+
*/
|
|
73
|
+
async updateLastLogin(userId) {
|
|
74
|
+
return await this.updateOne({ userId }, { $set: { lastLoginAt: new Date() } });
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 锁定用户
|
|
78
|
+
*/
|
|
79
|
+
async lockUser(userId) {
|
|
80
|
+
return await this.updateOne({ userId }, { $set: { status: types_1.UserStatus.LOCKED, updatedAt: new Date() } });
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 激活用户
|
|
84
|
+
*/
|
|
85
|
+
async activateUser(userId) {
|
|
86
|
+
return await this.updateOne({ userId }, { $set: { status: types_1.UserStatus.ACTIVE, updatedAt: new Date() } });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 删除用户(软删除)
|
|
90
|
+
*/
|
|
91
|
+
async softDeleteUser(userId) {
|
|
92
|
+
return await this.updateOne({ userId }, { $set: { status: types_1.UserStatus.INACTIVE, updatedAt: new Date() } });
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 永久删除用户
|
|
96
|
+
*/
|
|
97
|
+
async hardDeleteUser(userId) {
|
|
98
|
+
return await this.deleteOne({ userId });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.UserRepository = UserRepository;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 审计服务
|
|
4
|
+
* 负责记录和查询审计日志
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AuditService = void 0;
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const audit_model_1 = require("../models/audit.model");
|
|
10
|
+
class AuditService {
|
|
11
|
+
constructor(auditRepo) {
|
|
12
|
+
this.auditRepo = auditRepo;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 记录审计日志
|
|
16
|
+
*/
|
|
17
|
+
async log(data) {
|
|
18
|
+
const log = (0, audit_model_1.createAuditLog)(data);
|
|
19
|
+
await this.auditRepo.insertOne(log);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 查询审计日志
|
|
23
|
+
*/
|
|
24
|
+
async getAuditLogs(projectId, query) {
|
|
25
|
+
const result = await this.auditRepo.findAuditLogs(projectId, query);
|
|
26
|
+
return {
|
|
27
|
+
logs: result.logs,
|
|
28
|
+
total: result.total,
|
|
29
|
+
page: query.page || 1,
|
|
30
|
+
limit: query.limit || 50,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 获取最近的审计日志
|
|
35
|
+
*/
|
|
36
|
+
async getRecentLogs(projectId, limit = 100) {
|
|
37
|
+
return await this.auditRepo.findRecentLogs(projectId, limit);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 统计失败登录次数
|
|
41
|
+
*/
|
|
42
|
+
async countFailedLogins(projectId, userId, since) {
|
|
43
|
+
return await this.auditRepo.countFailedLogins(projectId, userId, since);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 记录项目创建
|
|
47
|
+
*/
|
|
48
|
+
async logProjectCreated(projectId, userId, projectName, success) {
|
|
49
|
+
await this.log({
|
|
50
|
+
projectId,
|
|
51
|
+
userId,
|
|
52
|
+
action: types_1.AuditAction.CREATE_PROJECT,
|
|
53
|
+
resourceType: types_1.ResourceType.PROJECT,
|
|
54
|
+
resourceId: projectId,
|
|
55
|
+
details: {
|
|
56
|
+
keyName: projectName,
|
|
57
|
+
success,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 记录密钥创建
|
|
63
|
+
*/
|
|
64
|
+
async logKeyCreated(projectId, userId, keyId, keyName, keyType, success) {
|
|
65
|
+
await this.log({
|
|
66
|
+
projectId,
|
|
67
|
+
userId,
|
|
68
|
+
action: types_1.AuditAction.CREATE_KEY,
|
|
69
|
+
resourceType: types_1.ResourceType.KEY,
|
|
70
|
+
resourceId: keyId,
|
|
71
|
+
details: {
|
|
72
|
+
keyName,
|
|
73
|
+
keyType,
|
|
74
|
+
success,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 记录密钥读取
|
|
80
|
+
*/
|
|
81
|
+
async logKeyRead(projectId, userId, keyId, keyName, success) {
|
|
82
|
+
await this.log({
|
|
83
|
+
projectId,
|
|
84
|
+
userId,
|
|
85
|
+
action: types_1.AuditAction.READ_KEY,
|
|
86
|
+
resourceType: types_1.ResourceType.KEY,
|
|
87
|
+
resourceId: keyId,
|
|
88
|
+
details: {
|
|
89
|
+
keyName,
|
|
90
|
+
success,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 记录密钥删除
|
|
96
|
+
*/
|
|
97
|
+
async logKeyDeleted(projectId, userId, keyId, keyName, success) {
|
|
98
|
+
await this.log({
|
|
99
|
+
projectId,
|
|
100
|
+
userId,
|
|
101
|
+
action: types_1.AuditAction.DELETE_KEY,
|
|
102
|
+
resourceType: types_1.ResourceType.KEY,
|
|
103
|
+
resourceId: keyId,
|
|
104
|
+
details: {
|
|
105
|
+
keyName,
|
|
106
|
+
success,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.AuditService = AuditService;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 认证服务
|
|
4
|
+
* 负责用户认证和授权
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AuthService = void 0;
|
|
8
|
+
const user_model_1 = require("../models/user.model");
|
|
9
|
+
const bcrypt_1 = require("bcrypt");
|
|
10
|
+
const crypto_1 = require("crypto");
|
|
11
|
+
const types_1 = require("../types");
|
|
12
|
+
const types_2 = require("../types");
|
|
13
|
+
class AuthService {
|
|
14
|
+
constructor(userRepo, auditService) {
|
|
15
|
+
this.userRepo = userRepo;
|
|
16
|
+
this.auditService = auditService;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 创建用户
|
|
20
|
+
*/
|
|
21
|
+
async createUser(projectId, creatorId, userData) {
|
|
22
|
+
// 验证用户数据
|
|
23
|
+
const validation = (0, user_model_1.validateUser)(userData);
|
|
24
|
+
if (!validation.valid) {
|
|
25
|
+
throw new types_1.ValidationError(validation.errors.join(', '));
|
|
26
|
+
}
|
|
27
|
+
// 检查用户名是否已存在
|
|
28
|
+
const existingUser = await this.userRepo.findByProjectAndUsername(projectId, userData.username);
|
|
29
|
+
if (existingUser) {
|
|
30
|
+
throw new types_1.ValidationError('Username already exists');
|
|
31
|
+
}
|
|
32
|
+
// 哈希密码
|
|
33
|
+
const passwordHash = await (0, bcrypt_1.hash)(userData.password, 10);
|
|
34
|
+
// 生成API密钥
|
|
35
|
+
const apiKey = this.generateApiKey();
|
|
36
|
+
const apiKeyHash = await (0, bcrypt_1.hash)(apiKey, 10);
|
|
37
|
+
const user = (0, user_model_1.createUser)(projectId, userData, passwordHash, apiKeyHash);
|
|
38
|
+
await this.userRepo.insertOne(user);
|
|
39
|
+
// 记录审计日志
|
|
40
|
+
await this.auditService.log({
|
|
41
|
+
projectId,
|
|
42
|
+
userId: creatorId,
|
|
43
|
+
action: types_2.AuditAction.CREATE_USER,
|
|
44
|
+
resourceType: types_2.ResourceType.USER,
|
|
45
|
+
resourceId: user.userId,
|
|
46
|
+
details: {
|
|
47
|
+
success: true,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
// 返回用户信息(不包含敏感信息)和API密钥
|
|
51
|
+
return {
|
|
52
|
+
...(0, user_model_1.toSafeUser)(user),
|
|
53
|
+
apiKey, // 仅在创建时返回一次
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 用户登录
|
|
58
|
+
*/
|
|
59
|
+
async login(projectId, credentials) {
|
|
60
|
+
const user = await this.userRepo.findByProjectAndUsername(projectId, credentials.username);
|
|
61
|
+
if (!user) {
|
|
62
|
+
await this.auditService.log({
|
|
63
|
+
projectId,
|
|
64
|
+
action: types_2.AuditAction.LOGIN_FAILED,
|
|
65
|
+
resourceType: types_2.ResourceType.USER,
|
|
66
|
+
resourceId: credentials.username,
|
|
67
|
+
details: {
|
|
68
|
+
success: false,
|
|
69
|
+
errorMessage: 'User not found',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
error: 'Invalid username or password',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (user.status !== 'active') {
|
|
78
|
+
await this.auditService.log({
|
|
79
|
+
projectId,
|
|
80
|
+
userId: user.userId,
|
|
81
|
+
action: types_2.AuditAction.LOGIN_FAILED,
|
|
82
|
+
resourceType: types_2.ResourceType.USER,
|
|
83
|
+
resourceId: user.userId,
|
|
84
|
+
details: {
|
|
85
|
+
success: false,
|
|
86
|
+
errorMessage: 'User account is not active',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: 'User account is not active',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const passwordMatch = await (0, bcrypt_1.compare)(credentials.password, user.passwordHash);
|
|
95
|
+
if (!passwordMatch) {
|
|
96
|
+
await this.auditService.log({
|
|
97
|
+
projectId,
|
|
98
|
+
userId: user.userId,
|
|
99
|
+
action: types_2.AuditAction.LOGIN_FAILED,
|
|
100
|
+
resourceType: types_2.ResourceType.USER,
|
|
101
|
+
resourceId: user.userId,
|
|
102
|
+
details: {
|
|
103
|
+
success: false,
|
|
104
|
+
errorMessage: 'Invalid password',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
error: 'Invalid username or password',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// 更新最后登录时间
|
|
113
|
+
await this.userRepo.updateLastLogin(user.userId);
|
|
114
|
+
// 记录审计日志
|
|
115
|
+
await this.auditService.log({
|
|
116
|
+
projectId,
|
|
117
|
+
userId: user.userId,
|
|
118
|
+
action: types_2.AuditAction.LOGIN,
|
|
119
|
+
resourceType: types_2.ResourceType.USER,
|
|
120
|
+
resourceId: user.userId,
|
|
121
|
+
details: {
|
|
122
|
+
success: true,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
user: (0, user_model_1.toSafeUser)(user),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 使用API密钥认证
|
|
132
|
+
*/
|
|
133
|
+
async authenticateWithApiKey(projectId, apiKey) {
|
|
134
|
+
// 查找所有用户并检查API密钥
|
|
135
|
+
const users = await this.userRepo.findByProjectId(projectId);
|
|
136
|
+
for (const user of users) {
|
|
137
|
+
if (user.apiKeyHash && await (0, bcrypt_1.compare)(apiKey, user.apiKeyHash)) {
|
|
138
|
+
if (user.status === 'active') {
|
|
139
|
+
return user;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 验证用户凭证
|
|
147
|
+
*/
|
|
148
|
+
async verifyCredentials(projectId, username, password) {
|
|
149
|
+
const result = await this.login(projectId, { username, password });
|
|
150
|
+
return result.success;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 生成API密钥
|
|
154
|
+
*/
|
|
155
|
+
generateApiKey() {
|
|
156
|
+
const apiKeyPrefix = 'kms_';
|
|
157
|
+
const randomBytesBuffer = (0, crypto_1.randomBytes)(32);
|
|
158
|
+
const randomString = randomBytesBuffer.toString('hex');
|
|
159
|
+
return `${apiKeyPrefix}${randomString}`;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 轮换API密钥
|
|
163
|
+
*/
|
|
164
|
+
async rotateApiKey(projectId, userId, targetUserId) {
|
|
165
|
+
const user = await this.userRepo.getByUserId(targetUserId);
|
|
166
|
+
if (user.projectId !== projectId) {
|
|
167
|
+
throw new types_1.ValidationError('User does not belong to this project');
|
|
168
|
+
}
|
|
169
|
+
// 生成新API密钥
|
|
170
|
+
const newApiKey = this.generateApiKey();
|
|
171
|
+
const newApiKeyHash = await (0, bcrypt_1.hash)(newApiKey, 10);
|
|
172
|
+
await this.userRepo.updateUser(targetUserId, { apiKeyHash: newApiKeyHash });
|
|
173
|
+
return newApiKey;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.AuthService = AuthService;
|