Skip to content

Commit 57e1ef9

Browse files
committed
1.3.1: Refactor schema extension and permission validation:
- Replace legacy `EntitySchemaExtender` with `SPathUtil.extendEntitySchema`. - Update `permissionValidation` to accept a `ValidationRequest` payload. - Adjust all imports, interfaces, and test usage accordingly. - Add `ValidationRequest` type documentation. - Update Swagger‑Mate dependency to 1.3.2 and bump package version to 1.3.1.
1 parent 05ef25d commit 57e1ef9

9 files changed

Lines changed: 110 additions & 101 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@grandlinex/kernel",
3-
"version": "1.3.0",
3+
"version": "1.3.1",
44
"description": "GrandLineX is an out-of-the-box server framework on top of ExpressJs.",
55
"type": "module",
66
"exports": {
@@ -47,7 +47,7 @@
4747
"license": "BSD-3-Clause",
4848
"dependencies": {
4949
"@grandlinex/core": "1.3.1",
50-
"@grandlinex/swagger-mate": "1.3.1",
50+
"@grandlinex/swagger-mate": "1.3.2",
5151
"@types/jsonwebtoken": "9.0.10",
5252
"@types/ms": "2.1.0",
5353
"body-parser": "2.2.1",

src/classes/BaseAction.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import { CoreAction, IDataBase } from '@grandlinex/core';
1+
import {
2+
CoreAction,
3+
CoreEntity,
4+
IDataBase,
5+
instanceOfEntity,
6+
} from '@grandlinex/core';
27
import {
38
ActionMode,
49
ErrorType,
510
isErrorType,
611
isSwaggerRef,
12+
SPathUtil,
713
SSchemaEl,
814
} from '@grandlinex/swagger-mate';
915
import {
@@ -33,7 +39,7 @@ export default abstract class BaseAction<
3339

3440
forceDebug: boolean;
3541

36-
requestSchema: SSchemaEl | null;
42+
requestSchema: SSchemaEl | CoreEntity | string | null;
3743

3844
constructor(chanel: string, module: IBaseKernelModule<K, T, P, C, E>) {
3945
super(chanel, module);
@@ -47,15 +53,21 @@ export default abstract class BaseAction<
4753

4854
static validateSchema(
4955
error: ErrorType,
50-
schema: SSchemaEl,
56+
inputSchema: SSchemaEl | CoreEntity | string,
5157
key: string,
5258
field: any,
5359
required: boolean = true,
5460
) {
55-
if (isSwaggerRef(schema)) {
61+
let schema: SSchemaEl | string;
62+
if (instanceOfEntity(inputSchema)) {
63+
schema = SPathUtil.schemaFromEntity(inputSchema)!;
64+
} else {
65+
schema = inputSchema;
66+
}
67+
if (isSwaggerRef(schema) || typeof schema === 'string') {
5668
error.field?.push({
5769
key,
58-
message: `Ref schema body validation is not supported yet`,
70+
message: `Ref or string schema body validation is not supported yet`,
5971
});
6072
return;
6173
}
@@ -185,6 +197,7 @@ export default abstract class BaseAction<
185197
body = this.bodyValidation(req);
186198
}
187199
if (isErrorType(body)) {
200+
this.debug(body);
188201
res.status(400).send(body);
189202
return;
190203
}
@@ -221,6 +234,7 @@ export default abstract class BaseAction<
221234
body = this.bodyValidation(req);
222235
}
223236
if (isErrorType(body)) {
237+
this.debug(body);
224238
res.status(400).send(body);
225239
return;
226240
}
@@ -253,6 +267,7 @@ export default abstract class BaseAction<
253267
body = this.bodyValidation(req);
254268
}
255269
if (isErrorType(body)) {
270+
this.debug(body);
256271
res.status(400).send(body);
257272
return;
258273
}

src/classes/BaseAuthProvider.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as jwt from 'jsonwebtoken';
22
import { XRequest } from '../lib/express.js';
3+
import { ValidationRequest } from '../lib/index.js';
34

45
export type JwtExtend = {
56
username: string;
@@ -20,7 +21,7 @@ export interface IAuthProvider<T extends JwtExtend> {
2021
requestType: string,
2122
): Promise<AuthResult>;
2223

23-
validateAccess(token: JwtToken<T>, requestType: string): Promise<boolean>;
24+
validateAccess(request: ValidationRequest<T>): Promise<boolean>;
2425

2526
bearerTokenValidation(req: XRequest): Promise<JwtToken<T> | number>;
2627

@@ -39,10 +40,7 @@ export default abstract class BaseAuthProvider<T extends JwtExtend = JwtExtend>
3940
requestType: string,
4041
): Promise<AuthResult>;
4142

42-
abstract validateAccess(
43-
token: JwtToken<T>,
44-
requestType: string,
45-
): Promise<boolean>;
43+
abstract validateAccess(request: ValidationRequest<T>): Promise<boolean>;
4644

4745
abstract bearerTokenValidation(req: XRequest): Promise<JwtToken<T> | number>;
4846

src/classes/BaseEndpoint.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ export default abstract class BaseEndpoint<
4646

4747
protected port: number;
4848

49-
constructor(
50-
chanel: string,
51-
module: IBaseKernelModule<any, any, any, any>,
52-
port: number,
53-
) {
49+
constructor(chanel: string, module: IBaseKernelModule<any>, port: number) {
5450
super(`endpoint-${chanel}`, module);
5551
this.port = port;
5652
this.appServer = express();
@@ -60,7 +56,7 @@ export default abstract class BaseEndpoint<
6056
}
6157

6258
private setAppHeader() {
63-
this.appServer.use((req, res, next) => {
59+
this.appServer.use((_, res, next) => {
6460
res.setHeader('X-Powered-By', 'GrandLineX');
6561
next();
6662
});

src/lib/EntitySchemaExtender.ts

Lines changed: 0 additions & 61 deletions
This file was deleted.

src/lib/index.ts

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,95 @@ import express from 'express';
1616
import * as jwt from 'jsonwebtoken';
1717
import { IAuthProvider, JwtExtend, JwtToken } from '../classes/index.js';
1818

19-
import EntitySchemaExtender from './EntitySchemaExtender.js';
20-
2119
import { XActionEvent, XRequest } from './express.js';
2220

23-
export { EntitySchemaExtender };
21+
/**
22+
* Represents a request for validating a JWT token with optional constraints.
23+
*
24+
* @template T - The shape of the JWT claims, extending {@link JwtExtend}.
25+
*
26+
* This type includes:
27+
* - **token**: the JWT token to validate.
28+
* - **requestType**: an array of request type identifiers that the token must satisfy.
29+
* - **required** (optional): indicates whether the token is mandatory for the request; if omitted, the token is considered required.
30+
*/
31+
export type ValidationRequest<T extends JwtExtend = JwtExtend> = {
32+
token: JwtToken<T>;
33+
requestType: string[];
34+
required?: boolean;
35+
};
2436

2537
export interface ICClient<T extends JwtExtend = JwtExtend>
2638
extends ICoreCClient {
39+
/**
40+
* Assigns the authentication provider responsible for handling authentication operations.
41+
*
42+
* @param provider The authentication provider to use.
43+
* @return True if the provider was set successfully; otherwise, false.
44+
*/
2745
setAuthProvider(provider: IAuthProvider<T>): boolean;
2846

47+
/**
48+
* Verifies a JWT access token and returns the decoded payload or an error code.
49+
*
50+
* @param token - The JWT access token string to verify.
51+
* @return A promise that resolves to the decoded token payload of type {@link JwtToken<T>} if verification succeeds, or a numeric error code if verification fails.
52+
*/
2953
jwtVerifyAccessToken(token: string): Promise<JwtToken<T> | number>;
3054

55+
/**
56+
* Decodes a JWT access token and returns its payload.
57+
*
58+
* @param {string} token - The JWT access token to decode.
59+
* @returns {jwt.JwtPayload | null} The decoded payload if the token is valid, otherwise null.
60+
*/
3161
jwtDecodeAccessToken(token: string): jwt.JwtPayload | null;
3262

63+
/**
64+
* Generates a signed JWT access token.
65+
*
66+
* @param {JwtToken<T>} data - The payload data for the token.
67+
* @param {Record<string, any>} [extend] - Optional additional fields to merge into the token payload.
68+
* @param {string|number} [expire] - Optional expiration time for the token, expressed in seconds or as an ISO 8601 duration string.
69+
* @return {Promise<string>} A promise that resolves to the generated JWT token string.
70+
*/
3371
jwtGenerateAccessToken(
3472
data: JwtToken<T>,
3573
extend?: Record<string, any>,
3674
expire?: string | number,
3775
): Promise<string>;
3876

77+
/**
78+
* Validates an API token for a specified user and request type.
79+
*
80+
* @param {string} username - The username associated with the token.
81+
* @param {string} token - The API token to validate.
82+
* @param {string} requestType - The type of request for which the token is being validated.
83+
* @returns {Promise<{ valid: boolean; userId: string | null }>} A promise that resolves to an object containing a boolean indicating whether the token is valid and the user ID if validation succeeds; otherwise, `null` is returned for the user ID.
84+
*/
3985
apiTokenValidation(
4086
username: string,
4187
token: string,
4288
requestType: string,
4389
): Promise<{ valid: boolean; userId: string | null }>;
4490

45-
permissionValidation(
46-
token: JwtToken<T>,
47-
requestType: string,
48-
): Promise<boolean>;
49-
91+
/**
92+
* Validates user permissions according to the supplied validation request.
93+
*
94+
* @param {ValidationRequest<T>} request - The request object containing the necessary
95+
* data for permission evaluation, such as user identity, requested action,
96+
* and contextual parameters.
97+
*
98+
* @return {Promise<boolean>} A promise that resolves to `true` if the permissions
99+
* are valid for the specified request, otherwise resolves to `false`.
100+
*/
101+
permissionValidation(request: ValidationRequest<T>): Promise<boolean>;
102+
103+
/**
104+
* Validates the bearer token present in the provided request.
105+
* @param {XRequest} req - The HTTP request containing the Authorization header.
106+
* @return {Promise<JwtToken<T> | number>} A promise that resolves to the decoded JWT payload if the token is valid, or a numeric status code (e.g., 401) if validation fails.
107+
*/
50108
bearerTokenValidation(req: XRequest): Promise<JwtToken<T> | number>;
51109
}
52110

src/modules/crypto/CryptoClient.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CoreCryptoClient } from '@grandlinex/core';
22
import * as jwt from 'jsonwebtoken';
33
import { type StringValue } from 'ms';
4-
import { ICClient, IKernel } from '../../lib/index.js';
4+
import { ICClient, IKernel, ValidationRequest } from '../../lib/index.js';
55
import { IAuthProvider, JwtExtend, JwtToken } from '../../classes/index.js';
66
import { XRequest } from '../../lib/express.js';
77

@@ -95,12 +95,9 @@ export default class CryptoClient<T extends JwtExtend = JwtExtend>
9595
};
9696
}
9797

98-
async permissionValidation(
99-
token: JwtToken<T>,
100-
requestType: string,
101-
): Promise<boolean> {
98+
async permissionValidation(request: ValidationRequest<T>): Promise<boolean> {
10299
if (this.authProvider) {
103-
return this.authProvider.validateAccess(token, requestType);
100+
return this.authProvider.validateAccess(request);
104101
}
105102
return false;
106103
}

src/tests/DebugClasses.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
BaseApiAction,
77
BaseAuthProvider,
88
ICClient,
9-
JwtToken,
9+
JwtToken, ValidationRequest,
1010
XActionEvent,
1111
XRequest
1212
} from '../index.js';
@@ -52,7 +52,7 @@ export class TestAuthProvider extends BaseAuthProvider {
5252
username: string,
5353
token: string,
5454
requestType: string
55-
): Promise< AuthResult> {
55+
): Promise<AuthResult> {
5656

5757
const valid=username === 'admin' && token === 'admin' && requestType === 'api';
5858
return {
@@ -61,8 +61,9 @@ export class TestAuthProvider extends BaseAuthProvider {
6161
};
6262
}
6363

64-
async validateAccess(token: JwtToken, requestType: string): Promise<boolean> {
65-
return token.username === 'admin' && requestType === 'api';
64+
async validateAccess(request:ValidationRequest<any>): Promise<boolean> {
65+
const { token, requestType } = request;
66+
return token.username === 'admin' && requestType.includes('api');
6667
}
6768

6869
async bearerTokenValidation(req: XRequest): Promise<JwtToken | number> {

0 commit comments

Comments
 (0)