Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<!-- check micrometer.version vertx-micrometer-metrics consumes before bumping up -->
<micrometer.version>1.12.2</micrometer.version>
<junit-jupiter.version>5.11.2</junit-jupiter.version>
<uid2-shared.version>10.7.14</uid2-shared.version>
<uid2-shared.version>10.8.0</uid2-shared.version>
<okta-jwt.version>0.5.10</okta-jwt.version>
<image.version>${project.version}</image.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ private void handleServiceLinkUpdate(RoutingContext rc) {
Integer siteId = body.getInteger("site_id");
String name = body.getString("name");
JsonArray rolesJson = body.getJsonArray("roles");
Boolean disabled = body.getBoolean("disabled");

if (siteId == null || serviceId == null || linkId == null || linkId.isEmpty()) {
ResponseUtil.error(rc, 400, "required parameters: site_id, service_id, link_id");
Expand Down Expand Up @@ -195,6 +196,10 @@ private void handleServiceLinkUpdate(RoutingContext rc) {
serviceLink.setName(name);
}

if (disabled != null) {
serviceLink.setDisabled(disabled);
}

if (rolesJson != null && !rolesJson.isEmpty()) {
final Set<Role> roles;
try {
Expand Down Expand Up @@ -259,6 +264,7 @@ private JsonObject toJson(ServiceLink s) {
jsonObject.put("site_id", s.getSiteId());
jsonObject.put("name", s.getName());
jsonObject.put("roles", s.getRoles());
jsonObject.put("disabled", s.isDisabled());
return jsonObject;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ private void handleUpdate(RoutingContext rc) {
Integer siteId = body.getInteger("site_id");
String name = body.getString("name");
String linkIdRegex = body.getString("link_id_regex");
Boolean disabled = body.getBoolean("disabled");

JsonArray rolesSpec = null;
if (body.getString("roles") != null && !body.getString("roles").isEmpty()) {
Expand Down Expand Up @@ -242,6 +243,10 @@ private void handleUpdate(RoutingContext rc) {
service.setLinkIdRegex(linkIdRegex);
}

if (disabled != null) {
service.setDisabled(disabled);
}

if (siteId != null && siteId != 0) {
service.setSiteId(siteId);
}
Expand Down Expand Up @@ -295,6 +300,7 @@ private JsonObject toJson(Service s) {
jsonObject.put("name", s.getName());
jsonObject.put("roles", s.getRoles());
jsonObject.put("link_id_regex", s.getLinkIdRegex());
jsonObject.put("disabled", s.isDisabled());
return jsonObject;
}

Expand Down
86 changes: 72 additions & 14 deletions src/test/java/com/uid2/admin/vertx/ServiceLinkServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.vertx.junit5.VertxTestContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

Expand Down Expand Up @@ -387,6 +388,34 @@ void updateServiceLink_updateNameOnly_succeeds(Vertx vertx, VertxTestContext tes
});
}

@Test
void updateServiceLink_updateDisabledOnly_succeeds(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.MAINTAINER);

setSites(new Site(123, "name1", false));
setServices(new Service(1, 123, "name1", Set.of(Role.MAINTAINER, Role.MAPPER)));
ServiceLink existingLink = new ServiceLink("link1", 1, 123, "name1", Set.of(Role.MAPPER));
setServiceLinks(existingLink);

JsonObject jo = new JsonObject();
jo.put("link_id", "link1");
jo.put("service_id", 1);
jo.put("site_id", 123);
jo.put("disabled", true);

ServiceLink expected = new ServiceLink("link1", 1, 123, "name1", Set.of(Role.MAPPER));
expected.setDisabled(true);

post(vertx, testContext, "api/service_link/update", jo.encode(), response -> {
assertEquals(200, response.statusCode());
checkServiceLinkJson(expected, response.bodyAsJsonObject());
verify(serviceStoreWriter, never()).upload(null, null);
verify(serviceLinkStoreWriter, times(1)).upload(List.of(expected), null);

testContext.completeNow();
});
}

@Test
void updateServiceLink_updateRoleOnly_succeeds(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.MAINTAINER);
Expand Down Expand Up @@ -442,6 +471,35 @@ void updateServiceLink_updateRoleAndName_succeeds(Vertx vertx, VertxTestContext
});
}

@Test
void updateServiceLink_updateRoleNameDisabled_succeeds(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.MAINTAINER);

setSites(new Site(123, "name1", false));
setServices(new Service(1, 123, "name1", Set.of(Role.MAINTAINER, Role.MAPPER, Role.SHARER)));
ServiceLink existingLink = new ServiceLink("link1", 1, 123, "name1", Set.of(Role.MAPPER));
setServiceLinks(existingLink);

JsonObject jo = new JsonObject();
jo.put("link_id", "link1");
jo.put("service_id", 1);
jo.put("site_id", 123);
jo.put("name", "newname");
jo.put("roles", JsonArray.of(Role.MAPPER, Role.SHARER));
jo.put("disabled", true);

ServiceLink expected = new ServiceLink("link1", 1, 123, "newname", Set.of(Role.MAPPER, Role.SHARER), true);

post(vertx, testContext, "api/service_link/update", jo.encode(), response -> {
assertEquals(200, response.statusCode());
checkServiceLinkJson(expected, response.bodyAsJsonObject());
verify(serviceStoreWriter, never()).upload(null, null);
verify(serviceLinkStoreWriter, times(1)).upload(List.of(expected), null);

testContext.completeNow();
});
}

@Test
void updateServiceLink_roleDoesNotExist_returnsError(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.MAINTAINER);
Expand Down Expand Up @@ -622,20 +680,20 @@ void addServiceLink_linkIdRegex_validation(String linkIdRegex, String linkId, bo

private static java.util.stream.Stream<org.junit.jupiter.params.provider.Arguments> linkIdRegexCases() {
return java.util.stream.Stream.of(
org.junit.jupiter.params.provider.Arguments.of("link[0-9]+", "invalidLink", false),
org.junit.jupiter.params.provider.Arguments.of("link[0-9]+", "link42", true),
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "XY12345", true), // snowflake valid
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "UID2_ENVIRONMENT", true), // snowflake valid
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "xy12345", false), // snowflake invalid, lowercase
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "X", true), // snowflake valid, minimum length
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "X".repeat(256), true), // snowflake valid, maximum length
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "X".repeat(257), false), // snowflake invalid, exceeds maximum length
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", " XY12345", false), // snowflake invalid, leading whitespace
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "XY12345 ", false), // snowflake invalid, trailing whitespace
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "XY 12345", false), // snowflake invalid, whitespace in the middle
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "", false), // snowflake invalid, empty
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", " ", false), // snowflake invalid, just whitespace
org.junit.jupiter.params.provider.Arguments.of("^[A-Z0-9_]{1,256}$", "XY_12345", true) // snowflake valid, used underscore
Arguments.of("link[0-9]+", "invalidLink", false),
Arguments.of("link[0-9]+", "link42", true),
Arguments.of("^[A-Z0-9_]{1,256}$", "XY12345", true), // snowflake valid
Arguments.of("^[A-Z0-9_]{1,256}$", "UID2_ENVIRONMENT", true), // snowflake valid
Arguments.of("^[A-Z0-9_]{1,256}$", "xy12345", false), // snowflake invalid, lowercase
Arguments.of("^[A-Z0-9_]{1,256}$", "X", true), // snowflake valid, minimum length
Arguments.of("^[A-Z0-9_]{1,256}$", "X".repeat(256), true), // snowflake valid, maximum length
Arguments.of("^[A-Z0-9_]{1,256}$", "X".repeat(257), false), // snowflake invalid, exceeds maximum length
Arguments.of("^[A-Z0-9_]{1,256}$", " XY12345", false), // snowflake invalid, leading whitespace
Arguments.of("^[A-Z0-9_]{1,256}$", "XY12345 ", false), // snowflake invalid, trailing whitespace
Arguments.of("^[A-Z0-9_]{1,256}$", "XY 12345", false), // snowflake invalid, whitespace in the middle
Arguments.of("^[A-Z0-9_]{1,256}$", "", false), // snowflake invalid, empty
Arguments.of("^[A-Z0-9_]{1,256}$", " ", false), // snowflake invalid, just whitespace
Arguments.of("^[A-Z0-9_]{1,256}$", "XY_12345", true) // snowflake valid, used underscore
);
}
}
46 changes: 46 additions & 0 deletions src/test/java/com/uid2/admin/vertx/ServiceServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,26 @@ void updateName(Vertx vertx, VertxTestContext testContext) {
});
}

