@sustaina/iam-middleware 1.0.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 +61 -0
- package/dist/auth/AuthMiddleware.d.ts +51 -0
- package/dist/auth/AuthMiddleware.d.ts.map +1 -0
- package/dist/auth/AuthMiddleware.js +312 -0
- package/dist/auth/AuthMiddleware.js.map +1 -0
- package/dist/auth/ImplementModeMiddleware.d.ts +26 -0
- package/dist/auth/ImplementModeMiddleware.d.ts.map +1 -0
- package/dist/auth/ImplementModeMiddleware.js +77 -0
- package/dist/auth/ImplementModeMiddleware.js.map +1 -0
- package/dist/base/BaseMiddleware.d.ts +15 -0
- package/dist/base/BaseMiddleware.d.ts.map +1 -0
- package/dist/base/BaseMiddleware.js +50 -0
- package/dist/base/BaseMiddleware.js.map +1 -0
- package/dist/domain/entities/AggregateRoot.d.ts +17 -0
- package/dist/domain/entities/AggregateRoot.d.ts.map +1 -0
- package/dist/domain/entities/AggregateRoot.js +79 -0
- package/dist/domain/entities/AggregateRoot.js.map +1 -0
- package/dist/domain/entities/Currency.d.ts +26 -0
- package/dist/domain/entities/Currency.d.ts.map +1 -0
- package/dist/domain/entities/Currency.js +51 -0
- package/dist/domain/entities/Currency.js.map +1 -0
- package/dist/domain/entities/OutboxEvent.d.ts +17 -0
- package/dist/domain/entities/OutboxEvent.d.ts.map +1 -0
- package/dist/domain/entities/OutboxEvent.js +24 -0
- package/dist/domain/entities/OutboxEvent.js.map +1 -0
- package/dist/domain/event/DomainEvent.d.ts +38 -0
- package/dist/domain/event/DomainEvent.d.ts.map +1 -0
- package/dist/domain/event/DomainEvent.js +20 -0
- package/dist/domain/event/DomainEvent.js.map +1 -0
- package/dist/domain/repositories/OutboxRepository.d.ts +7 -0
- package/dist/domain/repositories/OutboxRepository.d.ts.map +1 -0
- package/dist/domain/repositories/OutboxRepository.js +3 -0
- package/dist/domain/repositories/OutboxRepository.js.map +1 -0
- package/dist/errors/index.d.ts +71 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +150 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/RedisClient.d.ts +14 -0
- package/dist/infrastructure/RedisClient.d.ts.map +1 -0
- package/dist/infrastructure/RedisClient.js +69 -0
- package/dist/infrastructure/RedisClient.js.map +1 -0
- package/dist/logging/EventLogMiddleware.d.ts +4 -0
- package/dist/logging/EventLogMiddleware.d.ts.map +1 -0
- package/dist/logging/EventLogMiddleware.js +49 -0
- package/dist/logging/EventLogMiddleware.js.map +1 -0
- package/dist/shared/config.d.ts +2 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/config.js +5 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/test.d.ts +4 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +80 -0
- package/dist/test.js.map +1 -0
- package/dist/types/AuthTypes.d.ts +43 -0
- package/dist/types/AuthTypes.d.ts.map +1 -0
- package/dist/types/AuthTypes.js +3 -0
- package/dist/types/AuthTypes.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @sustaina/iam-middleware
|
|
2
|
+
|
|
3
|
+
## For CD
|
|
4
|
+
add these line in ```kustomization.yaml``` file in ```*-deploy``` repository
|
|
5
|
+
```
|
|
6
|
+
patch: |-
|
|
7
|
+
- op: add
|
|
8
|
+
path: ...
|
|
9
|
+
value:
|
|
10
|
+
- secretRef:
|
|
11
|
+
name: your-secret
|
|
12
|
+
- secretRef:
|
|
13
|
+
name: iam-middleware-dev-secret # <--- add this
|
|
14
|
+
# change dev to -> sit / uat
|
|
15
|
+
```
|
|
16
|
+
now you good to go.
|
|
17
|
+
## Example Usage
|
|
18
|
+
```
|
|
19
|
+
import "dotenv/config";
|
|
20
|
+
import fastify from "fastify";
|
|
21
|
+
import pino from "pino";
|
|
22
|
+
import { AuthMiddleware } from "./auth/AuthMiddleware";
|
|
23
|
+
import { ImplementModeMiddleware } from "./auth/ImplementModeMiddleware";
|
|
24
|
+
|
|
25
|
+
const logger = pino({
|
|
26
|
+
level: process.env.LOG_LEVEL || "info",
|
|
27
|
+
transport: { target: "pino-pretty", options: { colorize: true } },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
async function createApp() {
|
|
31
|
+
const app = fastify();
|
|
32
|
+
|
|
33
|
+
const authMiddleware = new AuthMiddleware();
|
|
34
|
+
const implementMode = new ImplementModeMiddleware();
|
|
35
|
+
|
|
36
|
+
// Register standardized routes
|
|
37
|
+
await app.register(
|
|
38
|
+
async (appInstance) => {
|
|
39
|
+
appInstance.get(
|
|
40
|
+
"/test",
|
|
41
|
+
{
|
|
42
|
+
preHandler: [
|
|
43
|
+
authMiddleware.authenticate.bind(authMiddleware), // required for implementMode.authorise and authMiddleware.permissionGuard
|
|
44
|
+
implementMode.authorise.bind(implementMode),
|
|
45
|
+
authMiddleware.permissionGuard([{ access: "canRead", subProgram: "test" }]).bind(authMiddleware),
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
async (request, reply) => {
|
|
49
|
+
return reply.send({
|
|
50
|
+
success: true,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
{ prefix: "/api/v1" }
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return app;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import { AuthMiddlewareOptions, JwtPayload, UserInfo, PermissionGuardParams } from "../types/AuthTypes";
|
|
3
|
+
import { BaseMiddleware } from "../base/BaseMiddleware";
|
|
4
|
+
declare module "fastify" {
|
|
5
|
+
interface FastifyRequest {
|
|
6
|
+
user?: UserInfo;
|
|
7
|
+
payload?: JwtPayload;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export declare class AuthMiddleware extends BaseMiddleware {
|
|
11
|
+
private readonly jwtSecret;
|
|
12
|
+
/**
|
|
13
|
+
* It allows providing the Redis connection through these options
|
|
14
|
+
* 1. a URL string
|
|
15
|
+
* 2. a pre-configured Redis client instance.
|
|
16
|
+
* 3. IAM_REDIS_URL env **(recommended)**
|
|
17
|
+
*
|
|
18
|
+
* And provide jwt secret via IAM_JWT_SECRET env **(recommended)**
|
|
19
|
+
*
|
|
20
|
+
* @param {AuthMiddlewareOptions} [options.serviceName] - An optional unique name for the service using this middleware.
|
|
21
|
+
* @param {AuthMiddlewareOptions} [options.redisUrl] - A optional connection URL for the Redis server (e.g., 'redis://localhost:6379').
|
|
22
|
+
* @param {AuthMiddlewareOptions} [options.redisClient] - A optional pre-configured Redis client instance.
|
|
23
|
+
* @param {AuthMiddlewareOptions} [options.jwtSecret] - A optional jwt secret.
|
|
24
|
+
*/
|
|
25
|
+
constructor(options?: AuthMiddlewareOptions);
|
|
26
|
+
private verifyJwt;
|
|
27
|
+
private checkAllowList;
|
|
28
|
+
/**
|
|
29
|
+
* A middleware to check if jwt token is valid and **populate request object with user and payload info**.
|
|
30
|
+
* @example
|
|
31
|
+
* const authMiddleware = new AuthMiddleware()
|
|
32
|
+
* const prehandler = [authMiddleware.authenticate.bind(authMiddleware)]
|
|
33
|
+
*/
|
|
34
|
+
authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
|
|
35
|
+
private checkPermission;
|
|
36
|
+
/**
|
|
37
|
+
* A middleware to check if user has permissions.
|
|
38
|
+
* @example
|
|
39
|
+
* const authMiddleware = new AuthMiddleware()
|
|
40
|
+
* const prehandler = [
|
|
41
|
+
* authMiddleware.authenticate.bind(authMiddleware), // required
|
|
42
|
+
* authMiddleware.permissionGuard([
|
|
43
|
+
* { access: "canRead", subProgram: "sub1" },
|
|
44
|
+
* { access: "canRead", subProgram: "sub2" }
|
|
45
|
+
* ]).bind(authMiddleware)
|
|
46
|
+
* ]
|
|
47
|
+
*/
|
|
48
|
+
permissionGuard: (params: PermissionGuardParams) => (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
|
|
49
|
+
optionalAuth: (request: FastifyRequest) => Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=AuthMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthMiddleware.d.ts","sourceRoot":"","sources":["../../src/auth/AuthMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAIvD,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,QAAQ,EAER,qBAAqB,EAEtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAKxD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,IAAI,CAAC,EAAE,QAAQ,CAAC;QAChB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB;CACF;AAED,qBAAa,cAAe,SAAQ,cAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC;;;;;;;;;;;;OAYG;gBACS,OAAO,CAAC,EAAE,qBAAqB;IAK3C,OAAO,CAAC,SAAS,CAEf;IAEF,OAAO,CAAC,cAAc,CAqBpB;IAEF;;;;;OAKG;IACH,YAAY,GAAU,SAAS,cAAc,EAAE,OAAO,YAAY,wBA8HhE;IAEF,OAAO,CAAC,eAAe,CA2ErB;IAEF;;;;;;;;;;;OAWG;IACH,eAAe,GAAI,QAAQ,qBAAqB,MAAY,SAAS,cAAc,EAAE,OAAO,YAAY,wBAwBtG;IAEF,YAAY,GAAU,SAAS,cAAc,mBAqD3C;CACH"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AuthMiddleware = void 0;
|
|
7
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
|
+
const api_1 = require("@opentelemetry/api");
|
|
9
|
+
const pino_1 = __importDefault(require("pino"));
|
|
10
|
+
const BaseMiddleware_1 = require("../base/BaseMiddleware");
|
|
11
|
+
const logger = (0, pino_1.default)({ name: "AuthMiddleware" });
|
|
12
|
+
class AuthMiddleware extends BaseMiddleware_1.BaseMiddleware {
|
|
13
|
+
/**
|
|
14
|
+
* It allows providing the Redis connection through these options
|
|
15
|
+
* 1. a URL string
|
|
16
|
+
* 2. a pre-configured Redis client instance.
|
|
17
|
+
* 3. IAM_REDIS_URL env **(recommended)**
|
|
18
|
+
*
|
|
19
|
+
* And provide jwt secret via IAM_JWT_SECRET env **(recommended)**
|
|
20
|
+
*
|
|
21
|
+
* @param {AuthMiddlewareOptions} [options.serviceName] - An optional unique name for the service using this middleware.
|
|
22
|
+
* @param {AuthMiddlewareOptions} [options.redisUrl] - A optional connection URL for the Redis server (e.g., 'redis://localhost:6379').
|
|
23
|
+
* @param {AuthMiddlewareOptions} [options.redisClient] - A optional pre-configured Redis client instance.
|
|
24
|
+
* @param {AuthMiddlewareOptions} [options.jwtSecret] - A optional jwt secret.
|
|
25
|
+
*/
|
|
26
|
+
constructor(options) {
|
|
27
|
+
super({ ...options, name: "AuthMiddleware" });
|
|
28
|
+
this.verifyJwt = (token) => {
|
|
29
|
+
return jsonwebtoken_1.default.verify(token, this.jwtSecret);
|
|
30
|
+
};
|
|
31
|
+
this.checkAllowList = async (payload, token) => {
|
|
32
|
+
const allowlistKey = `auth:session:${payload.id}`;
|
|
33
|
+
const tokenJti = payload.jti;
|
|
34
|
+
if (!tokenJti) {
|
|
35
|
+
logger.warn({ token }, "Missing jti in token");
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const jtiInRedis = await this.redisClient.get(allowlistKey);
|
|
40
|
+
const isAllowlisted = jtiInRedis === tokenJti;
|
|
41
|
+
logger.debug({ allowlistKey, tokenJti, jtiInRedis, isAllowlisted }, "Checked Token allowlist in Redis");
|
|
42
|
+
return isAllowlisted;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
logger.error({ err, allowlistKey }, "Redis allowlist check failed");
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* A middleware to check if jwt token is valid and **populate request object with user and payload info**.
|
|
51
|
+
* @example
|
|
52
|
+
* const authMiddleware = new AuthMiddleware()
|
|
53
|
+
* const prehandler = [authMiddleware.authenticate.bind(authMiddleware)]
|
|
54
|
+
*/
|
|
55
|
+
this.authenticate = async (request, reply) => {
|
|
56
|
+
const tracer = api_1.trace.getTracer(this.serviceName);
|
|
57
|
+
const span = tracer.startSpan("AuthMiddleware.authenticate");
|
|
58
|
+
try {
|
|
59
|
+
const authHeader = request.headers.authorization;
|
|
60
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
61
|
+
span.setAttributes({
|
|
62
|
+
"error.type": "missing_token",
|
|
63
|
+
"error.message": "No authorization token provided",
|
|
64
|
+
});
|
|
65
|
+
logger.warn({ url: request.url }, "Authentication failed: No token provided");
|
|
66
|
+
return reply.status(401).send({
|
|
67
|
+
error: "Authentication Error",
|
|
68
|
+
message: "Authorization token is required",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const token = authHeader.substring(7);
|
|
72
|
+
// Verify JWT token
|
|
73
|
+
let payload;
|
|
74
|
+
try {
|
|
75
|
+
payload = this.verifyJwt(token);
|
|
76
|
+
request.payload = payload;
|
|
77
|
+
}
|
|
78
|
+
catch (jwtError) {
|
|
79
|
+
span.setAttributes({
|
|
80
|
+
"error.type": "invalid_token",
|
|
81
|
+
"error.message": "Token verification failed",
|
|
82
|
+
});
|
|
83
|
+
logger.warn({
|
|
84
|
+
url: request.url,
|
|
85
|
+
error: jwtError instanceof Error ? jwtError.message : "Unknown JWT error",
|
|
86
|
+
}, "Authentication failed: Invalid token");
|
|
87
|
+
return reply.status(401).send({
|
|
88
|
+
error: "Authentication Error",
|
|
89
|
+
message: "Invalid or expired token",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// Validate payload structure
|
|
93
|
+
if (!payload.id ||
|
|
94
|
+
!payload.name ||
|
|
95
|
+
!payload.email ||
|
|
96
|
+
!payload.tenantId ||
|
|
97
|
+
!payload.type ||
|
|
98
|
+
!payload.tenantLocale ||
|
|
99
|
+
!payload.passwordPolicy ||
|
|
100
|
+
payload.isDenyPasswordChange === undefined ||
|
|
101
|
+
payload.isPasswordSendEmail === undefined) {
|
|
102
|
+
span.setAttributes({
|
|
103
|
+
"error.type": "invalid_payload",
|
|
104
|
+
"error.message": "Token payload is incomplete",
|
|
105
|
+
});
|
|
106
|
+
logger.warn({ url: request.url, payload }, "Authentication failed: Invalid token payload");
|
|
107
|
+
return reply.status(401).send({
|
|
108
|
+
error: "Authentication Error",
|
|
109
|
+
message: "Invalid token payload",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Check allowlist (Redis)
|
|
113
|
+
try {
|
|
114
|
+
const result = await this.checkAllowList(payload, token);
|
|
115
|
+
if (!result) {
|
|
116
|
+
return reply.status(401).send({
|
|
117
|
+
error: "Authentication Error",
|
|
118
|
+
message: "Token is not in allow list",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
logger.error({ error: error.message, url: request.url }, "Token validation failed in AuthMiddleware");
|
|
124
|
+
return reply.status(500).send({
|
|
125
|
+
error: "Internal Server Error",
|
|
126
|
+
message: "Error while checking token validity",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// Attach user info to request
|
|
130
|
+
request.user = {
|
|
131
|
+
id: payload.id,
|
|
132
|
+
name: payload.name,
|
|
133
|
+
email: payload.email,
|
|
134
|
+
type: payload.type,
|
|
135
|
+
};
|
|
136
|
+
span.setAttributes({
|
|
137
|
+
"user.id": payload.id,
|
|
138
|
+
"user.email": payload.email,
|
|
139
|
+
"user.name": payload.name,
|
|
140
|
+
"user.username": payload.username,
|
|
141
|
+
"auth.success": true,
|
|
142
|
+
});
|
|
143
|
+
logger.debug({
|
|
144
|
+
userId: payload.id,
|
|
145
|
+
email: payload.email,
|
|
146
|
+
url: request.url,
|
|
147
|
+
}, "User authenticated successfully");
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
span.recordException(error);
|
|
151
|
+
span.setAttributes({
|
|
152
|
+
"error.type": "middleware_error",
|
|
153
|
+
});
|
|
154
|
+
logger.error({ error, url: request.url }, "Error in authentication middleware");
|
|
155
|
+
return reply.status(500).send({
|
|
156
|
+
error: "Internal Server Error",
|
|
157
|
+
message: "Authentication service error",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
span.end();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
this.checkPermission = async ({ userId, access, subProgram }, reply, payload) => {
|
|
165
|
+
// skip if user is admin
|
|
166
|
+
if (payload.type === "administrator") {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const redis = this.redisClient;
|
|
171
|
+
const pipeline = redis.multi();
|
|
172
|
+
const cacheKey = `auth:permission:${subProgram.replace(/\s+/g, "")}:${userId}`;
|
|
173
|
+
const get = await redis.get(cacheKey);
|
|
174
|
+
const accessKey = get ? JSON.parse(get) : null;
|
|
175
|
+
if (!accessKey) {
|
|
176
|
+
return reply.status(403).send({
|
|
177
|
+
error: "PermissionNotFound",
|
|
178
|
+
message: `Permission not found for subprogram ${subProgram}`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
await pipeline.exec();
|
|
182
|
+
const ErrorCustom = (message) => reply.status(403).send({
|
|
183
|
+
error: "PermissionDenied",
|
|
184
|
+
message,
|
|
185
|
+
});
|
|
186
|
+
switch (access) {
|
|
187
|
+
case "canCreate":
|
|
188
|
+
if (!accessKey.canCreate) {
|
|
189
|
+
return ErrorCustom("Permission denied: cannot create");
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
case "canRead":
|
|
193
|
+
if (!accessKey.canRead) {
|
|
194
|
+
return ErrorCustom("Permission denied: cannot read");
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
case "canEdit":
|
|
198
|
+
if (!accessKey.canEdit) {
|
|
199
|
+
return ErrorCustom("Permission denied: cannot edit");
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
case "canDelete":
|
|
203
|
+
if (!accessKey.canDelete) {
|
|
204
|
+
return ErrorCustom("Permission denied: cannot delete");
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
case "canNotify":
|
|
208
|
+
if (!accessKey.canNotify) {
|
|
209
|
+
return ErrorCustom("Permission denied: cannot notify");
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
case "canCreateDraft":
|
|
213
|
+
if (!accessKey.canCreateDraft) {
|
|
214
|
+
return ErrorCustom("Permission denied: cannot create draft");
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
default:
|
|
218
|
+
return ErrorCustom(`Unknown access type: ${access}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
logger.error({ err, userId }, "Failed to check user tenant permissions");
|
|
223
|
+
return reply.status(500).send({
|
|
224
|
+
error: "Internal Server Error",
|
|
225
|
+
message: "Redis error while checking permissions",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
/**
|
|
230
|
+
* A middleware to check if user has permissions.
|
|
231
|
+
* @example
|
|
232
|
+
* const authMiddleware = new AuthMiddleware()
|
|
233
|
+
* const prehandler = [
|
|
234
|
+
* authMiddleware.authenticate.bind(authMiddleware), // required
|
|
235
|
+
* authMiddleware.permissionGuard([
|
|
236
|
+
* { access: "canRead", subProgram: "sub1" },
|
|
237
|
+
* { access: "canRead", subProgram: "sub2" }
|
|
238
|
+
* ]).bind(authMiddleware)
|
|
239
|
+
* ]
|
|
240
|
+
*/
|
|
241
|
+
this.permissionGuard = (params) => async (request, reply) => {
|
|
242
|
+
let pool = [];
|
|
243
|
+
if (!Array.isArray(params)) {
|
|
244
|
+
pool = [params];
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
pool = params;
|
|
248
|
+
}
|
|
249
|
+
if (!request.user) {
|
|
250
|
+
logger.error("permissionGuard: no user info from AuthMiddleware.authenticate");
|
|
251
|
+
return reply.status(401).send({ error: "Authentication Error", message: "User not authenticated" });
|
|
252
|
+
}
|
|
253
|
+
await Promise.all(pool.map((v) => this.checkPermission({
|
|
254
|
+
userId: request.user.id,
|
|
255
|
+
subProgram: v.subProgram,
|
|
256
|
+
access: v.access,
|
|
257
|
+
}, reply, request.payload)));
|
|
258
|
+
};
|
|
259
|
+
this.optionalAuth = async (request) => {
|
|
260
|
+
const tracer = api_1.trace.getTracer(this.serviceName);
|
|
261
|
+
const span = tracer.startSpan("AuthMiddleware.optionalAuth");
|
|
262
|
+
try {
|
|
263
|
+
const authHeader = request.headers.authorization;
|
|
264
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
265
|
+
span.setAttributes({
|
|
266
|
+
"auth.optional": true,
|
|
267
|
+
"auth.token_present": false,
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const token = authHeader.substring(7);
|
|
272
|
+
try {
|
|
273
|
+
const payload = this.verifyJwt(token);
|
|
274
|
+
if (payload.id &&
|
|
275
|
+
payload.name &&
|
|
276
|
+
payload.email &&
|
|
277
|
+
payload.tenantId &&
|
|
278
|
+
payload.type &&
|
|
279
|
+
payload.tenantLocale &&
|
|
280
|
+
payload.passwordPolicy &&
|
|
281
|
+
payload.isDenyPasswordChange !== undefined &&
|
|
282
|
+
payload.isPasswordSendEmail !== undefined) {
|
|
283
|
+
request.user = { id: payload.id, name: payload.name, email: payload.email, type: payload.type };
|
|
284
|
+
span.setAttributes({
|
|
285
|
+
"auth.optional": true,
|
|
286
|
+
"auth.token_present": true,
|
|
287
|
+
"user.id": payload.id,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch (jwtError) {
|
|
292
|
+
logger.debug({ error: jwtError }, "Optional auth: Invalid token provided");
|
|
293
|
+
span.setAttributes({
|
|
294
|
+
"auth.optional": true,
|
|
295
|
+
"auth.token_present": true,
|
|
296
|
+
"auth.token_valid": false,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
span.recordException(error);
|
|
302
|
+
logger.error({ error: error.message || error }, "Error in optional authentication middleware");
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
span.end();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
this.jwtSecret = options?.jwtSecret ?? process.env.IAM_JWT_SECRET ?? "your_secret";
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
exports.AuthMiddleware = AuthMiddleware;
|
|
312
|
+
//# sourceMappingURL=AuthMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthMiddleware.js","sourceRoot":"","sources":["../../src/auth/AuthMiddleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import { BaseMiddleware } from "../base/BaseMiddleware";
|
|
3
|
+
import { ImplementModeMiddlewareOptions } from "../types/AuthTypes";
|
|
4
|
+
export declare class ImplementModeMiddleware extends BaseMiddleware {
|
|
5
|
+
/**
|
|
6
|
+
* It allows providing the Redis connection through these options
|
|
7
|
+
* 1. a URL string
|
|
8
|
+
* 2. a pre-configured Redis client instance.
|
|
9
|
+
* 3. IAM_REDIS_URL env **(recommended)**
|
|
10
|
+
*
|
|
11
|
+
* @param {AuthMiddlewareOptions} [options.serviceName] - An optional unique name for the service using this middleware.
|
|
12
|
+
* @param {AuthMiddlewareOptions} [options.redisUrl] - A optional connection URL for the Redis server (e.g., 'redis://localhost:6379').
|
|
13
|
+
* @param {AuthMiddlewareOptions} [options.redisClient] - A optional pre-configured Redis client instance.
|
|
14
|
+
*/
|
|
15
|
+
constructor(options?: ImplementModeMiddlewareOptions);
|
|
16
|
+
private generateTenantImplementModeKey;
|
|
17
|
+
/**
|
|
18
|
+
* A middleware to check if server is in implement mode.
|
|
19
|
+
* Only admin can access resources in implement mode.
|
|
20
|
+
* @example
|
|
21
|
+
* const implModeMiddleware = new ImplementModeMiddleware()
|
|
22
|
+
* const prehandler = [implModeMiddleware.authorise.bind(implModeMiddleware)]
|
|
23
|
+
*/
|
|
24
|
+
authorise(request: FastifyRequest, reply: FastifyReply): Promise<FastifyReply | undefined>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ImplementModeMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImplementModeMiddleware.d.ts","sourceRoot":"","sources":["../../src/auth/ImplementModeMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAKvD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAKpE,qBAAa,uBAAwB,SAAQ,cAAc;IACzD;;;;;;;;;OASG;gBACS,OAAO,CAAC,EAAE,8BAA8B;IAIpD,OAAO,CAAC,8BAA8B;IAItC;;;;;;OAMG;IACG,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;CAuDjG"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ImplementModeMiddleware = void 0;
|
|
7
|
+
const api_1 = require("@opentelemetry/api");
|
|
8
|
+
const pino_1 = __importDefault(require("pino"));
|
|
9
|
+
const config_1 = require("../shared/config");
|
|
10
|
+
const errors_1 = require("../errors");
|
|
11
|
+
const BaseMiddleware_1 = require("../base/BaseMiddleware");
|
|
12
|
+
const RedisClient_1 = require("../infrastructure/RedisClient");
|
|
13
|
+
const logger = (0, pino_1.default)({ name: "ImplementModeMiddleware" });
|
|
14
|
+
class ImplementModeMiddleware extends BaseMiddleware_1.BaseMiddleware {
|
|
15
|
+
/**
|
|
16
|
+
* It allows providing the Redis connection through these options
|
|
17
|
+
* 1. a URL string
|
|
18
|
+
* 2. a pre-configured Redis client instance.
|
|
19
|
+
* 3. IAM_REDIS_URL env **(recommended)**
|
|
20
|
+
*
|
|
21
|
+
* @param {AuthMiddlewareOptions} [options.serviceName] - An optional unique name for the service using this middleware.
|
|
22
|
+
* @param {AuthMiddlewareOptions} [options.redisUrl] - A optional connection URL for the Redis server (e.g., 'redis://localhost:6379').
|
|
23
|
+
* @param {AuthMiddlewareOptions} [options.redisClient] - A optional pre-configured Redis client instance.
|
|
24
|
+
*/
|
|
25
|
+
constructor(options) {
|
|
26
|
+
super({ ...options, name: "ImplementModeMiddleware" });
|
|
27
|
+
}
|
|
28
|
+
generateTenantImplementModeKey(tenantId) {
|
|
29
|
+
return RedisClient_1.RedisClient.generateKey("tenant", "implement-mode", `${tenantId}`);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A middleware to check if server is in implement mode.
|
|
33
|
+
* Only admin can access resources in implement mode.
|
|
34
|
+
* @example
|
|
35
|
+
* const implModeMiddleware = new ImplementModeMiddleware()
|
|
36
|
+
* const prehandler = [implModeMiddleware.authorise.bind(implModeMiddleware)]
|
|
37
|
+
*/
|
|
38
|
+
async authorise(request, reply) {
|
|
39
|
+
const tracer = api_1.trace.getTracer(config_1.SERVICE_NAME);
|
|
40
|
+
const span = tracer.startSpan("ImplementModeMiddleware.authorise");
|
|
41
|
+
try {
|
|
42
|
+
if (!request.payload?.tenantId) {
|
|
43
|
+
throw new errors_1.CustomError("ImplementModeMiddleware: Missing tenant info from AuthMiddleware.authenticate", 401, "ImplementModeMiddlewareMissingTenantIdError", "middleware_error", "User not authenticated");
|
|
44
|
+
}
|
|
45
|
+
if (!request.user) {
|
|
46
|
+
throw new errors_1.CustomError("ImplementModeMiddleware: Missing user info from AuthMiddleware.authenticate", 401, "ImplementModeMiddlewareMissingUserError", "middleware_error", "User not authenticated");
|
|
47
|
+
}
|
|
48
|
+
// check if user is admin
|
|
49
|
+
if (request.user.type === "administrator") {
|
|
50
|
+
return; // do nothing and progress to next handler
|
|
51
|
+
}
|
|
52
|
+
// check cache
|
|
53
|
+
let implementMode = false;
|
|
54
|
+
// this key is set by authentication-service or authentication-system-settings-consumer-services
|
|
55
|
+
const key = this.generateTenantImplementModeKey(request.payload.tenantId);
|
|
56
|
+
let raw = await this.redisClient.get(key);
|
|
57
|
+
if (raw === null) {
|
|
58
|
+
logger.info(`cache miss on ${key}`);
|
|
59
|
+
raw = "false"; // force to false by default
|
|
60
|
+
}
|
|
61
|
+
const cached = JSON.parse(raw);
|
|
62
|
+
if (!(typeof cached === "boolean")) {
|
|
63
|
+
throw new Error(`Invalid value type for key: ${key}`);
|
|
64
|
+
}
|
|
65
|
+
implementMode = cached;
|
|
66
|
+
// if implementMode is on
|
|
67
|
+
if (implementMode) {
|
|
68
|
+
throw new errors_1.CustomError("ImplementModeMiddleware: Permission Denied, Application is on implement mode", 403, "ImplementModeMiddlewarePermissionDeniedError", "middleware_error", "Application is on implement mode");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return (0, errors_1.ErrorResponse)(error, reply, span, logger);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.ImplementModeMiddleware = ImplementModeMiddleware;
|
|
77
|
+
//# sourceMappingURL=ImplementModeMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImplementModeMiddleware.js","sourceRoot":"","sources":["../../src/auth/ImplementModeMiddleware.ts"],"names":[],"mappings":";;;;;;AACA,4CAA2C;AAC3C,gDAAwB;AACxB,6CAAgD;AAChD,sCAAuD;AACvD,2DAAwD;AAExD,+DAA4D;AAE5D,MAAM,MAAM,GAAG,IAAA,cAAI,EAAC,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC,CAAC;AAEzD,MAAa,uBAAwB,SAAQ,+BAAc;IACzD;;;;;;;;;OASG;IACH,YAAY,OAAwC;QAClD,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC,CAAC;IACzD,CAAC;IAEO,8BAA8B,CAAC,QAAgB;QACrD,OAAO,yBAAW,CAAC,WAAW,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,CAAC,OAAuB,EAAE,KAAmB;QAC1D,MAAM,MAAM,GAAG,WAAK,CAAC,SAAS,CAAC,qBAAY,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,mCAAmC,CAAC,CAAC;QACnE,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;gBAC/B,MAAM,IAAI,oBAAW,CACnB,+EAA+E,EAC/E,GAAG,EACH,6CAA6C,EAC7C,kBAAkB,EAClB,wBAAwB,CACzB,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBAClB,MAAM,IAAI,oBAAW,CACnB,6EAA6E,EAC7E,GAAG,EACH,yCAAyC,EACzC,kBAAkB,EAClB,wBAAwB,CACzB,CAAC;YACJ,CAAC;YAED,yBAAyB;YACzB,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBAC1C,OAAO,CAAC,0CAA0C;YACpD,CAAC;YACD,cAAc;YACd,IAAI,aAAa,GAAG,KAAK,CAAC;YAC1B,gGAAgG;YAChG,MAAM,GAAG,GAAG,IAAI,CAAC,8BAA8B,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC1E,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;gBACpC,GAAG,GAAG,OAAO,CAAC,CAAC,4BAA4B;YAC7C,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,OAAO,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;YACxD,CAAC;YACD,aAAa,GAAG,MAAM,CAAC;YACvB,yBAAyB;YACzB,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,IAAI,oBAAW,CACnB,8EAA8E,EAC9E,GAAG,EACH,8CAA8C,EAC9C,kBAAkB,EAClB,kCAAkC,CACnC,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAA,sBAAa,EAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;CACF;AAjFD,0DAiFC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { RedisClientType } from "redis";
|
|
3
|
+
import { BaseOptions } from "../types/AuthTypes";
|
|
4
|
+
import { Logger } from "pino";
|
|
5
|
+
export declare class BaseMiddleware {
|
|
6
|
+
readonly logger: Logger;
|
|
7
|
+
protected redisClient: RedisClientType;
|
|
8
|
+
protected serviceName: string;
|
|
9
|
+
constructor(options: BaseOptions & {
|
|
10
|
+
name: string;
|
|
11
|
+
});
|
|
12
|
+
private setupRedisClient;
|
|
13
|
+
private useSingletonClient;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=BaseMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseMiddleware.d.ts","sourceRoot":"","sources":["../../src/base/BaseMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAgB,eAAe,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAa,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAIpC,qBAAa,cAAc;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,WAAW,EAAG,eAAe,CAAC;IACxC,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,WAAW,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;YAerC,gBAAgB;YAqBhB,kBAAkB;CAGjC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BaseMiddleware = void 0;
|
|
7
|
+
require("dotenv/config");
|
|
8
|
+
const redis_1 = require("redis");
|
|
9
|
+
const pino_1 = __importDefault(require("pino"));
|
|
10
|
+
const config_1 = require("../shared/config");
|
|
11
|
+
const RedisClient_1 = require("../infrastructure/RedisClient");
|
|
12
|
+
class BaseMiddleware {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.logger = (0, pino_1.default)({ name: options.name });
|
|
15
|
+
this.serviceName = options.serviceName ?? config_1.SERVICE_NAME;
|
|
16
|
+
if ("redisUrl" in options && options.redisUrl) {
|
|
17
|
+
this.setupRedisClient(options.redisUrl);
|
|
18
|
+
}
|
|
19
|
+
else if ("redisClient" in options && options.redisClient) {
|
|
20
|
+
if (!options.redisClient.isOpen) {
|
|
21
|
+
options.redisClient.connect();
|
|
22
|
+
}
|
|
23
|
+
this.redisClient = options.redisClient;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.useSingletonClient();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async setupRedisClient(redisUrl) {
|
|
30
|
+
this.redisClient = (0, redis_1.createClient)({
|
|
31
|
+
url: redisUrl,
|
|
32
|
+
socket: process.env.NODE_ENV !== "local" ? { tls: true, rejectUnauthorized: false } : undefined,
|
|
33
|
+
});
|
|
34
|
+
this.redisClient.on("error", (err) => {
|
|
35
|
+
this.logger.error(err, "Redis client error");
|
|
36
|
+
});
|
|
37
|
+
this.redisClient.on("connect", () => {
|
|
38
|
+
this.logger.info("Redis client connected");
|
|
39
|
+
});
|
|
40
|
+
this.redisClient.on("disconnect", () => {
|
|
41
|
+
this.logger.info("Redis client disconnected");
|
|
42
|
+
});
|
|
43
|
+
await this.redisClient.connect();
|
|
44
|
+
}
|
|
45
|
+
async useSingletonClient() {
|
|
46
|
+
this.redisClient = await RedisClient_1.RedisClient.getInstance();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.BaseMiddleware = BaseMiddleware;
|
|
50
|
+
//# sourceMappingURL=BaseMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseMiddleware.js","sourceRoot":"","sources":["../../src/base/BaseMiddleware.ts"],"names":[],"mappings":";;;;;;AAAA,yBAAuB;AACvB,iCAAsD;AAEtD,gDAAoC;AACpC,6CAAgD;AAChD,+DAA4D;AAE5D,MAAa,cAAc;IAKzB,YAAY,OAAuC;QACjD,IAAI,CAAC,MAAM,GAAG,IAAA,cAAI,EAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,qBAAY,CAAC;QACvD,IAAI,UAAU,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC9C,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;aAAM,IAAI,aAAa,IAAI,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YAC3D,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;gBAChC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YAChC,CAAC;YACD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAA8B,CAAC;QAC5D,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,QAAgB;QAC7C,IAAI,CAAC,WAAW,GAAG,IAAA,oBAAY,EAAC;YAC9B,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;SAChG,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;YACrC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;IACnC,CAAC;IAEO,KAAK,CAAC,kBAAkB;QAC9B,IAAI,CAAC,WAAW,GAAG,MAAM,yBAAW,CAAC,WAAW,EAAE,CAAC;IACrD,CAAC;CACF;AA5CD,wCA4CC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DomainEvent } from "../event/DomainEvent";
|
|
2
|
+
export declare abstract class AggregateRoot {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly createdAt: Date;
|
|
5
|
+
updatedAt: Date;
|
|
6
|
+
private _domainEvents;
|
|
7
|
+
private _version;
|
|
8
|
+
constructor(id: string, createdAt?: Date, updatedAt?: Date);
|
|
9
|
+
get version(): number;
|
|
10
|
+
get domainEvents(): ReadonlyArray<DomainEvent>;
|
|
11
|
+
protected addDomainEvent(event: DomainEvent): void;
|
|
12
|
+
clearDomainEvents(): void;
|
|
13
|
+
protected applyEvent(event: DomainEvent): void;
|
|
14
|
+
static replayEvents<T extends AggregateRoot>(aggregateClass: new (...args: any[]) => T, events: DomainEvent[]): T;
|
|
15
|
+
protected abstract when(event: DomainEvent): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=AggregateRoot.d.ts.map
|