From 2aa2ba86a2a639336079a1151ec38aca93ed9360 Mon Sep 17 00:00:00 2001 From: YuriyZ <91314855+yuriyzz@users.noreply.github.com> Date: Fri, 21 Jan 2022 13:28:29 +0200 Subject: [PATCH] feat(jans-auth-server): add methods to dynamic client registration script to modify POST, PUT and GET responses (#661) * feat(jans-core): added methods for register response modification * feat(jans-auth-server): added post response modification method * feat(jans-auth-server): added put response modification method * feat(jans-core): added read response modification method * feat(jans-auth-server): added read response modification method * feat(jans-auth-server): invoke custom script methods for response modification --- .../server/model/common/ExecutionContext.java | 3 +- .../ws/rs/RegisterRestWebServiceImpl.java | 43 ++++++++++-- ...ernalDynamicClientRegistrationService.java | 70 +++++++++++++++++++ .../type/client/ClientRegistrationType.java | 6 ++ .../client/DummyClientRegistrationType.java | 15 ++++ 5 files changed, 131 insertions(+), 6 deletions(-) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java index aa602436fde..bafcf750e52 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java @@ -94,8 +94,9 @@ public Client getClient() { return client; } - public void setClient(Client client) { + public ExecutionContext setClient(Client client) { this.client = client; + return this; } public void setHttpRequest(HttpServletRequest httpRequest) { diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java index f18d4c00a0b..86ed1bff2c1 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java @@ -43,6 +43,7 @@ import io.jans.as.server.model.common.AbstractToken; import io.jans.as.server.model.common.AuthorizationGrant; import io.jans.as.server.model.common.AuthorizationGrantList; +import io.jans.as.server.model.common.ExecutionContext; import io.jans.as.server.model.registration.RegisterParamsValidator; import io.jans.as.server.model.token.HandleTokenFactory; import io.jans.as.server.service.ClientService; @@ -374,7 +375,10 @@ private Response registerClientImpl(String requestParams, HttpServletRequest htt clientService.persist(client); JSONObject jsonObject = getJSONObject(client); - builder.entity(jsonObject.toString(4).replace("\\/", "/")); + + jsonObject = modifyPostScript(jsonObject, new ExecutionContext(httpRequest, null).setClient(client)); + + builder.entity(jsonObjectToString(jsonObject)); log.info("Client registered: clientId = {}, applicationType = {}, clientName = {}, redirectUris = {}, sectorIdentifierUri = {}", client.getClientId(), client.getApplicationType(), client.getClientName(), client.getRedirectUris(), client.getSectorIdentifierUri()); @@ -946,10 +950,16 @@ public Response requestClientUpdate(String requestParams, String clientId, @Head if (updateClient) { clientService.merge(client); + JSONObject jsonObject = getJSONObject(client); + jsonObject = modifyPutScript(jsonObject, new ExecutionContext(httpRequest, null).setClient(client)); + + final Response response = Response.ok().entity(jsonObjectToString(jsonObject)).build(); + oAuth2AuditLog.setScope(clientScopesToString(client)); oAuth2AuditLog.setSuccess(true); applicationAuditLogger.sendMessage(oAuth2AuditLog); - return Response.ok().entity(clientAsEntity(client)).build(); + + return response; } else { clientService.removeFromCache(client); // clear cache to force reload from persistence log.trace("The Access Token is not valid for the Client ID, returns invalid_token error, client_id: {}", clientId); @@ -1051,7 +1061,10 @@ public Response requestClientRead(String clientId, String authorization, HttpSer if (client != null) { oAuth2AuditLog.setScope(clientScopesToString(client)); oAuth2AuditLog.setSuccess(true); - builder.entity(clientAsEntity(client)); + + JSONObject jsonObject = getJSONObject(client); + jsonObject = modifyReadScript(jsonObject, new ExecutionContext(httpRequest, null).setClient(client)); + builder.entity(jsonObjectToString(jsonObject)); } else { log.trace("The Access Token is not valid for the Client ID, returns invalid_token error."); builder = Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).type(MediaType.APPLICATION_JSON_TYPE); @@ -1075,8 +1088,7 @@ public Response requestClientRead(String clientId, String authorization, HttpSer return builder.build(); } - private String clientAsEntity(Client client) throws JSONException, StringEncrypter.EncryptionException { - final JSONObject jsonObject = getJSONObject(client); + private static String jsonObjectToString(JSONObject jsonObject) throws JSONException { return jsonObject.toString(4).replace("\\/", "/"); } @@ -1318,4 +1330,25 @@ private void putCustomAttributesInResponse(Client client, JSONObject responseJso } } + private JSONObject modifyPostScript(JSONObject jsonObject, ExecutionContext executionContext) throws StringEncrypter.EncryptionException { + if (!externalDynamicClientRegistrationService.modifyPostResponse(jsonObject, executionContext)) { + return getJSONObject(executionContext.getClient()); // script forbids modification, re-create json object + } + return jsonObject; + } + + private JSONObject modifyPutScript(JSONObject jsonObject, ExecutionContext executionContext) throws StringEncrypter.EncryptionException { + if (!externalDynamicClientRegistrationService.modifyPutResponse(jsonObject, executionContext)) { + return getJSONObject(executionContext.getClient()); // script forbids modification, re-create json object + } + return jsonObject; + } + + private JSONObject modifyReadScript(JSONObject jsonObject, ExecutionContext executionContext) throws StringEncrypter.EncryptionException { + if (!externalDynamicClientRegistrationService.modifyReadResponse(jsonObject, executionContext)) { + return getJSONObject(executionContext.getClient()); // script forbids modification, re-create json object + } + return jsonObject; + } + } \ No newline at end of file diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalDynamicClientRegistrationService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalDynamicClientRegistrationService.java index 3e81856e116..0e4d1275e10 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalDynamicClientRegistrationService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalDynamicClientRegistrationService.java @@ -11,6 +11,7 @@ import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.jwt.Jwt; import io.jans.as.model.util.CertUtils; +import io.jans.as.server.model.common.ExecutionContext; import io.jans.as.server.service.external.context.DynamicClientRegistrationContext; import io.jans.model.custom.script.CustomScriptType; import io.jans.model.custom.script.conf.CustomScriptConfiguration; @@ -231,4 +232,73 @@ public boolean isCertValidForClient(X509Certificate cert, DynamicClientRegistrat return false; } } + + public boolean modifyPostResponse(JSONObject responseAsJsonObject, ExecutionContext context) { + CustomScriptConfiguration script = defaultExternalCustomScript; + + try { + if (log.isTraceEnabled()) { + log.trace("Executing python 'modifyPostResponse' method, script name: {}, context: {}, response: {}", script.getName(), context, responseAsJsonObject.toString()); + } + context.setScript(script); + + ClientRegistrationType type = (ClientRegistrationType) script.getExternalType(); + final boolean result = type.modifyPostResponse(responseAsJsonObject, context); + if (log.isTraceEnabled()) { + log.trace("Finished 'modifyPostResponse' method, script name: {}, context: {}, result: {}, response: {}", script.getName(), context, result, responseAsJsonObject.toString()); + } + + return result; + } catch (Exception ex) { + log.error(ex.getMessage(), ex); + saveScriptError(script.getCustomScript(), ex); + } + return false; + } + + public boolean modifyPutResponse(JSONObject responseAsJsonObject, ExecutionContext context) { + CustomScriptConfiguration script = defaultExternalCustomScript; + + try { + if (log.isTraceEnabled()) { + log.trace("Executing python 'modifyPutResponse' method, script name: {}, context: {}, response: {}", script.getName(), context, responseAsJsonObject.toString()); + } + context.setScript(script); + + ClientRegistrationType type = (ClientRegistrationType) script.getExternalType(); + final boolean result = type.modifyPutResponse(responseAsJsonObject, context); + if (log.isTraceEnabled()) { + log.trace("Finished 'modifyPutResponse' method, script name: {}, context: {}, result: {}, response: {}", script.getName(), context, result, responseAsJsonObject.toString()); + } + + return result; + } catch (Exception ex) { + log.error(ex.getMessage(), ex); + saveScriptError(script.getCustomScript(), ex); + } + return false; + } + + public boolean modifyReadResponse(JSONObject responseAsJsonObject, ExecutionContext context) { + CustomScriptConfiguration script = defaultExternalCustomScript; + + try { + if (log.isTraceEnabled()) { + log.trace("Executing python 'modifyReadResponse' method, script name: {}, context: {}, response: {}", script.getName(), context, responseAsJsonObject.toString()); + } + context.setScript(script); + + ClientRegistrationType type = (ClientRegistrationType) script.getExternalType(); + final boolean result = type.modifyReadResponse(responseAsJsonObject, context); + if (log.isTraceEnabled()) { + log.trace("Finished 'modifyReadResponse' method, script name: {}, context: {}, result: {}, response: {}", script.getName(), context, result, responseAsJsonObject.toString()); + } + + return result; + } catch (Exception ex) { + log.error(ex.getMessage(), ex); + saveScriptError(script.getCustomScript(), ex); + } + return false; + } } diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/ClientRegistrationType.java b/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/ClientRegistrationType.java index 9dd3a2ab64b..31cd07cd481 100644 --- a/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/ClientRegistrationType.java +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/ClientRegistrationType.java @@ -35,4 +35,10 @@ public interface ClientRegistrationType extends BaseExternalType { // context - io.jans.as.server.service.external.context.DynamicClientRegistrationContext // cert - java.security.cert.X509Certificate boolean isCertValidForClient(Object cert, Object context); + + boolean modifyPutResponse(Object responseAsJsonObject, Object executionContext); + + boolean modifyReadResponse(Object responseAsJsonObject, Object executionContext); + + boolean modifyPostResponse(Object responseAsJsonObject, Object executionContext); } diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/DummyClientRegistrationType.java b/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/DummyClientRegistrationType.java index 0ef1da9e494..8858230aa6d 100644 --- a/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/DummyClientRegistrationType.java +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/type/client/DummyClientRegistrationType.java @@ -69,4 +69,19 @@ public String getDcrJwks(Object context) { public boolean isCertValidForClient(Object cert, Object context) { return false; } + + @Override + public boolean modifyPutResponse(Object responseAsJsonObject, Object executionContext) { + return false; + } + + @Override + public boolean modifyReadResponse(Object responseAsJsonObject, Object executionContext) { + return false; + } + + @Override + public boolean modifyPostResponse(Object responseAsJsonObject, Object executionContext) { + return false; + } }