@gov-cy/govcy-express-services 1.4.2 → 1.5.0
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
CHANGED
|
@@ -30,6 +30,8 @@ The APIs used for submission, temporary save and file uploads are not part of th
|
|
|
30
30
|
- [✅ Best Practices](#-best-practices)
|
|
31
31
|
- [📦 Full installation guide](#-full-installation-guide)
|
|
32
32
|
- [🛠️ Usage](#%EF%B8%8F-usage)
|
|
33
|
+
- [🔑 Authentication Middleware](#-authentication-middleware)
|
|
34
|
+
-[CY Login Access Policies](#cy-login-access-policies)
|
|
33
35
|
- [🧩 Dynamic services](#-dynamic-services)
|
|
34
36
|
- [Pages](#pages)
|
|
35
37
|
- [Form vs static pages](#form-vs-static-pages)
|
|
@@ -142,13 +144,67 @@ npm start
|
|
|
142
144
|
```
|
|
143
145
|
The server will start on `https://localhost:44319` (see [NOTES.md](NOTES.md#local-development) for more details on this).
|
|
144
146
|
|
|
145
|
-
### Authentication Middleware
|
|
147
|
+
### 🔑 Authentication Middleware
|
|
146
148
|
Authentication is handled via OpenID Connect using CY Login and is configured using environment variables. The middleware ensures users have valid sessions before accessing protected routes.
|
|
147
149
|
|
|
148
150
|
The CY Login tokens are used to also connect with the various APIs through [cyConnect](https://dev.azure.com/cyprus-gov-cds/Documentation/_wiki/wikis/Documentation/74/CY-Connect), so make sure to include the correct `scope` when requesting for a [cyLogin client registration](https://dev.azure.com/cyprus-gov-cds/Documentation/_wiki/wikis/Documentation/34/Developer-Guide).
|
|
149
151
|
|
|
150
152
|
The CY Login settings are configured in the `secrets/.env` file.
|
|
151
153
|
|
|
154
|
+
#### cyLogin Access Policies
|
|
155
|
+
|
|
156
|
+
Each service can specify which types of authenticated CY Login profiles are allowed to access it using the `site.cyLoginPolicies` property in its site configuration.
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
"cyLoginPolicies": ["naturalPerson", "legalPerson"]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
##### Supported Policies
|
|
163
|
+
|
|
164
|
+
| Policy name | Description | Typical use |
|
|
165
|
+
| --------------- | ------------------------------------------------------------ | ---------------------------------------------------- |
|
|
166
|
+
| `naturalPerson` | Allows individual users (Cypriot citizens or foreign residents) who have a verified profile in the Civil Registry. Identified by `profile_type: "Individual"` and a 10-digit identifier starting with `00` (citizen) or `05` (foreigner). | Citizen-facing services, personal applications, etc. |
|
|
167
|
+
| `legalPerson` | Allows legal entities (companies, partnerships, organisations) with verified profiles in the Registrar of Companies. Identified by `profile_type: "Organisation"` and a `legal_unique_identifier`. | Business-facing services, company submissions, etc. |
|
|
168
|
+
|
|
169
|
+
##### How it works
|
|
170
|
+
|
|
171
|
+
- Access is granted if **any** of the listed policies pass.
|
|
172
|
+
- If the user’s CY Login profile does not match any of the allowed policies, the request is blocked.
|
|
173
|
+
|
|
174
|
+
##### Defaults
|
|
175
|
+
|
|
176
|
+
If `cyLoginPolicies` is omitted, the framework defaults to:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
"cyLoginPolicies": ["naturalPerson"]
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
This maintains backward compatibility with existing services that only supported individual (civil registry) users.
|
|
183
|
+
|
|
184
|
+
##### Example
|
|
185
|
+
|
|
186
|
+
Allow both natural and legal persons:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
"site": {
|
|
190
|
+
"cyLoginPolicies": ["naturalPerson", "legalPerson"]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Restrict access to natural persons only:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
"site": {
|
|
198
|
+
"cyLoginPolicies": ["naturalPerson"]
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
##### Notes
|
|
203
|
+
|
|
204
|
+
- This configuration applies globally to the service.
|
|
205
|
+
- Both `requireAuth` and `cyLoginPolicy` middlewares must be present on protected routes (automatically included by the default route setup).
|
|
206
|
+
|
|
207
|
+
|
|
152
208
|
### 🧩 Dynamic Services
|
|
153
209
|
Services are rendered dynamically using JSON templates stored in the `/data` folder. All the service configuration, pages, routes, and logic is stored in the JSON files. The service will load `data/:siteId.json` to get the form data when a user visits `/:siteId/:pageUrl`. Checkout the [express-service-shema.json](express-service-shema.json) and the example JSON structure of the **[test.json](data/test.json)** file for more details.
|
|
154
210
|
|
|
@@ -158,6 +214,7 @@ Here is an example JSON config:
|
|
|
158
214
|
{
|
|
159
215
|
"site": {
|
|
160
216
|
"id": "test",
|
|
217
|
+
"cyLoginPolicies": ["naturalPerson"], //<-- Allowed CY Login policies
|
|
161
218
|
"lang": "el", //<-- Default language
|
|
162
219
|
"languages": [ //<-- Supported languages
|
|
163
220
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gov-cy/govcy-express-services",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "An Express-based system that dynamically renders services using @gov-cy/govcy-frontend-renderer and posts data to a submission API.",
|
|
5
5
|
"author": "DMRID - DSF Team",
|
|
6
6
|
"license": "MIT",
|
package/src/index.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { govcyCsrfMiddleware } from './middleware/govcyCsrf.mjs';
|
|
|
18
18
|
import { govcySessionData } from './middleware/govcySessionData.mjs';
|
|
19
19
|
import { govcyHttpErrorHandler } from './middleware/govcyHttpErrorHandler.mjs';
|
|
20
20
|
import { govcyLanguageMiddleware } from './middleware/govcyLanguageMiddleware.mjs';
|
|
21
|
-
import { requireAuth,
|
|
21
|
+
import { requireAuth, cyLoginPolicy, handleLoginRoute, handleSigninOidc, handleLogout } from './middleware/cyLoginAuth.mjs';
|
|
22
22
|
import { serviceConfigDataMiddleware } from './middleware/govcyConfigSiteData.mjs';
|
|
23
23
|
import { govcyManifestHandler } from './middleware/govcyManifestHandler.mjs';
|
|
24
24
|
import { govcyRoutePageHandler } from './middleware/govcyRoutePageHandler.mjs';
|
|
@@ -103,7 +103,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
103
103
|
// 🛠️ Debugging routes -----------------------------------------------------
|
|
104
104
|
// 🙍🏻♂️ -- ROUTE: Debugging route Protected Route
|
|
105
105
|
// if (!isProdOrStaging()) {
|
|
106
|
-
// app.get('/user', requireAuth,
|
|
106
|
+
// app.get('/user', requireAuth, cyLoginPolicy, (req, res) => {
|
|
107
107
|
// res.send(`
|
|
108
108
|
// User name: ${req.session.user.name}
|
|
109
109
|
// <br> Sub: ${req.session.user.sub}
|
|
@@ -139,7 +139,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
139
139
|
app.post('/apis/:siteId/:pageUrl/upload',
|
|
140
140
|
serviceConfigDataMiddleware,
|
|
141
141
|
requireAuth, // UNCOMMENT
|
|
142
|
-
|
|
142
|
+
cyLoginPolicy, // UNCOMMENT
|
|
143
143
|
govcyServiceEligibilityHandler(true), // UNCOMMENT
|
|
144
144
|
govcyFileUpload);
|
|
145
145
|
|
|
@@ -147,7 +147,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
147
147
|
app.post('/apis/:siteId/:pageUrl/multiple/add/upload',
|
|
148
148
|
serviceConfigDataMiddleware,
|
|
149
149
|
requireAuth, // UNCOMMENT
|
|
150
|
-
|
|
150
|
+
cyLoginPolicy, // UNCOMMENT
|
|
151
151
|
govcyServiceEligibilityHandler(true), // UNCOMMENT
|
|
152
152
|
govcyFileUpload
|
|
153
153
|
);
|
|
@@ -176,7 +176,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
176
176
|
injectSiteId,
|
|
177
177
|
serviceConfigDataMiddleware,
|
|
178
178
|
requireAuth,
|
|
179
|
-
|
|
179
|
+
cyLoginPolicy,
|
|
180
180
|
govcyServiceEligibilityHandler(),
|
|
181
181
|
...handlers,
|
|
182
182
|
];
|
|
@@ -196,7 +196,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
196
196
|
app.post('/apis/:siteId/:pageUrl/multiple/edit/:index/upload',
|
|
197
197
|
serviceConfigDataMiddleware,
|
|
198
198
|
requireAuth, // UNCOMMENT
|
|
199
|
-
|
|
199
|
+
cyLoginPolicy, // UNCOMMENT
|
|
200
200
|
govcyServiceEligibilityHandler(true), // UNCOMMENT
|
|
201
201
|
govcyFileUpload
|
|
202
202
|
);
|
|
@@ -205,7 +205,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
205
205
|
app.get('/:siteId/:pageUrl/multiple/add/view-file/:elementName',
|
|
206
206
|
serviceConfigDataMiddleware,
|
|
207
207
|
requireAuth,
|
|
208
|
-
|
|
208
|
+
cyLoginPolicy,
|
|
209
209
|
govcyServiceEligibilityHandler(true),
|
|
210
210
|
govcyFileViewHandler());
|
|
211
211
|
|
|
@@ -213,7 +213,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
213
213
|
app.get('/:siteId/:pageUrl/multiple/add/delete-file/:elementName',
|
|
214
214
|
serviceConfigDataMiddleware,
|
|
215
215
|
requireAuth,
|
|
216
|
-
|
|
216
|
+
cyLoginPolicy,
|
|
217
217
|
govcyServiceEligibilityHandler(),
|
|
218
218
|
govcyLoadSubmissionData(),
|
|
219
219
|
govcyFileDeletePageHandler(),
|
|
@@ -224,7 +224,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
224
224
|
app.get('/:siteId/:pageUrl/multiple/edit/:index/delete-file/:elementName',
|
|
225
225
|
serviceConfigDataMiddleware,
|
|
226
226
|
requireAuth,
|
|
227
|
-
|
|
227
|
+
cyLoginPolicy,
|
|
228
228
|
govcyServiceEligibilityHandler(),
|
|
229
229
|
govcyLoadSubmissionData(),
|
|
230
230
|
govcyFileDeletePageHandler(),
|
|
@@ -236,7 +236,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
236
236
|
app.post('/:siteId/:pageUrl/multiple/add/delete-file/:elementName',
|
|
237
237
|
serviceConfigDataMiddleware,
|
|
238
238
|
requireAuth,
|
|
239
|
-
|
|
239
|
+
cyLoginPolicy,
|
|
240
240
|
govcyServiceEligibilityHandler(true),
|
|
241
241
|
govcyFileDeletePostHandler()
|
|
242
242
|
);
|
|
@@ -245,7 +245,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
245
245
|
app.post('/:siteId/:pageUrl/multiple/edit/:index/delete-file/:elementName',
|
|
246
246
|
serviceConfigDataMiddleware,
|
|
247
247
|
requireAuth,
|
|
248
|
-
|
|
248
|
+
cyLoginPolicy,
|
|
249
249
|
govcyServiceEligibilityHandler(true),
|
|
250
250
|
govcyFileDeletePostHandler()
|
|
251
251
|
);
|
|
@@ -255,33 +255,33 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
255
255
|
app.get('/:siteId/:pageUrl/multiple/edit/:index/view-file/:elementName',
|
|
256
256
|
serviceConfigDataMiddleware,
|
|
257
257
|
requireAuth,
|
|
258
|
-
|
|
258
|
+
cyLoginPolicy,
|
|
259
259
|
govcyServiceEligibilityHandler(true),
|
|
260
260
|
govcyFileViewHandler());
|
|
261
261
|
|
|
262
262
|
// 🏠 -- ROUTE: Handle route with only siteId (/:siteId or /:siteId/)
|
|
263
|
-
app.get('/:siteId', serviceConfigDataMiddleware, requireAuth,
|
|
263
|
+
app.get('/:siteId', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(true), govcyLoadSubmissionData(), govcyPageHandler(), renderGovcyPage());
|
|
264
264
|
|
|
265
265
|
// 👀 -- ROUTE: Add Review Page Route (BEFORE the dynamic route)
|
|
266
|
-
app.get('/:siteId/review', serviceConfigDataMiddleware, requireAuth,
|
|
266
|
+
app.get('/:siteId/review', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(), govcyLoadSubmissionData(), govcyReviewPageHandler(), renderGovcyPage());
|
|
267
267
|
|
|
268
268
|
// ✅📄 -- ROUTE: Add Success PDF Route (BEFORE the dynamic route)
|
|
269
|
-
app.get('/:siteId/success/pdf', serviceConfigDataMiddleware, requireAuth,
|
|
269
|
+
app.get('/:siteId/success/pdf', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(), govcySuccessPageHandler(true), govcyPDFRender());
|
|
270
270
|
|
|
271
271
|
// ✅ -- ROUTE: Add Success Page Route (BEFORE the dynamic route)
|
|
272
|
-
app.get('/:siteId/success', serviceConfigDataMiddleware, requireAuth,
|
|
272
|
+
app.get('/:siteId/success', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(), govcySuccessPageHandler(), renderGovcyPage());
|
|
273
273
|
|
|
274
274
|
// 👀🗃️ -- ROUTE: View file (BEFORE the dynamic route)
|
|
275
|
-
app.get('/:siteId/:pageUrl/view-file/:elementName', serviceConfigDataMiddleware, requireAuth,
|
|
275
|
+
app.get('/:siteId/:pageUrl/view-file/:elementName', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(), govcyLoadSubmissionData(), govcyFileViewHandler());
|
|
276
276
|
|
|
277
277
|
// ❌🗃️ -- ROUTE: Delete file (BEFORE the dynamic route)
|
|
278
|
-
app.get('/:siteId/:pageUrl/delete-file/:elementName', serviceConfigDataMiddleware, requireAuth,
|
|
278
|
+
app.get('/:siteId/:pageUrl/delete-file/:elementName', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(), govcyLoadSubmissionData(), govcyFileDeletePageHandler(), renderGovcyPage());
|
|
279
279
|
|
|
280
280
|
// ➕ -- ROUTE: Add item page (BEFORE the generic dynamic route)
|
|
281
281
|
app.get('/:siteId/:pageUrl/multiple/add',
|
|
282
282
|
serviceConfigDataMiddleware,
|
|
283
283
|
requireAuth,
|
|
284
|
-
|
|
284
|
+
cyLoginPolicy,
|
|
285
285
|
govcyServiceEligibilityHandler(true),
|
|
286
286
|
govcyLoadSubmissionData(),
|
|
287
287
|
govcyMultipleThingsAddHandler(),
|
|
@@ -293,7 +293,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
293
293
|
app.post('/:siteId/:pageUrl/multiple/add',
|
|
294
294
|
serviceConfigDataMiddleware,
|
|
295
295
|
requireAuth,
|
|
296
|
-
|
|
296
|
+
cyLoginPolicy,
|
|
297
297
|
govcyServiceEligibilityHandler(true),
|
|
298
298
|
govcyMultipleThingsAddPostHandler()
|
|
299
299
|
);
|
|
@@ -302,7 +302,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
302
302
|
app.get('/:siteId/:pageUrl/multiple/edit/:index',
|
|
303
303
|
serviceConfigDataMiddleware,
|
|
304
304
|
requireAuth,
|
|
305
|
-
|
|
305
|
+
cyLoginPolicy,
|
|
306
306
|
govcyServiceEligibilityHandler(true),
|
|
307
307
|
govcyLoadSubmissionData(),
|
|
308
308
|
govcyMultipleThingsEditHandler(),
|
|
@@ -313,7 +313,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
313
313
|
app.post('/:siteId/:pageUrl/multiple/edit/:index',
|
|
314
314
|
serviceConfigDataMiddleware,
|
|
315
315
|
requireAuth,
|
|
316
|
-
|
|
316
|
+
cyLoginPolicy,
|
|
317
317
|
govcyServiceEligibilityHandler(true),
|
|
318
318
|
govcyMultipleThingsEditPostHandler()
|
|
319
319
|
);
|
|
@@ -322,7 +322,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
322
322
|
app.get('/:siteId/:pageUrl/multiple/delete/:index',
|
|
323
323
|
serviceConfigDataMiddleware,
|
|
324
324
|
requireAuth,
|
|
325
|
-
|
|
325
|
+
cyLoginPolicy,
|
|
326
326
|
govcyServiceEligibilityHandler(),
|
|
327
327
|
govcyLoadSubmissionData(),
|
|
328
328
|
govcyMultipleThingsDeletePageHandler(),
|
|
@@ -333,7 +333,7 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
333
333
|
app.post('/:siteId/:pageUrl/multiple/delete/:index',
|
|
334
334
|
serviceConfigDataMiddleware,
|
|
335
335
|
requireAuth,
|
|
336
|
-
|
|
336
|
+
cyLoginPolicy,
|
|
337
337
|
govcyServiceEligibilityHandler(true),
|
|
338
338
|
govcyMultipleThingsDeletePostHandler()
|
|
339
339
|
);
|
|
@@ -344,22 +344,22 @@ export default function initializeGovCyExpressService(opts = {}) {
|
|
|
344
344
|
app.post('/:siteId/:pageUrl/update-my-details-response',
|
|
345
345
|
serviceConfigDataMiddleware,
|
|
346
346
|
requireAuth,
|
|
347
|
-
|
|
347
|
+
cyLoginPolicy,
|
|
348
348
|
govcyServiceEligibilityHandler(true),
|
|
349
349
|
govcyUpdateMyDetailsPostHandler());
|
|
350
350
|
// ----- `updateMyDetails` handling
|
|
351
351
|
|
|
352
352
|
// 📝 -- ROUTE: Dynamic route to render pages based on siteId and pageUrl, using govcyPageHandler middleware
|
|
353
|
-
app.get('/:siteId/:pageUrl', serviceConfigDataMiddleware, requireAuth,
|
|
353
|
+
app.get('/:siteId/:pageUrl', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(true), govcyLoadSubmissionData(), govcyPageHandler(), renderGovcyPage());
|
|
354
354
|
|
|
355
355
|
// ❌🗃️📥 -- ROUTE: Handle POST requests for delete file
|
|
356
|
-
app.post('/:siteId/:pageUrl/delete-file/:elementName', serviceConfigDataMiddleware, requireAuth,
|
|
356
|
+
app.post('/:siteId/:pageUrl/delete-file/:elementName', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(true), govcyFileDeletePostHandler());
|
|
357
357
|
|
|
358
358
|
// 📥 -- ROUTE: Handle POST requests for review page. The `submit` action
|
|
359
|
-
app.post('/:siteId/review', serviceConfigDataMiddleware, requireAuth,
|
|
359
|
+
app.post('/:siteId/review', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(), govcyReviewPostHandler());
|
|
360
360
|
|
|
361
361
|
// 👀📥 -- ROUTE: Handle POST requests (Form Submissions) based on siteId and pageUrl, using govcyFormsPostHandler middleware
|
|
362
|
-
app.post('/:siteId/:pageUrl', serviceConfigDataMiddleware, requireAuth,
|
|
362
|
+
app.post('/:siteId/:pageUrl', serviceConfigDataMiddleware, requireAuth, cyLoginPolicy, govcyServiceEligibilityHandler(true), govcyFormsPostHandler());
|
|
363
363
|
|
|
364
364
|
// post for /:siteId/review
|
|
365
365
|
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
import { getLoginUrl, handleCallback, getLogoutUrl } from '../auth/cyLoginAuth.mjs';
|
|
8
8
|
import { logger } from "../utils/govcyLogger.mjs";
|
|
9
9
|
import { handleMiddlewareError } from "../utils/govcyUtils.mjs";
|
|
10
|
-
import { errorResponse } from "../utils/govcyApiResponse.mjs";
|
|
10
|
+
import { errorResponse } from "../utils/govcyApiResponse.mjs";
|
|
11
11
|
import { isApiRequest } from '../utils/govcyApiDetection.mjs';
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
/**
|
|
15
15
|
* Middleware to check if the user is authenticated. If not, redirect to the login page.
|
|
16
16
|
*
|
|
@@ -33,39 +33,7 @@ export function requireAuth(req, res, next) {
|
|
|
33
33
|
next();
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
* Middleware to enforce natural person policy. If the user is not a natural person, return a 403 error.
|
|
38
|
-
*
|
|
39
|
-
* @param {object} req The request object
|
|
40
|
-
* @param {object} res The response object
|
|
41
|
-
* @param {object} next The next middleware function
|
|
42
|
-
*/
|
|
43
|
-
export function naturalPersonPolicy(req, res, next) {
|
|
44
|
-
// // allow only natural persons with approved profiles
|
|
45
|
-
// if (req.session.user.profile_type == 'Individual' && req.session.user.unique_identifier) {
|
|
46
|
-
// next();
|
|
47
|
-
// } else {
|
|
48
|
-
// return handleMiddlewareError("🚨 Access Denied: natural person policy not met.", 403, next);
|
|
49
|
-
// }
|
|
50
|
-
// https://dev.azure.com/cyprus-gov-cds/Documentation/_wiki/wikis/Documentation/42/For-Cyprus-Natural-or-Legal-person
|
|
51
|
-
const { profile_type, unique_identifier } = req.session.user || {};
|
|
52
|
-
// Allow only natural persons with approved profiles
|
|
53
|
-
if (profile_type === 'Individual' && unique_identifier) {
|
|
54
|
-
|
|
55
|
-
// Validate Cypriot Citizen (starts with "00" and is 10 characters long)
|
|
56
|
-
if (unique_identifier.startsWith('00') && unique_identifier.length === 10) {
|
|
57
|
-
return next();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Validate Foreigner with ARN (starts with "05" and is 10 characters long)
|
|
61
|
-
if (unique_identifier.startsWith('05') && unique_identifier.length === 10) {
|
|
62
|
-
return next();
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Deny access if validation fails
|
|
67
|
-
return handleMiddlewareError("🚨 Access Denied: natural person policy not met.", 403, next);
|
|
68
|
-
}
|
|
36
|
+
/* c8 ignore start */
|
|
69
37
|
|
|
70
38
|
/**
|
|
71
39
|
* Middleware to handle the login route. Redirects the user to the login URL.
|
|
@@ -107,11 +75,11 @@ export function handleSigninOidc() {
|
|
|
107
75
|
// Redirect to the stored URL after login or fallback to '/'
|
|
108
76
|
const redirectUrl = req.session.redirectAfterLogin || '/';
|
|
109
77
|
// Clean up session for redirect after login
|
|
110
|
-
delete req.session.redirectAfterLogin;
|
|
78
|
+
delete req.session.redirectAfterLogin;
|
|
111
79
|
// Redirect to the stored URL
|
|
112
80
|
res.redirect(redirectUrl);
|
|
113
81
|
} catch (error) {
|
|
114
|
-
logger.debug('Token exchange failed:', error,req);
|
|
82
|
+
logger.debug('Token exchange failed:', error, req);
|
|
115
83
|
res.status(500).send('Authentication failed');
|
|
116
84
|
}
|
|
117
85
|
}
|
|
@@ -138,4 +106,79 @@ export function handleLogout() {
|
|
|
138
106
|
});
|
|
139
107
|
};
|
|
140
108
|
}
|
|
141
|
-
/* c8 ignore end */
|
|
109
|
+
/* c8 ignore end */
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
/************************************************************************/
|
|
113
|
+
/**
|
|
114
|
+
* Middleware to enforce natural person policy. If the user is not a verified natural person, return a false.
|
|
115
|
+
*
|
|
116
|
+
* @param {object} req The request object
|
|
117
|
+
*/
|
|
118
|
+
export function naturalPersonPolicy(req) {
|
|
119
|
+
// https://dev.azure.com/cyprus-gov-cds/Documentation/_wiki/wikis/Documentation/42/For-Cyprus-Natural-or-Legal-person
|
|
120
|
+
const { profile_type, unique_identifier } = req.session.user || {};
|
|
121
|
+
// Allow only natural persons with approved profiles
|
|
122
|
+
if (profile_type === 'Individual' && unique_identifier) {
|
|
123
|
+
|
|
124
|
+
// Validate Cypriot Citizen (starts with "00" and is 10 characters long)
|
|
125
|
+
if (unique_identifier.startsWith('00') && unique_identifier.length === 10) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Validate Foreigner with ARN (starts with "05" and is 10 characters long)
|
|
130
|
+
if (unique_identifier.startsWith('05') && unique_identifier.length === 10) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Deny access if validation fails
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** * Middleware to enforce legal person policy. If the user is not a verified legal person, return false.
|
|
140
|
+
*
|
|
141
|
+
* @param {object} req The request object
|
|
142
|
+
*/
|
|
143
|
+
export function legalPersonPolicy(req) {
|
|
144
|
+
// https://dev.azure.com/cyprus-gov-cds/Documentation/_wiki/wikis/Documentation/42/For-Cyprus-Natural-or-Legal-person
|
|
145
|
+
const { profile_type, legal_unique_identifier } = req.session.user || {};
|
|
146
|
+
// Allow only legal persons with approved profiles
|
|
147
|
+
if (profile_type === 'Organisation' && legal_unique_identifier) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Deny access if validation fails
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const policyRegistry = {
|
|
156
|
+
naturalPerson: naturalPersonPolicy,
|
|
157
|
+
legalPerson: legalPersonPolicy,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export function cyLoginPolicy(req, res, next) {
|
|
161
|
+
// Check what is allowed in the service configuration
|
|
162
|
+
const allowed = req?.serviceData?.site?.cyLoginPolicies || ["naturalPerson"];
|
|
163
|
+
|
|
164
|
+
// Check each policy in the allowed list
|
|
165
|
+
for (const name of allowed) {
|
|
166
|
+
const policy = policyRegistry[name];
|
|
167
|
+
// Skip if the policy is not registered
|
|
168
|
+
if (!policy) {
|
|
169
|
+
console.warn(`🚨 Unknown policy: ${name}`);
|
|
170
|
+
continue
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// 🚨 Strict mode: let errors throw naturally if data is malformed
|
|
174
|
+
const passed = policy(req);
|
|
175
|
+
if (passed) return next();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return handleMiddlewareError(
|
|
179
|
+
"🚨 Access Denied: none of the allowed CY Login policies matched.",
|
|
180
|
+
403,
|
|
181
|
+
next
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
/************************************************************************/
|
|
@@ -26,6 +26,7 @@ export function govcyPageHandler() {
|
|
|
26
26
|
logger.debug(`No pageUrl provided for siteId: ${siteId}`, req);
|
|
27
27
|
// Example: Redirect to a default page or load a homepage
|
|
28
28
|
pageUrl = "index"; // Change "index" to whatever makes sense for your service
|
|
29
|
+
req.params.pageUrl = pageUrl; // Update req.params to reflect the new pageUrl
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// 🔍 Find the page by pageUrl
|
|
@@ -497,7 +497,7 @@ export function govcyUpdateMyDetailsPostHandler() {
|
|
|
497
497
|
// 🔄 User chose to update their details externally
|
|
498
498
|
const redirectUrl = constructUpdateMyDetailsRedirect(req, userId, umdBaseURL, returnUrl);
|
|
499
499
|
logger.info("User opted to update details externally", {
|
|
500
|
-
userId: user.
|
|
500
|
+
userId: user.sub,
|
|
501
501
|
redirectUrl
|
|
502
502
|
});
|
|
503
503
|
return res.redirect(redirectUrl);
|
|
@@ -163,17 +163,17 @@ export const staticResources = {
|
|
|
163
163
|
},
|
|
164
164
|
multipleThingsEnptyState: {
|
|
165
165
|
en: "You did not add any entries.",
|
|
166
|
-
el: "Δεν έχετε προσθέσει ακόμη κάποια
|
|
166
|
+
el: "Δεν έχετε προσθέσει ακόμη κάποια καταχώριση.",
|
|
167
167
|
tr: "You did not add any entries."
|
|
168
168
|
},
|
|
169
169
|
multipleThingsEmptyStateReview: {
|
|
170
170
|
en: "You did not add any entries.",
|
|
171
|
-
el: "Δεν έχετε προσθέσει κάποια
|
|
171
|
+
el: "Δεν έχετε προσθέσει κάποια καταχώριση.",
|
|
172
172
|
tr: "You did not add any entries yet."
|
|
173
173
|
},
|
|
174
174
|
multipleThingsAddEntry: {
|
|
175
175
|
en: "Add new entry",
|
|
176
|
-
el: "Προσθήκη νέας
|
|
176
|
+
el: "Προσθήκη νέας καταχώρισης",
|
|
177
177
|
tr: "Add new entry"
|
|
178
178
|
},
|
|
179
179
|
multipleThingsDedupeMessage: {
|
|
@@ -193,7 +193,7 @@ export const staticResources = {
|
|
|
193
193
|
},
|
|
194
194
|
multipleThingsItemsValidationPrefix: {
|
|
195
195
|
en: "Entry {{index}} - ",
|
|
196
|
-
el: "
|
|
196
|
+
el: "Καταχώριση {{index}} - ",
|
|
197
197
|
tr: "Entry {{index}} - "
|
|
198
198
|
},
|
|
199
199
|
multipleThingsAddSuffix: {
|
|
@@ -208,12 +208,12 @@ export const staticResources = {
|
|
|
208
208
|
},
|
|
209
209
|
multipleThingsDeleteTitle: {
|
|
210
210
|
en: "Are you sure you want to delete the item \"{{item}}\"",
|
|
211
|
-
el: "Σίγουρα θέλετε να διαγράψετε την
|
|
211
|
+
el: "Σίγουρα θέλετε να διαγράψετε την καταχώριση \"{{item}}\"",
|
|
212
212
|
tr: "Are you sure you want to delete the item \"{{item}}\""
|
|
213
213
|
},
|
|
214
214
|
multipleThingsDeleteValidationError: {
|
|
215
215
|
en: "Select if you want to delete this item",
|
|
216
|
-
el: "Επιλέξτε αν θέλετε να διαγράψετε αυτή την
|
|
216
|
+
el: "Επιλέξτε αν θέλετε να διαγράψετε αυτή την καταχώριση",
|
|
217
217
|
tr: "Select if you want to delete the item"
|
|
218
218
|
},
|
|
219
219
|
multipleThingsEntries: {
|
|
@@ -222,12 +222,12 @@ export const staticResources = {
|
|
|
222
222
|
tr: "Entries"
|
|
223
223
|
},
|
|
224
224
|
multipleThingsDeleteYesOption: {
|
|
225
|
-
el: "Ναι, θέλω να διαγράψω την
|
|
225
|
+
el: "Ναι, θέλω να διαγράψω την καταχώριση",
|
|
226
226
|
en: "Yes, I want to delete this entry",
|
|
227
227
|
tr: "Yes, I want to delete this entry"
|
|
228
228
|
},
|
|
229
229
|
multipleThingsDeleteNoOption: {
|
|
230
|
-
el: "Όχι, δεν θέλω να διαγράψω την
|
|
230
|
+
el: "Όχι, δεν θέλω να διαγράψω την καταχώριση",
|
|
231
231
|
en: "No, I don't want to delete this entry",
|
|
232
232
|
tr: "No, I don't want to delete this entry"
|
|
233
233
|
},
|