@gov-cy/govcy-express-services 1.11.3 → 1.12.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 +54 -47
- package/package.json +2 -2
- package/src/utils/govcyValidator.mjs +53 -17
package/README.md
CHANGED
|
@@ -156,17 +156,17 @@ The CY Login settings are configured in the `secrets/.env` file.
|
|
|
156
156
|
|
|
157
157
|
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.
|
|
158
158
|
|
|
159
|
-
```json
|
|
160
|
-
"cyLoginPolicies": ["naturalPerson", "legalPerson", "eidasNaturalPerson"]
|
|
161
|
-
```
|
|
159
|
+
```json
|
|
160
|
+
"cyLoginPolicies": ["naturalPerson", "legalPerson", "eidasNaturalPerson"]
|
|
161
|
+
```
|
|
162
162
|
|
|
163
163
|
##### Supported Policies
|
|
164
164
|
|
|
165
|
-
| Policy name | Description | Typical use |
|
|
166
|
-
| --------------- | ------------------------------------------------------------ | ---------------------------------------------------- |
|
|
167
|
-
| `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. |
|
|
168
|
-
| `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. |
|
|
169
|
-
| `eidasNaturalPerson` | Allows eIDAS natural persons identified by `profile_type: "Individual"` and `unique_identifier` in the `CC/CC/<identifier>` format. | Cross-border eIDAS individual services. |
|
|
165
|
+
| Policy name | Description | Typical use |
|
|
166
|
+
| --------------- | ------------------------------------------------------------ | ---------------------------------------------------- |
|
|
167
|
+
| `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. |
|
|
168
|
+
| `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. |
|
|
169
|
+
| `eidasNaturalPerson` | Allows eIDAS natural persons identified by `profile_type: "Individual"` and `unique_identifier` in the `CC/CC/<identifier>` format. | Cross-border eIDAS individual services. |
|
|
170
170
|
|
|
171
171
|
##### How it works
|
|
172
172
|
|
|
@@ -185,21 +185,21 @@ This maintains backward compatibility with existing services that only supported
|
|
|
185
185
|
|
|
186
186
|
##### Example
|
|
187
187
|
|
|
188
|
-
Allow both natural and legal persons:
|
|
188
|
+
Allow both natural and legal persons:
|
|
189
189
|
|
|
190
|
-
```json
|
|
191
|
-
"site": {
|
|
192
|
-
"cyLoginPolicies": ["naturalPerson", "legalPerson"]
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Allow Cypriot and eIDAS natural persons:
|
|
197
|
-
|
|
198
|
-
```json
|
|
199
|
-
"site": {
|
|
200
|
-
"cyLoginPolicies": ["naturalPerson", "eidasNaturalPerson"]
|
|
201
|
-
}
|
|
202
|
-
```
|
|
190
|
+
```json
|
|
191
|
+
"site": {
|
|
192
|
+
"cyLoginPolicies": ["naturalPerson", "legalPerson"]
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Allow Cypriot and eIDAS natural persons:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
"site": {
|
|
200
|
+
"cyLoginPolicies": ["naturalPerson", "eidasNaturalPerson"]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
203
|
|
|
204
204
|
Restrict access to natural persons only:
|
|
205
205
|
|
|
@@ -1905,10 +1905,10 @@ The validation rules for each element are defined in the `"validations` array fo
|
|
|
1905
1905
|
- `noSpecialChars`: Consists only of letters, numbers and some other characters
|
|
1906
1906
|
- `noSpecialCharsEl`: Consists only of Greek letters, numbers and some other characters
|
|
1907
1907
|
- `textWide_EL`: Consists of Greek Letters and a wider range of characters
|
|
1908
|
-
- `textWide_EL_Latin`: Consists of Greek Letters, Latin letters, numbers and a wider range of characters
|
|
1909
|
-
- `textWide_EL_Latin_TR`: Consists of Greek Letters, Latin letters, Turkish letters, numbers and a wider range of characters
|
|
1910
|
-
- `textWide_EL_Latn`: Deprecated alias of `textWide_EL_Latin` (still supported for backward compatibility)
|
|
1911
|
-
- `textWide_EL_Latn_TR`: Deprecated alias of `textWide_EL_Latin_TR` (still supported for backward compatibility)
|
|
1908
|
+
- `textWide_EL_Latin`: Consists of Greek Letters, Latin letters, numbers and a wider range of characters
|
|
1909
|
+
- `textWide_EL_Latin_TR`: Consists of Greek Letters, Latin letters, Turkish letters, numbers and a wider range of characters
|
|
1910
|
+
- `textWide_EL_Latn`: Deprecated alias of `textWide_EL_Latin` (still supported for backward compatibility)
|
|
1911
|
+
- `textWide_EL_Latn_TR`: Deprecated alias of `textWide_EL_Latin_TR` (still supported for backward compatibility)
|
|
1912
1912
|
- `textWide_UTF`: Consists of any letters, numbers and a wider range of characters
|
|
1913
1913
|
- `numeric`: Numeric input
|
|
1914
1914
|
- `numDecimal`: Numeric decimal input
|
|
@@ -1925,16 +1925,23 @@ The validation rules for each element are defined in the `"validations` array fo
|
|
|
1925
1925
|
- `email`: Email input
|
|
1926
1926
|
- `date`: Date input (DD/MM/YYYY)
|
|
1927
1927
|
- `dateISO`: ISO date input `YYYY-M-D`
|
|
1928
|
-
- `dateDMY`: European/Common Format date input `D/M/YYYY`
|
|
1929
|
-
- `maxCurrentYear`: Maximum current year input
|
|
1928
|
+
- `dateDMY`: European/Common Format date input `D/M/YYYY`
|
|
1929
|
+
- `maxCurrentYear`: Maximum current year input. Use it for year-only inputs e.g. `2026` or formats that start with year e.g. `YYYY-M-D` (which works well with `dateInput` component). Do not use it with `D/M/YYYY` values (e.g. datePicker values); use date-based checks instead (`minCurrentDate`, `maxCurrentDate`, `withinDaysBeforeToday`, `withinDaysAfterToday`).
|
|
1930
|
+
- `minCurrentYear`: Minimum current year input. Use it for year-only inputs e.g. `2026` or formats that start with year e.g. `YYYY-M-D` (which works well with `dateInput` component). Do not use it with `D/M/YYYY` values (e.g. datePicker values); use date-based checks instead (`minCurrentDate`, `maxCurrentDate`, `withinDaysBeforeToday`, `withinDaysAfterToday`).
|
|
1931
|
+
- `minCurrentDate`: Checks if the value is greater than or equal to the current date.
|
|
1932
|
+
- `maxCurrentDate`: Checks if the value is less than or equal to the current date.
|
|
1930
1933
|
- `required`: Checks if the value is not null, undefined, or an empty string (after trimming).
|
|
1931
1934
|
- `length`: Checks if the value has a maximum length passed in the `checkValue` parameter.
|
|
1932
1935
|
- `regCheck`: Checks if the value matches the specified regular expression passed in the `checkValue` parameter.
|
|
1933
|
-
- `minValue`: Checks if the value is greater than or equal to the specified minimum value passed in the `checkValue` parameter.
|
|
1934
|
-
- `maxValue`: Checks if the value is less than or equal to the specified maximum value passed in the `checkValue` parameter.
|
|
1935
|
-
- `minValueDate`: Checks if the value is greater than or equal to the specified minimum date passed in the `checkValue` parameter.
|
|
1936
|
-
- `maxValueDate`: Checks if the value is less than or equal to the specified maximum date passed in the `checkValue` parameter.
|
|
1937
|
-
- `
|
|
1936
|
+
- `minValue`: Checks if the value is greater than or equal to the specified minimum value passed in the `checkValue` parameter.
|
|
1937
|
+
- `maxValue`: Checks if the value is less than or equal to the specified maximum value passed in the `checkValue` parameter.
|
|
1938
|
+
- `minValueDate`: Checks if the value is greater than or equal to the specified minimum date passed in the `checkValue` parameter.
|
|
1939
|
+
- `maxValueDate`: Checks if the value is less than or equal to the specified maximum date passed in the `checkValue` parameter.
|
|
1940
|
+
- `withinDaysBeforeToday`: Checks if the value is within the last `N` days (between `today - N` and `today`), where `N` is passed in `checkValue`.
|
|
1941
|
+
- `withinDaysAfterToday`: Checks if the value is within the next `N` days (between `today` and `today + N`), where `N` is passed in `checkValue`.
|
|
1942
|
+
- `minLength`: Checks if the value has a minimum length passed in the `checkValue` parameter.
|
|
1943
|
+
|
|
1944
|
+
For `withinDaysBeforeToday` and `withinDaysAfterToday`, validation compares calendar dates only (local server timezone), not time-of-day. Supported input date formats are `YYYY-M-D` / `YYYY-MM-DD` and `D/M/YYYY` / `DD/MM/YYYY`.
|
|
1938
1945
|
|
|
1939
1946
|
Example:
|
|
1940
1947
|
|
|
@@ -2066,16 +2073,16 @@ To use data layer values, use the special `dataLayer[]` array. For example `data
|
|
|
2066
2073
|
- `formData` is a reserved word for the form data (already inputed data by the user) for that page
|
|
2067
2074
|
- `showExtra`refers to a input component with that name
|
|
2068
2075
|
|
|
2069
|
-
The `dataLayer` typically contains keys such as:
|
|
2070
|
-
- `inputData`: **All data submitted by the user through forms**
|
|
2071
|
-
- `eligibilityResults`: **Cached results from service eligibility API checks**
|
|
2072
|
-
- `user.profile_type`: **Authenticated user profile type from CY Login context**
|
|
2073
|
-
- `user.policy`: **The matched CY Login policy name for the current session**
|
|
2076
|
+
The `dataLayer` typically contains keys such as:
|
|
2077
|
+
- `inputData`: **All data submitted by the user through forms**
|
|
2078
|
+
- `eligibilityResults`: **Cached results from service eligibility API checks**
|
|
2079
|
+
- `user.profile_type`: **Authenticated user profile type from CY Login context**
|
|
2080
|
+
- `user.policy`: **The matched CY Login policy name for the current session**
|
|
2074
2081
|
|
|
2075
2082
|
Example structure for a service with ID `my-service`:
|
|
2076
2083
|
|
|
2077
|
-
```js
|
|
2078
|
-
dataLayer = {
|
|
2084
|
+
```js
|
|
2085
|
+
dataLayer = {
|
|
2079
2086
|
'my-service.inputData.index.formData.fullName': 'John Smith',
|
|
2080
2087
|
'my-service.inputData.index.formData.age': '34',
|
|
2081
2088
|
'my-service.inputData.contact-details.formData.telephone': '+35712345678',
|
|
@@ -2085,13 +2092,13 @@ dataLayer = {
|
|
|
2085
2092
|
"permanent_residence"
|
|
2086
2093
|
],
|
|
2087
2094
|
'my-service.inputData.want-to-apply.formData.option-radio': 'yes',
|
|
2088
|
-
'my-service.eligibilityResults.check1.succeeded': true,
|
|
2089
|
-
'my-service.eligibilityResults.check2.ErrorCode': 0,
|
|
2090
|
-
'user.profile_type': 'Individual',
|
|
2091
|
-
'user.policy': 'naturalPerson'
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
```
|
|
2095
|
+
'my-service.eligibilityResults.check1.succeeded': true,
|
|
2096
|
+
'my-service.eligibilityResults.check2.ErrorCode': 0,
|
|
2097
|
+
'user.profile_type': 'Individual',
|
|
2098
|
+
'user.policy': 'naturalPerson'
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
```
|
|
2095
2102
|
|
|
2096
2103
|
If any part of the key path is missing (e.g., the page hasn’t been visited yet or a form field was left empty), the expression will safely return `undefined` and **will not throw an error**. This behavior is by design, so that conditional logic expressions can fail silently and fallback gracefully.
|
|
2097
2104
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gov-cy/govcy-express-services",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@gov-cy/dsf-email-templates": "^2.1.15",
|
|
61
|
-
"@gov-cy/govcy-frontend-renderer": "^1.
|
|
61
|
+
"@gov-cy/govcy-frontend-renderer": "^1.28.1",
|
|
62
62
|
"axios": "^1.9.0",
|
|
63
63
|
"cookie-parser": "^1.4.7",
|
|
64
64
|
"dotenv": "^16.3.1",
|
|
@@ -146,16 +146,42 @@ function validateValue(value, rules) {
|
|
|
146
146
|
}
|
|
147
147
|
return valueDate >= min;
|
|
148
148
|
},
|
|
149
|
-
maxValueDate: (val, maxDate) => {
|
|
150
|
-
const valueDate = parseDate(val); // Parse the input date
|
|
151
|
-
const max = parseDate(maxDate); // Parse the maximum date
|
|
152
|
-
if (isNaN(valueDate) || isNaN(max)) {
|
|
153
|
-
return false; // Return false if either date is invalid
|
|
154
|
-
}
|
|
155
|
-
return valueDate <= max;
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
|
|
149
|
+
maxValueDate: (val, maxDate) => {
|
|
150
|
+
const valueDate = parseDate(val); // Parse the input date
|
|
151
|
+
const max = parseDate(maxDate); // Parse the maximum date
|
|
152
|
+
if (isNaN(valueDate) || isNaN(max)) {
|
|
153
|
+
return false; // Return false if either date is invalid
|
|
154
|
+
}
|
|
155
|
+
return valueDate <= max;
|
|
156
|
+
},
|
|
157
|
+
withinDaysBeforeToday: (val, daysBeforeToday) => {
|
|
158
|
+
const valueDate = parseDate(val);
|
|
159
|
+
const days = Number.parseInt(daysBeforeToday, 10);
|
|
160
|
+
if (isNaN(valueDate) || Number.isNaN(days)) return false;
|
|
161
|
+
|
|
162
|
+
const today = new Date();
|
|
163
|
+
const valueOnly = new Date(valueDate.getFullYear(), valueDate.getMonth(), valueDate.getDate());
|
|
164
|
+
const todayOnly = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
165
|
+
const minAllowedDate = new Date(todayOnly);
|
|
166
|
+
minAllowedDate.setDate(minAllowedDate.getDate() - days);
|
|
167
|
+
|
|
168
|
+
return valueOnly >= minAllowedDate && valueOnly <= todayOnly;
|
|
169
|
+
},
|
|
170
|
+
withinDaysAfterToday: (val, daysAfterToday) => {
|
|
171
|
+
const valueDate = parseDate(val);
|
|
172
|
+
const days = Number.parseInt(daysAfterToday, 10);
|
|
173
|
+
if (isNaN(valueDate) || Number.isNaN(days)) return false;
|
|
174
|
+
|
|
175
|
+
const today = new Date();
|
|
176
|
+
const valueOnly = new Date(valueDate.getFullYear(), valueDate.getMonth(), valueDate.getDate());
|
|
177
|
+
const todayOnly = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
178
|
+
const maxAllowedDate = new Date(todayOnly);
|
|
179
|
+
maxAllowedDate.setDate(maxAllowedDate.getDate() + days);
|
|
180
|
+
|
|
181
|
+
return valueOnly >= todayOnly && valueOnly <= maxAllowedDate;
|
|
182
|
+
},
|
|
183
|
+
minLength: (val, min) => val.length >= min
|
|
184
|
+
};
|
|
159
185
|
|
|
160
186
|
for (const rule of rules) {
|
|
161
187
|
// Extract rule parameters
|
|
@@ -219,13 +245,23 @@ function validateValue(value, rules) {
|
|
|
219
245
|
return message;
|
|
220
246
|
}
|
|
221
247
|
|
|
222
|
-
// Check for "minLength"
|
|
223
|
-
if (check === 'minLength' && !validationRules.minLength(value, checkValue)) {
|
|
224
|
-
return message;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
248
|
+
// Check for "minLength"
|
|
249
|
+
if (check === 'minLength' && !validationRules.minLength(value, checkValue)) {
|
|
250
|
+
return message;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check for "withinDaysBeforeToday"
|
|
254
|
+
if (check === 'withinDaysBeforeToday' && !validationRules.withinDaysBeforeToday(value, checkValue)) {
|
|
255
|
+
return message;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Check for "withinDaysAfterToday"
|
|
259
|
+
if (check === 'withinDaysAfterToday' && !validationRules.withinDaysAfterToday(value, checkValue)) {
|
|
260
|
+
return message;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
}
|
|
229
265
|
|
|
230
266
|
return null;
|
|
231
267
|
}
|