@Test
void updateDisabled(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.PRIVILEGED);

Service existingService = new Service(1, 123, "name1", Set.of(Role.MAINTAINER));
setServices(existingService);

JsonObject jo = new JsonObject();
jo.put("service_id", 1);
jo.put("disabled", true);

post(vertx, testContext, "api/service/update", jo.encode(), response -> {
assertEquals(200, response.statusCode());
existingService.setDisabled(true);
checkServiceJson(existingService, response.bodyAsJsonObject());
verify(serviceStoreWriter, times(1)).upload(List.of(existingService), null);
testContext.completeNow();
});
}

@Test
void updateSiteId(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.PRIVILEGED);
Expand All @@ -600,6 +620,32 @@ void updateSiteId(Vertx vertx, VertxTestContext testContext) {
});
}

@Test
void updateNameDisabledSiteIdRoles(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.PRIVILEGED);

Service existingService = new Service(1, 123, "name1", Set.of(Role.MAINTAINER));
setServices(existingService);

JsonObject jo = new JsonObject();
jo.put("service_id", 1);
jo.put("site_id", 456);
jo.put("name", "newname");
jo.put("disabled", true);
jo.put("roles", JsonArray.of(Role.MAINTAINER, Role.MAPPER, Role.SHARER));

post(vertx, testContext, "api/service/update", jo.encode(), response -> {
assertEquals(200, response.statusCode());
existingService.setSiteId(456);
existingService.setName("newname");
existingService.setDisabled(true);
existingService.setRoles(Set.of(Role.MAINTAINER, Role.MAPPER, Role.SHARER));
checkServiceJson(existingService, response.bodyAsJsonObject());
verify(serviceStoreWriter, times(1)).upload(List.of(existingService), null);
testContext.completeNow();
});
}

