Skip to content

Commit 0d364c6

Browse files
authored
fix(calm-hub): fix missing validation annotations (finos#2630)
1 parent 92c5b04 commit 0d364c6

12 files changed

Lines changed: 140 additions & 45 deletions

calm-hub/AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,12 @@ void return_a_400_when_an_invalid_format_of_namespace_is_provided_on_get_pattern
298298
```
299299

300300
Import validation constants from `ResourceValidationConstants`:
301+
301302
```java
302-
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_NAME_MESSAGE;
303+
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_MESSAGE;
303304
import static org.finos.calm.resources.ResourceValidationConstants.VERSION_MESSAGE;
304305
import static org.finos.calm.resources.ResourceValidationConstants.NAMESPACE_MESSAGE;
306+
import static org.finos.calm.resources.ResourceValidationConstants.USERNAME_MESSAGE;
305307
```
306308

307309
#### Mongo Store Tests (`@QuarkusTest` + `@InjectMock`)

calm-hub/src/main/java/org/finos/calm/domain/Domain.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import jakarta.validation.constraints.NotNull;
55
import jakarta.validation.constraints.Pattern;
66

7-
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_NAME_MESSAGE;
8-
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_NAME_REGEX;
7+
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_MESSAGE;
8+
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_REGEX;
99

1010
/**
1111
* Represents a domain in the CALM system.
@@ -30,7 +30,7 @@ public Domain() {
3030

3131
}
3232

33-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
33+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
3434
@NotBlank(message = "domain name must not be blank")
3535
@NotNull(message = "domain name must not be null")
3636
private String name;

calm-hub/src/main/java/org/finos/calm/domain/UserAccess.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
55
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
66
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
7+
import jakarta.validation.constraints.NotNull;
8+
import jakarta.validation.constraints.Pattern;
79

810
import java.time.LocalDateTime;
911
import java.util.Objects;
1012

13+
import static org.finos.calm.resources.ResourceValidationConstants.*;
14+
1115
/**
1216
* Represents a CalmHub user access grant, scoped to either a namespace or a domain.
1317
*/
@@ -19,10 +23,10 @@ public enum Permission {
1923
admin
2024
}
2125

22-
private String username;
23-
private Permission permission;
24-
private String namespace;
25-
private String domain;
26+
private @Pattern(regexp = USERNAME_REGEX, message = USERNAME_MESSAGE) @NotNull String username;
27+
private @NotNull Permission permission;
28+
private @Pattern(regexp = NAMESPACE_REGEX, message = NAMESPACE_MESSAGE) String namespace;
29+
private @Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE) String domain;
2630
private int userAccessId;
2731

2832
@JsonDeserialize(using = LocalDateTimeDeserializer.class)

calm-hub/src/main/java/org/finos/calm/mcp/tools/McpValidationHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.function.Supplier;
99
import java.util.stream.Collectors;
1010

11-
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_NAME_REGEX;
11+
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_REGEX;
1212
import static org.finos.calm.resources.ResourceValidationConstants.NAMESPACE_REGEX;
1313
import static org.finos.calm.resources.ResourceValidationConstants.QUERY_PARAM_NO_WHITESPACE_REGEX;
1414
import static org.finos.calm.resources.ResourceValidationConstants.VERSION_REGEX;
@@ -108,7 +108,7 @@ static String validateDomain(String domain) {
108108
if (domain == null || domain.isBlank()) {
109109
return "Error: Domain must not be blank.";
110110
}
111-
if (!domain.matches(DOMAIN_NAME_REGEX)) {
111+
if (!domain.matches(DOMAIN_REGEX)) {
112112
return "Error: Invalid domain format '" + domain + "'. Must be alphanumeric with optional hyphens.";
113113
}
114114
return null;

calm-hub/src/main/java/org/finos/calm/resources/ControlResource.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public ControlResource(ControlStore store) {
4949
@PermissionsAllowed(CalmHubScopes.DOMAIN_READ)
5050
public Response getControlsForDomain(
5151
@PathParam("domain")
52-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
52+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
5353
String domain) {
5454
try {
5555
return Response.ok(new ValueWrapper<>(store.getControlsForDomain(domain))).build();
@@ -70,7 +70,7 @@ public Response getControlsForDomain(
7070
@PermissionsAllowed(CalmHubScopes.DOMAIN_WRITE)
7171
public Response createControlForDomain(
7272
@PathParam("domain")
73-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
73+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
7474
String domain,
7575
@Valid @NotNull(message = "Request must not be null") CreateControlRequirement createControlRequirement) {
7676
try {
@@ -92,7 +92,7 @@ public Response createControlForDomain(
9292
@PermissionsAllowed(CalmHubScopes.DOMAIN_READ)
9393
public Response getRequirementVersions(
9494
@PathParam("domain")
95-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
95+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
9696
String domain,
9797
@PathParam("controlId") int controlId) {
9898
try {
@@ -116,7 +116,7 @@ public Response getRequirementVersions(
116116
@PermissionsAllowed(CalmHubScopes.DOMAIN_READ)
117117
public Response getRequirementForVersion(
118118
@PathParam("domain")
119-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
119+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
120120
String domain,
121121
@PathParam("controlId") int controlId,
122122
@PathParam("version")
@@ -147,7 +147,7 @@ public Response getRequirementForVersion(
147147
@PermissionsAllowed(CalmHubScopes.DOMAIN_WRITE)
148148
public Response createRequirementForVersion(
149149
@PathParam("domain")
150-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
150+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
151151
String domain,
152152
@PathParam("controlId") int controlId,
153153
@PathParam("version")
@@ -182,7 +182,7 @@ public Response createRequirementForVersion(
182182
@PermissionsAllowed(CalmHubScopes.DOMAIN_READ)
183183
public Response getConfigurationsForControl(
184184
@PathParam("domain")
185-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
185+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
186186
String domain,
187187
@PathParam("controlId") int controlId) {
188188
try {
@@ -207,7 +207,7 @@ public Response getConfigurationsForControl(
207207
@PermissionsAllowed(CalmHubScopes.DOMAIN_WRITE)
208208
public Response createControlConfiguration(
209209
@PathParam("domain")
210-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
210+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
211211
String domain,
212212
@PathParam("controlId") int controlId,
213213
@Valid @NotNull(message = "Request must not be null") CreateControlConfiguration createControlConfiguration) {
@@ -233,7 +233,7 @@ public Response createControlConfiguration(
233233
@PermissionsAllowed(CalmHubScopes.DOMAIN_READ)
234234
public Response getConfigurationVersions(
235235
@PathParam("domain")
236-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
236+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
237237
String domain,
238238
@PathParam("controlId") int controlId,
239239
@PathParam("configId") int configId) {
@@ -261,7 +261,7 @@ public Response getConfigurationVersions(
261261
@PermissionsAllowed(CalmHubScopes.DOMAIN_READ)
262262
public Response getConfigurationForVersion(
263263
@PathParam("domain")
264-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
264+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
265265
String domain,
266266
@PathParam("controlId") int controlId,
267267
@PathParam("configId") int configId,
@@ -296,7 +296,7 @@ public Response getConfigurationForVersion(
296296
@PermissionsAllowed(CalmHubScopes.DOMAIN_WRITE)
297297
public Response createConfigurationForVersion(
298298
@PathParam("domain")
299-
@Pattern(regexp = DOMAIN_NAME_REGEX, message = DOMAIN_NAME_MESSAGE)
299+
@Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE)
300300
String domain,
301301
@PathParam("controlId") int controlId,
302302
@PathParam("configId") int configId,

calm-hub/src/main/java/org/finos/calm/resources/DomainUserAccessResource.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.finos.calm.resources;
22

33
import io.quarkus.security.PermissionsAllowed;
4+
import jakarta.validation.Valid;
5+
import jakarta.validation.constraints.NotNull;
6+
import jakarta.validation.constraints.Pattern;
47
import jakarta.ws.rs.*;
58
import jakarta.ws.rs.core.MediaType;
69
import jakarta.ws.rs.core.Response;
@@ -16,6 +19,9 @@
1619
import java.net.URISyntaxException;
1720
import java.time.LocalDateTime;
1821

22+
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_MESSAGE;
23+
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_REGEX;
24+
1925
@Path("/calm/domains")
2026
public class DomainUserAccessResource {
2127

@@ -35,8 +41,8 @@ public DomainUserAccessResource(UserAccessStore userAccessStore) {
3541
description = "Creates a user-access grant for a given domain"
3642
)
3743
@PermissionsAllowed(CalmHubScopes.GLOBAL_ADMIN)
38-
public Response createUserAccessForDomain(@PathParam("domain") String domain,
39-
UserAccess createUserAccessRequest) {
44+
public Response createUserAccessForDomain(@PathParam("domain") @Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE) String domain,
45+
@Valid @NotNull UserAccess createUserAccessRequest) {
4046

4147
createUserAccessRequest.setCreationDateTime(LocalDateTime.now());
4248
createUserAccessRequest.setUpdateDateTime(LocalDateTime.now());
@@ -63,7 +69,7 @@ public Response createUserAccessForDomain(@PathParam("domain") String domain,
6369
description = "Get user-access details for a given domain"
6470
)
6571
@PermissionsAllowed(CalmHubScopes.GLOBAL_ADMIN)
66-
public Response getUserAccessForDomain(@PathParam("domain") String domain) {
72+
public Response getUserAccessForDomain(@PathParam("domain") @Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE) String domain) {
6773
try {
6874
return Response.ok(store.getUserAccessForDomain(domain)).build();
6975
} catch (UserAccessNotFoundException ex) {
@@ -82,8 +88,8 @@ public Response getUserAccessForDomain(@PathParam("domain") String domain) {
8288
description = "Get user-access details for a given domain and Id"
8389
)
8490
@PermissionsAllowed(CalmHubScopes.GLOBAL_ADMIN)
85-
public Response getUserAccessForDomainAndId(@PathParam("domain") String domain,
86-
@PathParam("userAccessId") Integer userAccessId) {
91+
public Response getUserAccessForDomainAndId(@PathParam("domain") @Pattern(regexp = DOMAIN_REGEX, message = DOMAIN_MESSAGE) String domain,
92+
@PathParam("userAccessId") @NotNull Integer userAccessId) {
8793
try {
8894
return Response.ok(store.getUserAccessForDomainAndId(domain, userAccessId)).build();
8995
} catch (UserAccessNotFoundException ex) {

calm-hub/src/main/java/org/finos/calm/resources/ResourceValidationConstants.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
public class ResourceValidationConstants {
77
public static final String NAMESPACE_REGEX = "^[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*$";
88
public static final String NAMESPACE_MESSAGE = "namespace must match pattern '^[A-Za-z0-9-]+([.][A-Za-z0-9-]+)*$'";
9-
public static final String DOMAIN_NAME_REGEX = "^[A-Za-z0-9-]+$";
10-
public static final String DOMAIN_NAME_MESSAGE = "domain name must match pattern '^[A-Za-z0-9-]+$'";
9+
// dashes, dots, usernames, upper/lowercase letters and numbers.
10+
public static final String USERNAME_REGEX = "^[A-Za-z0-9._-]+$";
11+
public static final String USERNAME_MESSAGE = "username must match pattern '^[A-Za-z0-9._-]+$'";
12+
public static final String DOMAIN_REGEX = "^[A-Za-z0-9-]+$";
13+
public static final String DOMAIN_MESSAGE = "domain name must match pattern '^[A-Za-z0-9-]+$'";
1114
public static final String VERSION_REGEX = "^(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)$";
1215
public static final String VERSION_MESSAGE = "version must match pattern '^(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)$'";
1316
// First character must be a letter so slugs are never purely numeric (avoids clash with legacy numeric IDs).

calm-hub/src/main/java/org/finos/calm/resources/UserAccessResource.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package org.finos.calm.resources;
22

33
import io.quarkus.security.PermissionsAllowed;
4+
import jakarta.annotation.Nonnull;
5+
import jakarta.validation.Valid;
6+
import jakarta.validation.constraints.NotNull;
7+
import jakarta.validation.constraints.Pattern;
48
import jakarta.ws.rs.*;
59
import jakarta.ws.rs.core.MediaType;
610
import jakarta.ws.rs.core.Response;
@@ -17,6 +21,9 @@
1721
import java.net.URISyntaxException;
1822
import java.time.LocalDateTime;
1923

24+
import static org.finos.calm.resources.ResourceValidationConstants.NAMESPACE_MESSAGE;
25+
import static org.finos.calm.resources.ResourceValidationConstants.NAMESPACE_REGEX;
26+
2027
@Path("/calm/namespaces")
2128
public class UserAccessResource {
2229

@@ -36,8 +43,8 @@ public UserAccessResource(UserAccessStore userAccessStore) {
3643
description = "Creates a user-access for a given namespace on a particular resource type"
3744
)
3845
@PermissionsAllowed(CalmHubScopes.ADMIN)
39-
public Response createUserAccessForNamespace(@PathParam("namespace") String namespace,
40-
UserAccess createUserAccessRequest) {
46+
public Response createUserAccessForNamespace(@PathParam("namespace") @Pattern(regexp = NAMESPACE_REGEX, message = NAMESPACE_MESSAGE) String namespace,
47+
@Valid @NotNull UserAccess createUserAccessRequest) {
4148

4249
createUserAccessRequest.setCreationDateTime(LocalDateTime.now());
4350
createUserAccessRequest.setUpdateDateTime(LocalDateTime.now());
@@ -66,7 +73,7 @@ public Response createUserAccessForNamespace(@PathParam("namespace") String name
6673
description = "Get user-access details for a given namespace"
6774
)
6875
@PermissionsAllowed(CalmHubScopes.ADMIN)
69-
public Response getUserAccessForNamespace(@PathParam("namespace") String namespace) {
76+
public Response getUserAccessForNamespace(@PathParam("namespace") @Pattern(regexp = NAMESPACE_REGEX, message = NAMESPACE_MESSAGE) String namespace) {
7077

7178
try {
7279
return Response.ok(store.getUserAccessForNamespace(namespace))
@@ -90,8 +97,8 @@ public Response getUserAccessForNamespace(@PathParam("namespace") String namespa
9097
description = "Get user-access details for a given namespace and Id"
9198
)
9299
@PermissionsAllowed(CalmHubScopes.ADMIN)
93-
public Response getUserAccessForNamespaceAndId(@PathParam("namespace") String namespace,
94-
@PathParam("userAccessId") Integer userAccessId) {
100+
public Response getUserAccessForNamespaceAndId(@PathParam("namespace") @Pattern(regexp = NAMESPACE_REGEX, message = NAMESPACE_MESSAGE) String namespace,
101+
@PathParam("userAccessId") @NotNull Integer userAccessId) {
95102

96103
try {
97104
return Response.ok(store.getUserAccessForNamespaceAndId(namespace, userAccessId))

calm-hub/src/test/java/org/finos/calm/resources/TestControlResourceShould.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.util.stream.Stream;
2121

2222
import static io.restassured.RestAssured.given;
23-
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_NAME_MESSAGE;
23+
import static org.finos.calm.resources.ResourceValidationConstants.DOMAIN_MESSAGE;
2424
import static org.finos.calm.resources.ResourceValidationConstants.VERSION_MESSAGE;
2525
import static org.hamcrest.Matchers.*;
2626
import static org.mockito.ArgumentMatchers.any;
@@ -57,7 +57,7 @@ void return_404_when_domain_does_not_exist_on_get() {
5757
.get("/calm/domains/invalid_domain/controls")
5858
.then()
5959
.statusCode(400)
60-
.body(containsString(DOMAIN_NAME_MESSAGE));
60+
.body(containsString(DOMAIN_MESSAGE));
6161
}
6262

6363
@Test void return_a_list_of_control_details_for_a_domain() {
@@ -134,7 +134,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_create_control
134134
.post("/calm/domains/invalid_domain/controls")
135135
.then()
136136
.statusCode(400)
137-
.body(containsString(DOMAIN_NAME_MESSAGE));
137+
.body(containsString(DOMAIN_MESSAGE));
138138
}
139139
// --- Requirement Version Endpoints ---
140140

@@ -145,7 +145,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_get_requiremen
145145
.get("/calm/domains/invalid_domain/controls/1/requirement/versions")
146146
.then()
147147
.statusCode(400)
148-
.body(containsString(DOMAIN_NAME_MESSAGE));
148+
.body(containsString(DOMAIN_MESSAGE));
149149
}
150150

151151
static Stream<Arguments> provideParametersForRequirementVersionTests() {
@@ -193,7 +193,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_get_requiremen
193193
.get("/calm/domains/invalid_domain/controls/1/requirement/versions/1.0.0")
194194
.then()
195195
.statusCode(400)
196-
.body(containsString(DOMAIN_NAME_MESSAGE));
196+
.body(containsString(DOMAIN_MESSAGE));
197197
}
198198

199199
@Test
@@ -251,7 +251,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_get_configurat
251251
.get("/calm/domains/invalid_domain/controls/1/configurations")
252252
.then()
253253
.statusCode(400)
254-
.body(containsString(DOMAIN_NAME_MESSAGE));
254+
.body(containsString(DOMAIN_MESSAGE));
255255
}
256256

257257
static Stream<Arguments> provideParametersForGetConfigurationsTests() {
@@ -314,7 +314,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_get_configurat
314314
.get("/calm/domains/invalid_domain/controls/1/configurations/10/versions")
315315
.then()
316316
.statusCode(400)
317-
.body(containsString(DOMAIN_NAME_MESSAGE));
317+
.body(containsString(DOMAIN_MESSAGE));
318318
}
319319

320320
static Stream<Arguments> provideParametersForGetConfigurationVersionsTests() {
@@ -364,7 +364,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_get_configurat
364364
.get("/calm/domains/invalid_domain/controls/1/configurations/10/versions/1.0.0")
365365
.then()
366366
.statusCode(400)
367-
.body(containsString(DOMAIN_NAME_MESSAGE));
367+
.body(containsString(DOMAIN_MESSAGE));
368368
}
369369

370370
@Test
@@ -425,7 +425,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_create_require
425425
.post("/calm/domains/invalid_domain/controls/1/requirement/versions/2.0.0")
426426
.then()
427427
.statusCode(400)
428-
.body(containsString(DOMAIN_NAME_MESSAGE));
428+
.body(containsString(DOMAIN_MESSAGE));
429429
}
430430

431431
@Test
@@ -537,7 +537,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_create_configu
537537
.post("/calm/domains/invalid_domain/controls/1/configurations")
538538
.then()
539539
.statusCode(400)
540-
.body(containsString(DOMAIN_NAME_MESSAGE));
540+
.body(containsString(DOMAIN_MESSAGE));
541541
}
542542

543543
// --- createConfigurationForVersion ---
@@ -551,7 +551,7 @@ void return_a_400_when_an_invalid_format_of_domain_is_provided_on_create_configu
551551
.post("/calm/domains/invalid_domain/controls/1/configurations/10/versions/2.0.0")
552552
.then()
553553
.statusCode(400)
554-
.body(containsString(DOMAIN_NAME_MESSAGE));
554+
.body(containsString(DOMAIN_MESSAGE));
555555
}
556556

557557
@Test

0 commit comments

Comments
 (0)