@platform-modules/civil-aviation-authority 2.3.300 → 2.3.302

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.
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Server-side JWT session binding (CAA VAPT M003/M004).
3
+ * JWT remains valid cryptographically until exp; access is allowed only while
4
+ * the bound user_sessions row is active and not expired.
5
+ */
6
+ export type UserSessionReader = {
7
+ isSessionActiveById(sessionId: number, userId: number): Promise<boolean>;
8
+ /** Legacy fallback when REQUIRE_JWT_SESSION_ID=false and token has no sessionId. */
9
+ isSessionActive?(userId: number): Promise<unknown>;
10
+ };
11
+ /** When true (default), JWT must include sessionId and that session must be active. */
12
+ export declare function isRequireJwtSessionId(): boolean;
13
+ export declare function resolveJwtSessionId(sessionId: unknown): number | null;
14
+ /**
15
+ * Validate that a verified JWT is still authorized (not logged out / expired server-side).
16
+ */
17
+ export declare function validateUserSessionBinding(userId: number, sessionId: number | null | undefined, reader: UserSessionReader): Promise<boolean>;
18
+ /** Shared TypeORM filters: active, not deleted, not past expires_at. */
19
+ export declare function activeUserSessionSql(alias?: string): {
20
+ clause: string;
21
+ params: Record<string, unknown>;
22
+ };
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * Server-side JWT session binding (CAA VAPT M003/M004).
4
+ * JWT remains valid cryptographically until exp; access is allowed only while
5
+ * the bound user_sessions row is active and not expired.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.isRequireJwtSessionId = isRequireJwtSessionId;
9
+ exports.resolveJwtSessionId = resolveJwtSessionId;
10
+ exports.validateUserSessionBinding = validateUserSessionBinding;
11
+ exports.activeUserSessionSql = activeUserSessionSql;
12
+ /** When true (default), JWT must include sessionId and that session must be active. */
13
+ function isRequireJwtSessionId() {
14
+ const raw = (process.env.REQUIRE_JWT_SESSION_ID ?? 'true').trim().toLowerCase();
15
+ return raw !== 'false' && raw !== '0' && raw !== 'no';
16
+ }
17
+ function resolveJwtSessionId(sessionId) {
18
+ if (sessionId == null || sessionId === '')
19
+ return null;
20
+ const sid = Number(sessionId);
21
+ if (!Number.isFinite(sid) || sid <= 0)
22
+ return null;
23
+ return sid;
24
+ }
25
+ /**
26
+ * Validate that a verified JWT is still authorized (not logged out / expired server-side).
27
+ */
28
+ async function validateUserSessionBinding(userId, sessionId, reader) {
29
+ if (!Number.isFinite(userId) || userId <= 0)
30
+ return false;
31
+ const sid = resolveJwtSessionId(sessionId);
32
+ if (sid != null) {
33
+ return reader.isSessionActiveById(sid, userId);
34
+ }
35
+ if (isRequireJwtSessionId()) {
36
+ return false;
37
+ }
38
+ if (!reader.isSessionActive)
39
+ return false;
40
+ const legacy = await reader.isSessionActive(userId);
41
+ return !!legacy;
42
+ }
43
+ /** Shared TypeORM filters: active, not deleted, not past expires_at. */
44
+ function activeUserSessionSql(alias = 'user_sessions') {
45
+ return {
46
+ clause: `${alias}.is_active = :is_active AND ${alias}.is_deleted = :is_deleted AND (${alias}.expires_at IS NULL OR ${alias}.expires_at > :session_now)`,
47
+ params: {
48
+ is_active: true,
49
+ is_deleted: false,
50
+ session_now: new Date(),
51
+ },
52
+ };
53
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './models/user';
2
2
  export * from './models/role';
3
3
  export * from './models/user-sessions';
4
+ export * from './auth/user-session-validation';
4
5
  export * from './models/ITHelpDeskModel';
5
6
  export * from './models/ServiceTypeModel';
6
7
  export * from './models/CAAServices';
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ exports.RespondToEnquiriesRequestAttachment = exports.RespondToEnquiriesWorkFlow
20
20
  __exportStar(require("./models/user"), exports);
21
21
  __exportStar(require("./models/role"), exports);
22
22
  __exportStar(require("./models/user-sessions"), exports);
23
+ __exportStar(require("./auth/user-session-validation"), exports);
23
24
  __exportStar(require("./models/ITHelpDeskModel"), exports);
24
25
  // export * from './models/ITServicesTypesSalalahModel'; // REMOVED - table no longer used
25
26
  // export * from './models/ITServicesTypesMuscatModel'; // REMOVED - table no longer used
@@ -23,6 +23,7 @@ export declare class ResidentialUnitRentalRequest extends BaseModel {
23
23
  family_size: number;
24
24
  phone_number: string;
25
25
  location_of_unit: string;
26
+ number_of_apartments: string | null;
26
27
  comment: string;
27
28
  status: string;
28
29
  user_id: number;
@@ -69,6 +69,10 @@ __decorate([
69
69
  (0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
70
70
  __metadata("design:type", String)
71
71
  ], ResidentialUnitRentalRequest.prototype, "location_of_unit", void 0);
72
+ __decorate([
73
+ (0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
74
+ __metadata("design:type", Object)
75
+ ], ResidentialUnitRentalRequest.prototype, "number_of_apartments", void 0);
72
76
  __decorate([
73
77
  (0, typeorm_1.Column)({ type: 'text', nullable: true }),
74
78
  __metadata("design:type", String)
@@ -15,7 +15,9 @@ export declare class StudyLeaveWorkFlow extends BaseModel {
15
15
  approver_role_id: number | null;
16
16
  approver_user_id: number | null;
17
17
  approved_by: number | null;
18
+ department_id: number | null;
19
+ section_id: number | null;
18
20
  status: StudyLeaveWorkFlowStatus;
19
21
  status_ar: string | null;
20
- constructor(request_id: number, content: string, status: StudyLeaveWorkFlowStatus, service_id?: number | null, sub_service_id?: number | null, order?: number, approver_role_id?: number | null, approver_user_id?: number | null, approved_by?: number | null);
22
+ constructor(request_id: number, content: string, status: StudyLeaveWorkFlowStatus, service_id?: number | null, sub_service_id?: number | null, order?: number, approver_role_id?: number | null, approver_user_id?: number | null, approved_by?: number | null, department_id?: number | null, section_id?: number | null);
21
23
  }
@@ -20,7 +20,7 @@ var StudyLeaveWorkFlowStatus;
20
20
  StudyLeaveWorkFlowStatus["REJECTED"] = "Rejected";
21
21
  })(StudyLeaveWorkFlowStatus || (exports.StudyLeaveWorkFlowStatus = StudyLeaveWorkFlowStatus = {}));
22
22
  let StudyLeaveWorkFlow = class StudyLeaveWorkFlow extends BaseModel_1.BaseModel {
23
- constructor(request_id, content, status, service_id, sub_service_id, order, approver_role_id, approver_user_id, approved_by) {
23
+ constructor(request_id, content, status, service_id, sub_service_id, order, approver_role_id, approver_user_id, approved_by, department_id, section_id) {
24
24
  super();
25
25
  this.request_id = request_id;
26
26
  this.service_id = service_id || null;
@@ -31,6 +31,8 @@ let StudyLeaveWorkFlow = class StudyLeaveWorkFlow extends BaseModel_1.BaseModel
31
31
  this.approver_role_id = approver_role_id || null;
32
32
  this.approver_user_id = approver_user_id || null;
33
33
  this.approved_by = approved_by || null;
34
+ this.department_id = department_id || null;
35
+ this.section_id = section_id || null;
34
36
  }
35
37
  };
36
38
  exports.StudyLeaveWorkFlow = StudyLeaveWorkFlow;
@@ -70,6 +72,14 @@ __decorate([
70
72
  (0, typeorm_1.Column)({ type: "integer", nullable: true }),
71
73
  __metadata("design:type", Object)
72
74
  ], StudyLeaveWorkFlow.prototype, "approved_by", void 0);
75
+ __decorate([
76
+ (0, typeorm_1.Column)({ type: "integer", nullable: true }),
77
+ __metadata("design:type", Object)
78
+ ], StudyLeaveWorkFlow.prototype, "department_id", void 0);
79
+ __decorate([
80
+ (0, typeorm_1.Column)({ type: "integer", nullable: true }),
81
+ __metadata("design:type", Object)
82
+ ], StudyLeaveWorkFlow.prototype, "section_id", void 0);
73
83
  __decorate([
74
84
  (0, typeorm_1.Column)({
75
85
  type: "enum",
@@ -85,5 +95,5 @@ __decorate([
85
95
  ], StudyLeaveWorkFlow.prototype, "status_ar", void 0);
86
96
  exports.StudyLeaveWorkFlow = StudyLeaveWorkFlow = __decorate([
87
97
  (0, typeorm_1.Entity)({ name: "study_leave_workflows" }),
88
- __metadata("design:paramtypes", [Number, String, String, Object, Object, Number, Object, Object, Object])
98
+ __metadata("design:paramtypes", [Number, String, String, Object, Object, Number, Object, Object, Object, Object, Object])
89
99
  ], StudyLeaveWorkFlow);
@@ -15,7 +15,9 @@ export declare class TrainingWorkFlow extends BaseModel {
15
15
  approver_role_id: number | null;
16
16
  approver_user_id: number | null;
17
17
  approved_by: number | null;
18
+ department_id: number | null;
19
+ section_id: number | null;
18
20
  status: TrainingWorkFlowStatus;
19
21
  status_ar: string | null;
20
- constructor(request_id: number, content: string, status: TrainingWorkFlowStatus, service_id?: number | null, sub_service_id?: number | null, order?: number, approver_role_id?: number | null, approver_user_id?: number | null, approved_by?: number | null);
22
+ constructor(request_id: number, content: string, status: TrainingWorkFlowStatus, service_id?: number | null, sub_service_id?: number | null, order?: number, approver_role_id?: number | null, approver_user_id?: number | null, approved_by?: number | null, department_id?: number | null, section_id?: number | null);
21
23
  }
@@ -20,7 +20,7 @@ var TrainingWorkFlowStatus;
20
20
  TrainingWorkFlowStatus["REJECTED"] = "Rejected";
21
21
  })(TrainingWorkFlowStatus || (exports.TrainingWorkFlowStatus = TrainingWorkFlowStatus = {}));
22
22
  let TrainingWorkFlow = class TrainingWorkFlow extends BaseModel_1.BaseModel {
23
- constructor(request_id, content, status, service_id, sub_service_id, order, approver_role_id, approver_user_id, approved_by) {
23
+ constructor(request_id, content, status, service_id, sub_service_id, order, approver_role_id, approver_user_id, approved_by, department_id, section_id) {
24
24
  super();
25
25
  this.request_id = request_id;
26
26
  this.service_id = service_id || null;
@@ -31,6 +31,8 @@ let TrainingWorkFlow = class TrainingWorkFlow extends BaseModel_1.BaseModel {
31
31
  this.approver_role_id = approver_role_id || null;
32
32
  this.approver_user_id = approver_user_id || null;
33
33
  this.approved_by = approved_by || null;
34
+ this.department_id = department_id || null;
35
+ this.section_id = section_id || null;
34
36
  }
35
37
  };
36
38
  exports.TrainingWorkFlow = TrainingWorkFlow;
@@ -70,6 +72,14 @@ __decorate([
70
72
  (0, typeorm_1.Column)({ type: "integer", nullable: true }),
71
73
  __metadata("design:type", Object)
72
74
  ], TrainingWorkFlow.prototype, "approved_by", void 0);
75
+ __decorate([
76
+ (0, typeorm_1.Column)({ type: "integer", nullable: true }),
77
+ __metadata("design:type", Object)
78
+ ], TrainingWorkFlow.prototype, "department_id", void 0);
79
+ __decorate([
80
+ (0, typeorm_1.Column)({ type: "integer", nullable: true }),
81
+ __metadata("design:type", Object)
82
+ ], TrainingWorkFlow.prototype, "section_id", void 0);
73
83
  __decorate([
74
84
  (0, typeorm_1.Column)({
75
85
  type: "enum",
@@ -85,5 +95,5 @@ __decorate([
85
95
  ], TrainingWorkFlow.prototype, "status_ar", void 0);
86
96
  exports.TrainingWorkFlow = TrainingWorkFlow = __decorate([
87
97
  (0, typeorm_1.Entity)({ name: "training_workflows" }),
88
- __metadata("design:paramtypes", [Number, String, String, Object, Object, Number, Object, Object, Object])
98
+ __metadata("design:paramtypes", [Number, String, String, Object, Object, Number, Object, Object, Object, Object, Object])
89
99
  ], TrainingWorkFlow);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-modules/civil-aviation-authority",
3
- "version": "2.3.300",
3
+ "version": "2.3.302",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -9,6 +9,9 @@ ALTER TYPE asset_maintenance_category_en ADD VALUE IF NOT EXISTS 'Other';
9
9
  ALTER TABLE residential_unit_rental_requests
10
10
  ALTER COLUMN preferred_start_date DROP NOT NULL;
11
11
 
12
+ ALTER TABLE residential_unit_rental_requests
13
+ ADD COLUMN IF NOT EXISTS number_of_apartments VARCHAR(255);
14
+
12
15
  -- 3. Appeal against administrative decision: optional / removed payload fields
13
16
  ALTER TABLE appeal_against_administrative_decision_requests
14
17
  ALTER COLUMN description DROP NOT NULL,
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Server-side JWT session binding (CAA VAPT M003/M004).
3
+ * JWT remains valid cryptographically until exp; access is allowed only while
4
+ * the bound user_sessions row is active and not expired.
5
+ */
6
+
7
+ export type UserSessionReader = {
8
+ isSessionActiveById(sessionId: number, userId: number): Promise<boolean>;
9
+ /** Legacy fallback when REQUIRE_JWT_SESSION_ID=false and token has no sessionId. */
10
+ isSessionActive?(userId: number): Promise<unknown>;
11
+ };
12
+
13
+ /** When true (default), JWT must include sessionId and that session must be active. */
14
+ export function isRequireJwtSessionId(): boolean {
15
+ const raw = (process.env.REQUIRE_JWT_SESSION_ID ?? 'true').trim().toLowerCase();
16
+ return raw !== 'false' && raw !== '0' && raw !== 'no';
17
+ }
18
+
19
+ export function resolveJwtSessionId(sessionId: unknown): number | null {
20
+ if (sessionId == null || sessionId === '') return null;
21
+ const sid = Number(sessionId);
22
+ if (!Number.isFinite(sid) || sid <= 0) return null;
23
+ return sid;
24
+ }
25
+
26
+ /**
27
+ * Validate that a verified JWT is still authorized (not logged out / expired server-side).
28
+ */
29
+ export async function validateUserSessionBinding(
30
+ userId: number,
31
+ sessionId: number | null | undefined,
32
+ reader: UserSessionReader,
33
+ ): Promise<boolean> {
34
+ if (!Number.isFinite(userId) || userId <= 0) return false;
35
+
36
+ const sid = resolveJwtSessionId(sessionId);
37
+ if (sid != null) {
38
+ return reader.isSessionActiveById(sid, userId);
39
+ }
40
+
41
+ if (isRequireJwtSessionId()) {
42
+ return false;
43
+ }
44
+
45
+ if (!reader.isSessionActive) return false;
46
+ const legacy = await reader.isSessionActive(userId);
47
+ return !!legacy;
48
+ }
49
+
50
+ /** Shared TypeORM filters: active, not deleted, not past expires_at. */
51
+ export function activeUserSessionSql(alias = 'user_sessions'): {
52
+ clause: string;
53
+ params: Record<string, unknown>;
54
+ } {
55
+ return {
56
+ clause: `${alias}.is_active = :is_active AND ${alias}.is_deleted = :is_deleted AND (${alias}.expires_at IS NULL OR ${alias}.expires_at > :session_now)`,
57
+ params: {
58
+ is_active: true,
59
+ is_deleted: false,
60
+ session_now: new Date(),
61
+ },
62
+ };
63
+ }
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import { JobTransferRequestStatus } from './models/JobTransferRequestModel';
3
3
  export * from './models/user';
4
4
  export * from './models/role';
5
5
  export * from './models/user-sessions';
6
+ export * from './auth/user-session-validation';
6
7
  export * from './models/ITHelpDeskModel';
7
8
  // export * from './models/ITServicesTypesSalalahModel'; // REMOVED - table no longer used
8
9
  // export * from './models/ITServicesTypesMuscatModel'; // REMOVED - table no longer used
@@ -113,8 +113,8 @@ export class ResidentialUnitRentalRequest extends BaseModel {
113
113
 
114
114
  location_of_unit: string;
115
115
 
116
-
117
-
116
+ @Column({ type: 'varchar', length: 255, nullable: true })
117
+ number_of_apartments: string | null;
118
118
 
119
119
  @Column({ type: 'text', nullable: true })
120
120
 
@@ -37,6 +37,12 @@ export class StudyLeaveWorkFlow extends BaseModel {
37
37
  @Column({ type: "integer", nullable: true })
38
38
  approved_by: number | null;
39
39
 
40
+ @Column({ type: "integer", nullable: true })
41
+ department_id: number | null;
42
+
43
+ @Column({ type: "integer", nullable: true })
44
+ section_id: number | null;
45
+
40
46
  @Column({
41
47
  type: "enum",
42
48
  enum: StudyLeaveWorkFlowStatus,
@@ -57,7 +63,9 @@ export class StudyLeaveWorkFlow extends BaseModel {
57
63
  order?: number,
58
64
  approver_role_id?: number | null,
59
65
  approver_user_id?: number | null,
60
- approved_by?: number | null
66
+ approved_by?: number | null,
67
+ department_id?: number | null,
68
+ section_id?: number | null
61
69
  ) {
62
70
  super();
63
71
  this.request_id = request_id;
@@ -69,6 +77,8 @@ export class StudyLeaveWorkFlow extends BaseModel {
69
77
  this.approver_role_id = approver_role_id || null;
70
78
  this.approver_user_id = approver_user_id || null;
71
79
  this.approved_by = approved_by || null;
80
+ this.department_id = department_id || null;
81
+ this.section_id = section_id || null;
72
82
  }
73
83
  }
74
84
 
@@ -37,6 +37,12 @@ export class TrainingWorkFlow extends BaseModel {
37
37
  @Column({ type: "integer", nullable: true })
38
38
  approved_by: number | null;
39
39
 
40
+ @Column({ type: "integer", nullable: true })
41
+ department_id: number | null;
42
+
43
+ @Column({ type: "integer", nullable: true })
44
+ section_id: number | null;
45
+
40
46
  @Column({
41
47
  type: "enum",
42
48
  enum: TrainingWorkFlowStatus,
@@ -57,7 +63,9 @@ export class TrainingWorkFlow extends BaseModel {
57
63
  order?: number,
58
64
  approver_role_id?: number | null,
59
65
  approver_user_id?: number | null,
60
- approved_by?: number | null
66
+ approved_by?: number | null,
67
+ department_id?: number | null,
68
+ section_id?: number | null
61
69
  ) {
62
70
  super();
63
71
  this.request_id = request_id;
@@ -69,6 +77,8 @@ export class TrainingWorkFlow extends BaseModel {
69
77
  this.approver_role_id = approver_role_id || null;
70
78
  this.approver_user_id = approver_user_id || null;
71
79
  this.approved_by = approved_by || null;
80
+ this.department_id = department_id || null;
81
+ this.section_id = section_id || null;
72
82
  }
73
83
  }
74
84