diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/AuiFeature.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/AuiFeature.java index 519658102e9..c845256334d 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/AuiFeature.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/AuiFeature.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Objects; -@DataEntry(sortBy = { "auiFeatureId" }) +@DataEntry(sortBy = {"auiFeatureId"}) @ObjectClass(value = "auiFeatures") public class AuiFeature extends Entry implements Serializable { @@ -53,7 +53,7 @@ public List getWebhookIdsMapped() { } public void setWebhookIdsMapped(List webhookIdsMapped) { - if(webhookIdsMapped != null) { + if (webhookIdsMapped != null) { this.webhookIdsMapped = Lists.newArrayList(Sets.newHashSet(webhookIdsMapped)); } } @@ -64,7 +64,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; AuiFeature that = (AuiFeature) o; - return auiFeatureId.equals(that.auiFeatureId) && displayName.equals(that.displayName) && jansScope.equals(that.jansScope) && Objects.equals(webhookIdsMapped, that.webhookIdsMapped); + return auiFeatureId.equals(that.auiFeatureId) && displayName.equals(that.displayName) && jansScope.equals(that.jansScope); } @Override diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/ShortCodeRequest.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/ShortCodeRequest.java new file mode 100644 index 00000000000..21278d1c817 --- /dev/null +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/ShortCodeRequest.java @@ -0,0 +1,36 @@ +package io.jans.ca.plugin.adminui.model.webhook; + +import io.jans.orm.annotation.JsonObject; + +import java.io.Serializable; +import java.util.Map; + +public class ShortCodeRequest implements Serializable { + private String webhookId; + @JsonObject + transient Map shortcodeValueMap; + + public String getWebhookId() { + return webhookId; + } + + public void setWebhookId(String webhookId) { + this.webhookId = webhookId; + } + + public Map getShortcodeValueMap() { + return shortcodeValueMap; + } + + public void setShortcodeValueMap(Map shortcodeValueMap) { + this.shortcodeValueMap = shortcodeValueMap; + } + + @Override + public String toString() { + return "ShortCodeRequest{" + + "webhookId='" + webhookId + '\'' + + ", shortcodeValueMap=" + shortcodeValueMap + + '}'; + } +} diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/WebhookEntry.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/WebhookEntry.java index e03a5466d96..bd2dbc4dc59 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/WebhookEntry.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/model/webhook/WebhookEntry.java @@ -37,7 +37,7 @@ public class WebhookEntry extends Entry implements Serializable { private String url; @AttributeName(name = "httpRequestBody") @JsonObject - private Map httpRequestBody; + private transient Map httpRequestBody; @NotNull @AttributeName(name = "httpMethod") private String httpMethod; @@ -125,14 +125,14 @@ public void setDescription(String description) { this.description = description; } - public Map getHttpRequestBody() { + public Map getHttpRequestBody() { if (httpRequestBody == null) { httpRequestBody = new HashMap<>(); } return httpRequestBody; } - public void setHttpRequestBody(Map httpRequestBody) { + public void setHttpRequestBody(Map httpRequestBody) { this.httpRequestBody = httpRequestBody; } diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/rest/webhook/WebhookResource.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/rest/webhook/WebhookResource.java index 426ad57f581..0be81479684 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/rest/webhook/WebhookResource.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/rest/webhook/WebhookResource.java @@ -4,6 +4,7 @@ import io.jans.ca.plugin.adminui.model.auth.GenericResponse; import io.jans.ca.plugin.adminui.model.exception.ApplicationException; import io.jans.ca.plugin.adminui.model.webhook.AuiFeature; +import io.jans.ca.plugin.adminui.model.webhook.ShortCodeRequest; import io.jans.ca.plugin.adminui.model.webhook.WebhookEntry; import io.jans.ca.plugin.adminui.service.webhook.WebhookService; import io.jans.ca.plugin.adminui.utils.AppConstants; @@ -29,6 +30,7 @@ import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.apache.commons.collections.CollectionUtils; import org.python.google.common.collect.Sets; import org.slf4j.Logger; @@ -311,17 +313,19 @@ public Response deleteWebhook(@Parameter(description = "Webhook identifier") @Pa @Operation(summary = "Trigger webhooks mapped to featureId", description = "Trigger webhooks mapped to featureId", operationId = "trigger-webhook", tags = { "Admin UI - Webhooks"}, security = @SecurityRequirement(name = "oauth2", scopes = {SCOPE_WEBHOOK_READ})) + @RequestBody(description = "Webhook object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ShortCodeRequest.class), examples = @ExampleObject(name = "Request json example", value = "example/webhook/trigger-webooks-request.json"))) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AuiFeature.class), examples = @ExampleObject(name = "Response json example", value = "example/webhook/trigger-webooks.json"))), + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AuiFeature.class), examples = @ExampleObject(name = "Response json example", value = "example/webhook/trigger-webooks-response.json"))), @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = GenericResponse.class, description = "License response"))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "Not Found"), @ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = GenericResponse.class, description = "License response")))}) - @GET + @POST @Path(TRIGGER_PATH + FEATURE_ID_PATH_VARIABLE) @ProtectedApi(scopes = {SCOPE_WEBHOOK_READ}) @Produces(MediaType.APPLICATION_JSON) - public Response triggerWebhook(@Parameter(description = "Admin UI feature identifier") @PathParam(AppConstants.ADMIN_UI_FEATURE_ID) @NotNull String featureId) { + public Response triggerWebhook(@Parameter(description = "Admin UI feature identifier") @PathParam(AppConstants.ADMIN_UI_FEATURE_ID) @NotNull String featureId, + @Valid @NotNull List shortCodes) { try { if (log.isDebugEnabled()) { log.debug("Triggering all webhooks for Admin UI feature - featureId: {}", escapeLog(featureId)); @@ -329,16 +333,16 @@ public Response triggerWebhook(@Parameter(description = "Admin UI feature identi HashSet featureIdSet = Sets.newHashSet(); featureIdSet.add(featureId); List featureList = webhookService.getAuiFeaturesByIds(featureIdSet); - if (CommonUtils.isEmptyOrNullCollection(featureList)) { + if (CollectionUtils.isEmpty(featureList)) { log.error(ErrorResponse.WEBHOOK_RECORD_NOT_EXIST.getDescription()); throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.WEBHOOK_RECORD_NOT_EXIST.getDescription()); } AuiFeature featureObj = featureList.get(0); - if (CommonUtils.isEmptyOrNullCollection(featureObj.getWebhookIdsMapped())) { + if (CollectionUtils.isEmpty(featureObj.getWebhookIdsMapped())) { log.error(ErrorResponse.NO_WEBHOOK_FOUND.getDescription()); throw new ApplicationException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ErrorResponse.NO_WEBHOOK_FOUND.getDescription()); } - List responseList = webhookService.triggerEnabledWebhooks(Sets.newHashSet(featureObj.getWebhookIdsMapped())); + List responseList = webhookService.triggerEnabledWebhooks(Sets.newHashSet(featureObj.getWebhookIdsMapped()), shortCodes); return Response.ok(responseList).build(); } catch (ApplicationException e) { diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/adminui/AdminUIService.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/adminui/AdminUIService.java index ad286f843b2..461a2bb1032 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/adminui/AdminUIService.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/adminui/AdminUIService.java @@ -92,7 +92,7 @@ public AdminRole getRoleObjByName(String role) throws ApplicationException { try { AdminConf adminConf = entryManager.find(AdminConf.class, AppConstants.ADMIN_UI_CONFIG_DN); List roles = adminConf.getDynamic().getRoles().stream().filter(ele -> ele.getRole().equals(role)).collect(Collectors.toList()); - if (!CommonUtils.isEmptyOrNullCollection(roles)) { + if (!CollectionUtils.isEmpty(roles)) { return roles.get(0); } log.error(ErrorResponse.ROLE_NOT_FOUND.getDescription()); @@ -208,7 +208,7 @@ public AdminPermission getPermissionObjByName(String permission) throws Applicat try { AdminConf adminConf = entryManager.find(AdminConf.class, AppConstants.ADMIN_UI_CONFIG_DN); List permissions = adminConf.getDynamic().getPermissions().stream().filter(ele -> ele.getPermission().equals(permission)).collect(Collectors.toList()); - if (!CommonUtils.isEmptyOrNullCollection(permissions)) { + if (!CollectionUtils.isEmpty(permissions)) { return permissions.get(0); } log.error(ErrorResponse.ROLE_NOT_FOUND.getDescription()); @@ -385,7 +385,7 @@ public RolePermissionMapping getAdminUIRolePermissionsMapping(String role) throw List roleScopeMappings = adminConf.getDynamic().getRolePermissionMapping() .stream().filter(ele -> ele.getRole().equalsIgnoreCase(role)) .collect(Collectors.toList()); - if (!CommonUtils.isEmptyOrNullCollection(roleScopeMappings)) { + if (!CollectionUtils.isEmpty(roleScopeMappings)) { return roleScopeMappings.get(0); } log.error(ErrorResponse.ROLE_PERMISSION_MAP_NOT_FOUND.getDescription()); diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookCallable.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookCallable.java index 46748945ebb..690845b929f 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookCallable.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookCallable.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import io.jans.ca.plugin.adminui.model.auth.GenericResponse; import io.jans.ca.plugin.adminui.model.exception.ApplicationException; @@ -10,10 +11,12 @@ import io.jans.ca.plugin.adminui.utils.AppConstants; import io.jans.ca.plugin.adminui.utils.ClientFactory; import io.jans.ca.plugin.adminui.utils.CommonUtils; +import io.jans.ca.plugin.adminui.utils.ErrorResponse; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.Invocation; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import java.util.HashMap; @@ -32,36 +35,52 @@ public WebhookCallable(WebhookEntry webhook, Logger log) { @Override public GenericResponse call() throws ApplicationException { - log.debug("Webhook processing started. Id: {}. Name: {}, URL : {}, HttpMethod: {}", webhook.getWebhookId(), webhook.getDisplayName(), webhook.getUrl(), webhook.getHttpMethod()); - Invocation.Builder request = ClientFactory.instance().getClientBuilder(webhook.getUrl()); - //getting all headers - webhook.getHttpHeaders().stream() - .filter(Objects::nonNull) - .forEach(header -> request.header(header.getKey(), header.getValue())); - //Call rest endpoint - Invocation invocation = checkHttpMethod(request); - if (invocation == null) { - log.error("Error in creating invocation object for rest call (Name: {}, Id: {})", webhook.getDisplayName(), webhook.getWebhookId()); - return CommonUtils.createGenericResponse(false, - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), - "Error in creating invocation object for rest call (Name: " + webhook.getDisplayName() + ", Id: " + webhook.getWebhookId() + ")"); - } - Response response = invocation.invoke(); ObjectMapper objectMapper = new ObjectMapper(); - log.debug("Webhook (Name: {}, Id: {}) response status code: {}", webhook.getDisplayName(), webhook.getWebhookId(), response.getStatus()); JsonNode jsonNode = objectMapper.createObjectNode(); - ((ObjectNode) jsonNode).put("webhookId", webhook.getWebhookId()); - ((ObjectNode) jsonNode).put("webhookName", webhook.getDisplayName()); - if (response.getStatus() == Response.Status.OK.getStatusCode() || - response.getStatus() == Response.Status.CREATED.getStatusCode() || - response.getStatus() == Response.Status.ACCEPTED.getStatusCode()) { - String responseData = response.readEntity(String.class); - log.debug("Webhook (Name: {}, Id: {}) responseData : {}", webhook.getDisplayName(), webhook.getWebhookId(), responseData); - return CommonUtils.createGenericResponse(true, response.getStatus(), responseData, jsonNode); - } else { - String responseData = response.readEntity(String.class); - log.error("Webhook (Name: {}, Id: {}) responseData : {}", webhook.getDisplayName(), webhook.getWebhookId(), responseData); - return CommonUtils.createGenericResponse(false, response.getStatus(), responseData, jsonNode); + try { + log.debug("Webhook processing started. Id: {}. Name: {}, URL : {}, HttpMethod: {}", webhook.getWebhookId(), webhook.getDisplayName(), webhook.getUrl(), webhook.getHttpMethod()); + Invocation.Builder request = ClientFactory.instance().getClientBuilder(webhook.getUrl()); + //getting all headers + webhook.getHttpHeaders().stream() + .filter(Objects::nonNull) + .forEach(header -> request.header(header.getKey(), header.getValue())); + //Call rest endpoint + Invocation invocation = checkHttpMethod(request); + if (invocation == null) { + log.error("Error in creating invocation object for rest call (Name: {}, Id: {})", webhook.getDisplayName(), webhook.getWebhookId()); + return CommonUtils.createGenericResponse(false, + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), + "Error in creating invocation object for rest call (Name: " + webhook.getDisplayName() + ", Id: " + webhook.getWebhookId() + ")"); + } + Response response = invocation.invoke(); + log.debug("Webhook (Name: {}, Id: {}) response status code: {}", webhook.getDisplayName(), webhook.getWebhookId(), response.getStatus()); + + ((ObjectNode) jsonNode).put("webhookId", webhook.getWebhookId()); + ((ObjectNode) jsonNode).put("webhookName", webhook.getDisplayName()); + ((ObjectNode) jsonNode).put("webhookMethod", webhook.getHttpMethod()); + if (Lists.newArrayList("POST", "PUT", "PATCH").contains(webhook.getHttpMethod())) { + ((ObjectNode) jsonNode).put("webhookRequestBody", webhook.getHttpRequestBody().toString()); + } + if (response.getStatus() == Response.Status.OK.getStatusCode() || + response.getStatus() == Response.Status.CREATED.getStatusCode() || + response.getStatus() == Response.Status.ACCEPTED.getStatusCode()) { + String responseData = response.readEntity(String.class); + log.debug("Webhook (Name: {}, Id: {}) responseData : {}", webhook.getDisplayName(), webhook.getWebhookId(), responseData); + return CommonUtils.createGenericResponse(true, response.getStatus(), responseData, jsonNode); + } else { + String responseData = response.readEntity(String.class); + log.error("Webhook (Name: {}, Id: {}) responseData : {}", webhook.getDisplayName(), webhook.getWebhookId(), responseData); + return CommonUtils.createGenericResponse(false, response.getStatus(), responseData, jsonNode); + } + } catch (Exception e) { + log.error(ErrorResponse.WEBHOOK_TRIGGER_ERROR.getDescription(), e); + ((ObjectNode) jsonNode).put("webhookId", webhook.getWebhookId()); + ((ObjectNode) jsonNode).put("webhookName", webhook.getDisplayName()); + ((ObjectNode) jsonNode).put("webhookMethod", webhook.getHttpMethod()); + if (Lists.newArrayList("POST", "PUT", "PATCH").contains(webhook.getHttpMethod())) { + ((ObjectNode) jsonNode).put("webhookRequestBody", webhook.getHttpRequestBody().toString()); + } + return CommonUtils.createGenericResponse(false, Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getMessage(), jsonNode); } } @@ -77,7 +96,7 @@ private Invocation checkHttpMethod(Invocation.Builder request) { case "POST": case "PUT": case "PATCH": - if (CommonUtils.isEmptyOrNullCollection(webhook.getHttpRequestBody())) { + if (MapUtils.isEmpty(webhook.getHttpRequestBody())) { break; } Map requestBody = setRequestBody(webhook); @@ -95,21 +114,16 @@ private Invocation checkHttpMethod(Invocation.Builder request) { private Map setRequestBody(WebhookEntry webhook) { try { Map body = new HashMap<>(); - webhook.getHttpHeaders().stream() - .filter(Objects::nonNull) - .forEach(header -> { - if (header.getKey().equalsIgnoreCase(AppConstants.CONTENT_TYPE) && header.getKey().equalsIgnoreCase(AppConstants.APPLICATION_JSON)) { - Map reqBody = webhook.getHttpRequestBody(); - for (String key : reqBody.keySet()) { - body.put(key, reqBody.get(key)); - } - } - }); + if (webhook.getHttpHeaders().stream().anyMatch(header -> header.getKey().equals(AppConstants.CONTENT_TYPE))) { + Map reqBody = webhook.getHttpRequestBody(); + for (String key : reqBody.keySet()) { + body.put(key, reqBody.get(key)); + } + } return body; } catch (Exception ex) { log.error("Error in parsing request-body.", ex); return Maps.newHashMap(); } } - } diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookService.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookService.java index bc54abd8e2f..bb70732b0ec 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookService.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/webhook/WebhookService.java @@ -6,6 +6,7 @@ import io.jans.ca.plugin.adminui.model.auth.GenericResponse; import io.jans.ca.plugin.adminui.model.exception.ApplicationException; import io.jans.ca.plugin.adminui.model.webhook.AuiFeature; +import io.jans.ca.plugin.adminui.model.webhook.ShortCodeRequest; import io.jans.ca.plugin.adminui.model.webhook.WebhookEntry; import io.jans.ca.plugin.adminui.utils.AppConstants; import io.jans.ca.plugin.adminui.utils.CommonUtils; @@ -22,7 +23,8 @@ import jakarta.ws.rs.core.Response; import org.python.google.common.collect.Sets; import org.slf4j.Logger; - +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import javax.validation.Valid; import java.util.*; import java.util.concurrent.*; @@ -41,6 +43,8 @@ public class WebhookService { @Inject ConfigurationFactory configurationFactory; + public static final String AUI_FEATURE_ID = "auiFeatureId"; + /** * The function retrieves all AuiFeature objects from the entryManager and returns them as a List. * @@ -48,7 +52,8 @@ public class WebhookService { */ public List getAllAuiFeatures() throws ApplicationException { try { - return entryManager.findEntries(AppConstants.ADMIN_UI_FEATURES_DN, AuiFeature.class, null); + final Filter filter = Filter.createPresenceFilter(AUI_FEATURE_ID); + return entryManager.findEntries(AppConstants.ADMIN_UI_FEATURES_DN, AuiFeature.class, filter); } catch (Exception e) { log.error(ErrorResponse.FETCH_DATA_ERROR.getDescription(), e); throw new ApplicationException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ErrorResponse.FETCH_DATA_ERROR.getDescription()); @@ -134,15 +139,15 @@ public List getWebhookByIds(Set ids) { public List getWebhooksByFeatureId(String featureId) { try { - Filter filter = Filter.createSubstringFilter("auiFeatureId", null, new String[]{featureId}, null); + Filter filter = Filter.createSubstringFilter(AUI_FEATURE_ID, null, new String[]{featureId}, null); List features = entryManager.findEntries(AppConstants.ADMIN_UI_FEATURES_DN, AuiFeature.class, filter); - if (CommonUtils.isEmptyOrNullCollection(features)) { + if (CollectionUtils.isEmpty(features)) { log.error(ErrorResponse.WEBHOOK_RECORD_NOT_EXIST.getDescription()); throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.WEBHOOK_RECORD_NOT_EXIST.getDescription()); } AuiFeature feature = features.get(0); List webhooksIds = feature.getWebhookIdsMapped(); - if (CommonUtils.isEmptyOrNullCollection(webhooksIds)) { + if (CollectionUtils.isEmpty(webhooksIds)) { log.error(ErrorResponse.NO_WEBHOOK_FOUND.getDescription()); throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.NO_WEBHOOK_FOUND.getDescription()); } @@ -165,7 +170,7 @@ public List getAuiFeaturesByIds(Set ids) { Filter searchFilter = null; List filters = new ArrayList<>(); for (String id : ids) { - Filter filter = Filter.createSubstringFilter("auiFeatureId", null, new String[]{id}, null); + Filter filter = Filter.createSubstringFilter(AUI_FEATURE_ID, null, new String[]{id}, null); filters.add(filter); } searchFilter = Filter.createORFilter(filters); @@ -307,7 +312,7 @@ public void validateWebhookEntry(WebhookEntry webhookEntry) throws ApplicationEx throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.WEBHOOK_HTTP_METHOD_EMPTY.getDescription()); } if (Lists.newArrayList("POST", "PUT", "PATCH").contains(webhookEntry.getHttpMethod())) { - if (CommonUtils.isEmptyOrNullCollection(webhookEntry.getHttpRequestBody())) { + if (MapUtils.isEmpty(webhookEntry.getHttpRequestBody())) { log.error(ErrorResponse.WEBHOOK_REQUEST_BODY_EMPTY.getDescription()); throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.WEBHOOK_REQUEST_BODY_EMPTY.getDescription()); } @@ -325,13 +330,15 @@ public void validateWebhookEntry(WebhookEntry webhookEntry) throws ApplicationEx * @param webhookIds A set of webhook IDs. * @return The method is returning a List of Strings. */ - public List triggerEnabledWebhooks(Set webhookIds) throws ApplicationException { + public List triggerEnabledWebhooks(Set webhookIds, List shortCodes) throws ApplicationException { ExecutorService executor = Executors.newFixedThreadPool(10); List responseList = new ArrayList<>(); List> callables = new ArrayList<>(); List webhooks = getWebhookByIds(webhookIds); for (WebhookEntry webhook : webhooks) { validateWebhookEntry(webhook); + ShortCodeRequest shortCodeObj = shortCodes.stream().filter(shortCode -> shortCode.getWebhookId().equals(webhook.getWebhookId())).findAny().orElse(null); + replaceShortCodeWithValues(webhook, shortCodeObj); if (webhook.isJansEnabled()) { Callable callable = new WebhookCallable(webhook, log); callables.add(callable); @@ -352,6 +359,19 @@ public List triggerEnabledWebhooks(Set webhookIds) thro return responseList; } + private void replaceShortCodeWithValues(WebhookEntry webhook, ShortCodeRequest shortCodeObj) { + if (shortCodeObj == null) { + return; + } + + if (CommonUtils.hasShortCode(webhook.getUrl())) { + webhook.setUrl(CommonUtils.replacePlaceholders(webhook.getUrl(), shortCodeObj.getShortcodeValueMap())); + } + + if (CommonUtils.hasShortCode(webhook.getHttpRequestBody()) && Lists.newArrayList("POST", "PUT", "PATCH").contains(webhook.getHttpMethod())) { + webhook.setHttpRequestBody(CommonUtils.replacePlaceholders(webhook.getHttpRequestBody(), shortCodeObj.getShortcodeValueMap())); + } + } private static String idFromName(String name) { return UUID.nameUUIDFromBytes(name.getBytes(UTF_8)).toString(); @@ -361,7 +381,6 @@ private static String dnOfWebhook(String id, String baseDn) { return String.format("webhookId=%s,%s", id, baseDn); } - public int getRecordMaxCount() { log.trace(" MaxCount details - ApiAppConfiguration.MaxCount():{}, DEFAULT_MAX_COUNT:{} ", configurationFactory.getApiAppConfiguration().getMaxCount(), ApiConstants.DEFAULT_MAX_COUNT); diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/CommonUtils.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/CommonUtils.java index cd1af720283..f16b41eb6cf 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/CommonUtils.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/CommonUtils.java @@ -17,8 +17,11 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Collection; +import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class CommonUtils { @Inject @@ -71,11 +74,71 @@ public static GenericResponse createGenericResponse(boolean result, int response return genericResponse; } - public static boolean isEmptyOrNullCollection(Collection collection) { - return (collection == null || collection.isEmpty()); + public static boolean hasShortCode(Map map) { + // Use regular expression to match placeholders in keys like "${placeholder}" + String pattern = "\\$\\{(\\w+)}"; + Pattern placeholderPattern = Pattern.compile(pattern); + + // Iterate through map keys and check for placeholders + for (Object value : map.values()) { + Matcher matcher = placeholderPattern.matcher(value.toString()); + if (matcher.find()) { + // Placeholder found in key + return true; + } + } + + // No placeholders found in any key + return false; + } + + public static boolean hasShortCode(String input) { + // Use regular expression to match placeholders like "${placeholder}" + String pattern = "\\$\\{(\\w+)}"; + Pattern placeholderPattern = Pattern.compile(pattern); + + // Create a Matcher and check for placeholders + Matcher matcher = placeholderPattern.matcher(input); + return matcher.find(); + } + + public static Map replacePlaceholders(Map map, Map placeholderValues) { + final Pattern placeholderPattern = Pattern.compile("\\$\\{(\\w+)\\}"); + Map replacedMap = new HashMap<>(); + + for (Map.Entry entry : map.entrySet()) { + String value = entry.getValue().toString(); + String replacedValue = value; + + Matcher matcher = placeholderPattern.matcher(value); + while (matcher.find()) { + String placeholderKey = matcher.group(1); + Object replacement = placeholderValues.get(placeholderKey); // Get replacement from placeholder values map + if (replacement != null) { + replacedValue = replacedValue.replace(matcher.group(0), replacement.toString()); + } + } + + replacedMap.put(entry.getKey(), replacedValue); + } + + return replacedMap; } - public static boolean isEmptyOrNullCollection(final Map m) { - return m == null || m.isEmpty(); + public static String replacePlaceholders(String url, Map placeholderValues) { + final Pattern placeholderPattern = Pattern.compile("\\$\\{(\\w+)\\}"); + Matcher matcher = placeholderPattern.matcher(url); + StringBuffer sb = new StringBuffer(); + + while (matcher.find()) { + String placeholderKey = matcher.group(1); + Object replacement = placeholderValues.get(placeholderKey); + if (replacement != null) { + matcher.appendReplacement(sb, replacement.toString()); // Efficient replacement using appendReplacement + } + } + + matcher.appendTail(sb); // Append remaining string + return sb.toString(); } } \ No newline at end of file diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/ErrorResponse.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/ErrorResponse.java index 957bbb0d0de..3a1ac6f28d2 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/ErrorResponse.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/utils/ErrorResponse.java @@ -1,13 +1,8 @@ package io.jans.ca.plugin.adminui.utils; public enum ErrorResponse { - GET_ACCESS_TOKEN_ERROR("Error in getting access token."), GET_API_PROTECTION_TOKEN_ERROR("Error in generating token to access Jans Config Api endpoints."), - GET_USER_INFO_ERROR("Error in getting User-Info."), - AUTHORIZATION_CODE_BLANK("Bad Request: Authourization `code` blank or empty."), USER_INFO_JWT_BLANK("User-Info jwt is blank or empty. Generating token with default scopes."), - CODE_OR_TOKEN_REQUIRED("Bad Request: Either `code` or `access_token` is required."), - CODE_VERIFIER_REQUIRED("Bad Request: `code_verifier` is required."), CHECK_LICENSE_ERROR("Error in checking license status. Check logs for further details."), ERROR_IN_LICENSE_CONFIGURATION_VALIDATION("Error in validating license configuration."), ACTIVATE_LICENSE_ERROR("Error in activating License. Check logs for further details."), @@ -55,6 +50,7 @@ public enum ErrorResponse { WEBHOOK_REQUEST_BODY_EMPTY("HTTP request-body for webhook is required for POST/PUT/PATCH request."), WEBHOOK_SAVE_ERROR("Error in saving webhook."), WEBHOOK_SEARCH_ERROR("Error in fetching webhook."), + WEBHOOK_TRIGGER_ERROR("Error in triggering webhook."), WEBHOOK_UPDATE_ERROR("Error in updating webhook."), WEBHOOK_ID_MISSING("Webhook Id is missing in request."), WEBHOOK_DELETE_ERROR("Error in removing webhook."), diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks-request.json b/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks-request.json new file mode 100644 index 00000000000..0edecf6f859 --- /dev/null +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks-request.json @@ -0,0 +1,4 @@ +[{ + "webhookId": "0a8e784b-2394-3a34-89d3-4a09d245aa32", + "shortcodeValueMap": {"price": 100} +}] \ No newline at end of file diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks-response.json b/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks-response.json new file mode 100644 index 00000000000..42f9271b15d --- /dev/null +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks-response.json @@ -0,0 +1,20 @@ +[{ + "success": true, + "responseMessage": "{\"id\":101,\"title\":\"iPhone 9\",\"price\":\"100\",\"description\":\"An apple mobile which is nothing like apple\"}", + "responseCode": 200, + "responseObject": { + "webhookId": "0a8e784b-2394-3a34-89d3-4a09d245aa32", + "webhookName": "Webhook1", + "webhookMethod": "POST", + "webhookRequestBody": "{dummy={a=b}, price=100, description=An apple mobile which is nothing like apple, id=1, title=iPhone 9}" + } +}, { + "success": true, + "responseMessage": "{\"users\":[{\"id\":1,\"firstName\":\"Terry\",\"lastName\":\"Medhurst\",\"maidenName\":\"Smitham\",\"age\":50,\"gender\":\"male\",\"email\":\"atuny0@sohu.com\",\"phone\":\"+63 791 675 8914\",\"username\":\"atuny0\",\"password\":\"9uQFF1Lh\",\"birthDate\":\"2000-12-25\",\"image\":\"https://robohash.org/Terry.png?set=set4\",\"bloodGroup\":\"A-\",\"height\":189,\"weight\":75.4,\"eyeColor\":\"Green\",\"hair\":{\"color\":\"Black\",\"type\":\"Strands\"},\"domain\":\"slashdot.org\",\"ip\":\"117.29.86.254\",\"address\":{\"address\":\"1745 T Street Southeast\",\"city\":\"Washington\",\"coordinates\":{\"lat\":38.867033,\"lng\":-76.979235},\"postalCode\":\"20020\",\"state\":\"DC\"},\"macAddress\":\"13:69:BA:56:A3:74\",\"university\":\"Capitol University\",\"bank\":{\"cardExpire\":\"06/22\",\"cardNumber\":\"50380955204220685\",\"cardType\":\"maestro\",\"currency\":\"Peso\",\"iban\":\"NO17 0695 2754 967\"},\"company\":{\"address\":{\"address\":\"629 Debbie Drive\",\"city\":\"Nashville\",\"coordinates\":{\"lat\":36.208114,\"lng\":-86.58621199999999},\"postalCode\":\"37076\",\"state\":\"TN\"},\"department\":\"Marketing\",\"name\":\"Blanda-O'Keefe\",\"title\":\"Help Desk Operator\"},\"ein\":\"20-9487066\",\"ssn\":\"661-64-2976\",\"userAgent\":\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/12.0.702.0 Safari/534.24\",\"crypto\":{\"coin\":\"Bitcoin\",\"wallet\":\"0xb9fc2fe63b2a6c003f1c324c3bfa53259162181a\",\"network\":\"Ethereum (ERC20)\"}},{\"id\":2,\"firstName\":\"Sheldon\",\"lastName\":\"Quigley\",\"maidenName\":\"Cole\",\"age\":28,\"gender\":\"male\",\"email\":\"hbingley1@plala.or.jp\",\"phone\":\"+7 813 117 7139\",\"username\":\"hbingley1\",\"password\":\"CQutx25i8r\",\"birthDate\":\"2003-08-02\",\"image\":\"https://robohash.org/Sheldon.png?set=set4\",\"bloodGroup\":\"O+\",\"height\":187,\"weight\":74,\"eyeColor\":\"Brown\",\"hair\":{\"color\":\"Blond\",\"type\":\"Curly\"},\"domain\":\"51.la\",\"ip\":\"253.240.20.181\",\"address\":{\"address\":\"6007 Applegate Lane\",\"city\":\"Louisville\",\"coordinates\":{\"lat\":38.1343013,\"lng\":-85.6498512},\"postalCode\":\"40219\",\"state\":\"KY\"}", + "responseCode": 200, + "responseObject": { + "webhookId": "bf21de5b-adb9-3581-82f5-76bb335f1cbd", + "webhookName": "Dummyuser-test", + "webhookMethod": "GET" + } +}] \ No newline at end of file diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks.json b/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks.json deleted file mode 100644 index 99c9ca96f01..00000000000 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/resources/example/webhook/trigger-webooks.json +++ /dev/null @@ -1,21 +0,0 @@ -[{ - "success": true, - "responseMessage": "{\n \"id\": 101\n}", - "responseCode": 201 -}, { - "success": true, - "responseMessage": "{\n \"id\": 102\n}", - "responseCode": 200 -}, { - "success": true, - "responseMessage": "{\n \"id\": 103\n}", - "responseCode": 200 -}, { - "success": true, - "responseMessage": "{\n \"id\": 104\n}", - "responseCode": 201 -}, { - "success": true, - "responseMessage": "{\n \"id\": 105\n}", - "responseCode": 201 -}] \ No newline at end of file diff --git a/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml b/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml index 6d94c9d251e..38d9a6ab1cc 100644 --- a/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml @@ -1412,7 +1412,7 @@ paths: - oauth2: - https://jans.io/oauth/jans-auth-server/config/adminui/webhook.readonly /admin-ui/webhook/trigger/{featureId}: - get: + post: tags: - Admin UI - Webhooks summary: Trigger webhooks mapped to featureId @@ -1425,6 +1425,20 @@ paths: required: true schema: type: string + requestBody: + description: Webhook object + content: + application/json: + schema: + $ref: '#/components/schemas/ShortCodeRequest' + examples: + Request json example: + description: Request json example + value: | + [{ + "webhookId": "0a8e784b-2394-3a34-89d3-4a09d245aa32", + "shortcodeValueMap": {"price": 100} + }] responses: "200": description: Ok @@ -1438,24 +1452,23 @@ paths: value: | [{ "success": true, - "responseMessage": "{\n \"id\": 101\n}", - "responseCode": 201 - }, { - "success": true, - "responseMessage": "{\n \"id\": 102\n}", - "responseCode": 200 + "responseMessage": "{\"id\":101,\"title\":\"iPhone 9\",\"price\":\"100\",\"description\":\"An apple mobile which is nothing like apple\"}", + "responseCode": 200, + "responseObject": { + "webhookId": "0a8e784b-2394-3a34-89d3-4a09d245aa32", + "webhookName": "Webhook1", + "webhookMethod": "POST", + "webhookRequestBody": "{dummy={a=b}, price=100, description=An apple mobile which is nothing like apple, id=1, title=iPhone 9}" + } }, { "success": true, - "responseMessage": "{\n \"id\": 103\n}", - "responseCode": 200 - }, { - "success": true, - "responseMessage": "{\n \"id\": 104\n}", - "responseCode": 201 - }, { - "success": true, - "responseMessage": "{\n \"id\": 105\n}", - "responseCode": 201 + "responseMessage": "{\"users\":[{\"id\":1,\"firstName\":\"Terry\",\"lastName\":\"Medhurst\",\"maidenName\":\"Smitham\",\"age\":50,\"gender\":\"male\",\"email\":\"atuny0@sohu.com\",\"phone\":\"+63 791 675 8914\",\"username\":\"atuny0\",\"password\":\"9uQFF1Lh\",\"birthDate\":\"2000-12-25\",\"image\":\"https://robohash.org/Terry.png?set=set4\",\"bloodGroup\":\"A-\",\"height\":189,\"weight\":75.4,\"eyeColor\":\"Green\",\"hair\":{\"color\":\"Black\",\"type\":\"Strands\"},\"domain\":\"slashdot.org\",\"ip\":\"117.29.86.254\",\"address\":{\"address\":\"1745 T Street Southeast\",\"city\":\"Washington\",\"coordinates\":{\"lat\":38.867033,\"lng\":-76.979235},\"postalCode\":\"20020\",\"state\":\"DC\"},\"macAddress\":\"13:69:BA:56:A3:74\",\"university\":\"Capitol University\",\"bank\":{\"cardExpire\":\"06/22\",\"cardNumber\":\"50380955204220685\",\"cardType\":\"maestro\",\"currency\":\"Peso\",\"iban\":\"NO17 0695 2754 967\"},\"company\":{\"address\":{\"address\":\"629 Debbie Drive\",\"city\":\"Nashville\",\"coordinates\":{\"lat\":36.208114,\"lng\":-86.58621199999999},\"postalCode\":\"37076\",\"state\":\"TN\"},\"department\":\"Marketing\",\"name\":\"Blanda-O'Keefe\",\"title\":\"Help Desk Operator\"},\"ein\":\"20-9487066\",\"ssn\":\"661-64-2976\",\"userAgent\":\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/12.0.702.0 Safari/534.24\",\"crypto\":{\"coin\":\"Bitcoin\",\"wallet\":\"0xb9fc2fe63b2a6c003f1c324c3bfa53259162181a\",\"network\":\"Ethereum (ERC20)\"}},{\"id\":2,\"firstName\":\"Sheldon\",\"lastName\":\"Quigley\",\"maidenName\":\"Cole\",\"age\":28,\"gender\":\"male\",\"email\":\"hbingley1@plala.or.jp\",\"phone\":\"+7 813 117 7139\",\"username\":\"hbingley1\",\"password\":\"CQutx25i8r\",\"birthDate\":\"2003-08-02\",\"image\":\"https://robohash.org/Sheldon.png?set=set4\",\"bloodGroup\":\"O+\",\"height\":187,\"weight\":74,\"eyeColor\":\"Brown\",\"hair\":{\"color\":\"Blond\",\"type\":\"Curly\"},\"domain\":\"51.la\",\"ip\":\"253.240.20.181\",\"address\":{\"address\":\"6007 Applegate Lane\",\"city\":\"Louisville\",\"coordinates\":{\"lat\":38.1343013,\"lng\":-85.6498512},\"postalCode\":\"40219\",\"state\":\"KY\"}", + "responseCode": 200, + "responseObject": { + "webhookId": "bf21de5b-adb9-3581-82f5-76bb335f1cbd", + "webhookName": "Dummyuser-test", + "webhookMethod": "GET" + } }] "400": description: Bad Request @@ -1635,7 +1648,7 @@ components: httpRequestBody: type: object additionalProperties: - type: string + type: object httpMethod: type: string jansEnabled: @@ -1759,6 +1772,15 @@ components: type: array items: type: object + ShortCodeRequest: + type: object + properties: + webhookId: + type: string + shortcodeValueMap: + type: object + additionalProperties: + type: object securitySchemes: oauth2: type: oauth2 diff --git a/jans-linux-setup/jans_setup/static/rdbm/sql_data_types.json b/jans-linux-setup/jans_setup/static/rdbm/sql_data_types.json index cdefd0d93ab..f10640f5305 100644 --- a/jans-linux-setup/jans_setup/static/rdbm/sql_data_types.json +++ b/jans-linux-setup/jans_setup/static/rdbm/sql_data_types.json @@ -1105,5 +1105,38 @@ "spanner": { "type": "ARRAY" } - } + }, + "webhookId": { + "mysql": { + "type": "TEXT" + }, + "pgsql": { + "type": "TEXT" + }, + "spanner": { + "type": "STRING(MAX)" + } + }, + "url": { + "mysql": { + "type": "TEXT" + }, + "pgsql": { + "type": "TEXT" + }, + "spanner": { + "type": "STRING(MAX)" + } + }, + "httpRequestBody": { + "mysql": { + "type": "TEXT" + }, + "pgsql": { + "type": "TEXT" + }, + "spanner": { + "type": "STRING(MAX)" + } + } } diff --git a/jans-linux-setup/jans_setup/templates/jans-auth/role-scope-mappings.json b/jans-linux-setup/jans_setup/templates/jans-auth/role-scope-mappings.json index 70f628ca37f..8448b075b8d 100644 --- a/jans-linux-setup/jans_setup/templates/jans-auth/role-scope-mappings.json +++ b/jans-linux-setup/jans_setup/templates/jans-auth/role-scope-mappings.json @@ -342,132 +342,150 @@ "defaultPermissionInToken": false, "tag": "ssa" }, - { + { "permission": "https://jans.io/oauth/config/organization.readonly", "description": "", "defaultPermissionInToken": false, "tag": "organization" }, - { + { "permission": "https://jans.io/oauth/config/organization.write", "description": "", "defaultPermissionInToken": false, "tag": "organization" }, - { + { "permission": "https://jans.io/oauth/config/user.readonly", "description": "", "defaultPermissionInToken": false, "tag": "user" }, - { + { "permission": "https://jans.io/oauth/config/user.write", "description": "", "defaultPermissionInToken": false, "tag": "user" }, - { + { "permission": "https://jans.io/oauth/config/user.delete", "description": "", "defaultPermissionInToken": false, "tag": "user" }, - { + { "permission": "https://jans.io/oauth/config/agama.readonly", "description": "", "defaultPermissionInToken": false, "tag": "agama" }, - { + { "permission": "https://jans.io/oauth/config/agama.write", "description": "", "defaultPermissionInToken": false, "tag": "agama" }, - { + { "permission": "https://jans.io/oauth/config/agama.delete", "description": "", "defaultPermissionInToken": false, "tag": "agama" }, - { + { "permission": "https://jans.io/oauth/jans-auth-server/session.readonly", "description": "", "defaultPermissionInToken": false, "tag": "session" }, - { + { "permission": "https://jans.io/oauth/jans-auth-server/session.delete", "description": "", "defaultPermissionInToken": false, "tag": "session" }, - { + { "permission": "https://jans.io/oauth/config/plugin.readonly", "description": "", "defaultPermissionInToken": false, "tag": "plugin" }, - { + { "permission": "https://jans.io/oauth/config/properties.readonly", "description": "", "defaultPermissionInToken": false, "tag": "properties" }, - { + { "permission": "https://jans.io/oauth/config/properties.write", "description": "", "defaultPermissionInToken": false, "tag": "properties" }, - { + { "permission": "https://jans.io/oauth/client/authorizations.readonly", "description": "", "defaultPermissionInToken": false, "tag": "authorizations" }, - { + { "permission": "https://jans.io/oauth/client/authorizations.delete", "description": "", "defaultPermissionInToken": false, "tag": "authorizations" }, - { + { "permission":"https://jans.io/oauth/config/jans-link.readonly", "description": "", "defaultPermissionInToken": false, "tag": "jans-link" }, - { + { "permission": "https://jans.io/oauth/config/jans-link.write", "description": "", "defaultPermissionInToken": false, "tag": "jans-link" }, - { + { "permission": "https://jans.io/oauth/config/saml-config.readonly", "description": "", "defaultPermissionInToken": false, "tag": "saml-config" }, - { + { "permission": "https://jans.io/oauth/config/saml-config.write", "description": "", "defaultPermissionInToken": false, "tag": "saml-config" }, - { + { "permission": "https://jans.io/oauth/config/saml-scope.readonly", "description": "", "defaultPermissionInToken": false, "tag": "saml-scope" }, - { + { "permission": "https://jans.io/oauth/config/saml-scope.write", "description": "", "defaultPermissionInToken": false, "tag": "saml-scope" }, + { + "permission": "https://jans.io/oauth/jans-auth-server/config/adminui/webhook.readonly", + "description": "", + "defaultPermissionInToken": false, + "tag": "webhook" + }, + { + "permission": "https://jans.io/oauth/jans-auth-server/config/adminui/webhook.write", + "description": "", + "defaultPermissionInToken": false, + "tag": "webhook" + }, + { + "permission": "https://jans.io/oauth/jans-auth-server/config/adminui/webhook.delete", + "description": "", + "defaultPermissionInToken": false, + "tag": "webhook" + }, { "permission": "https://jans.io/oauth/jans-auth-server/config/adminui/properties.readonly", "description": "", @@ -657,6 +675,9 @@ "https://jans.io/auth/ssa.admin", "https://jans.io/auth/ssa.portal", "https://jans.io/auth/ssa.developer", + "https://jans.io/oauth/jans-auth-server/config/adminui/webhook.readonly", + "https://jans.io/oauth/jans-auth-server/config/adminui/webhook.write", + "https://jans.io/oauth/jans-auth-server/config/adminui/webhook.delete", "https://jans.io/oauth/jans-auth-server/config/adminui/properties.readonly", "https://jans.io/oauth/jans-auth-server/config/adminui/properties.write" ]