@Test
void updateWithEmptyValues(Vertx vertx, VertxTestContext testContext) {
fakeAuth(Role.PRIVILEGED);
Expand Down
12 changes: 10 additions & 2 deletions webroot/adm/service-link.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ <h1>UID2 Env - Service Link Management</h1>
]
};

const disabledInput = {
name: 'disabled',
label: 'Disable Service Link',
type: 'checkbox'
};

const operationConfig = {
read: [
{
Expand Down Expand Up @@ -141,7 +147,8 @@ <h1>UID2 Env - Service Link Management</h1>
serviceIdInput,
siteIdInput,
linkNameInput,
rolesInput
rolesInput,
disabledInput,
],
apiCall: {
method: 'POST',
Expand All @@ -156,7 +163,8 @@ <h1>UID2 Env - Service Link Management</h1>
service_id: parseInt(inputs.serviceId),
site_id: parseInt(inputs.siteId),
name: inputs.linkName || '',
roles: rolesArray
roles: rolesArray,
disabled: typeof inputs.disabled === 'boolean' ? inputs.disabled : undefined,
};
}
}
Expand Down
10 changes: 9 additions & 1 deletion webroot/adm/service.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ <h1>UID2 Env - Service Management</h1>
label: 'Link Id Regex'
};

const disabledInput = {
name: 'disabled',
label: 'Disable Service',
type: 'checkbox'
};

const operationConfig = {
read: [
{
Expand Down Expand Up @@ -142,7 +148,8 @@ <h1>UID2 Env - Service Management</h1>
{...serviceNameInput, required: false},
{...siteIdInput, required: false},
{...rolesInput, required: false},
linkIdRegexInput
linkIdRegexInput,
disabledInput
],
apiCall: {
method: 'POST',
Expand All @@ -157,6 +164,7 @@ <h1>UID2 Env - Service Management</h1>
if (rolesArray.length > 0) payload.roles = rolesArray;

if (inputs.linkIdRegex) payload.link_id_regex = inputs.linkIdRegex;
if (typeof inputs.disabled === 'boolean') payload.disabled = inputs.disabled;
return payload;
}
}
Expand Down