From 20f38fd6ee1372033f42d282d948852059389951 Mon Sep 17 00:00:00 2001 From: Yuriy Movchan Date: Thu, 21 Dec 2023 13:56:20 +0300 Subject: [PATCH] feat; OPA related config updates (#7173) * feat: deploy lock-service as custom library Signed-off-by: Yuriy Movchan * feat: policy downloader Signed-off-by: Yuriy Movchan * feat: policy downloader Signed-off-by: Yuriy Movchan * feat: add apache config Signed-off-by: Yuriy Movchan * Revert "feat: add apache config" This reverts commit 5ae88692dc54c9104c8c4655c2fccde52f8ac7e1. * feat: policy downloader Signed-off-by: Yuriy Movchan * feat: add message scopes Signed-off-by: Yuriy Movchan * feat: publish message API Signed-off-by: Yuriy Movchan --------- Signed-off-by: Yuriy Movchan Signed-off-by: Mustafa Baser --- jans-auth-server/server/pom.xml | 2 + .../as/server/service/net/HttpService2.java | 318 +-------- .../ClientInfoRestWebServiceEmbeddedTest.java | 2 +- .../jans/configapi/rest/ApiApplication.java | 1 + .../main/resources/config-api-rs-protect.json | 174 +++++ .../consumer/MessagePolicyInterface.java | 22 + .../policy/consumer/PolicyConsumer.java | 18 + .../jans/model/net/HttpServiceResponse.java | 45 ++ .../io/jans/service/net/BaseHttpService.java | 333 ++++++++++ .../io/jans/lock/service/net/HttpService.java | 21 + .../service/status/StatusCheckerTimer.java | 5 +- .../lock/ws/rs/HealthCheckController.java | 2 + .../main/resources/config-api-rs-protect.json | 620 ------------------ .../server/src/main/resources/log4j2.xml | 35 +- .../lock/model/config/AppConfiguration.java | 9 + .../{ => message}/MessageConsumerFactory.java | 12 +- .../{ => message}/NullMessageConsumer.java | 6 +- .../consumer/policy/NullPolicyConsumer.java | 35 + .../policy/PolicyConsumerFactory.java | 44 ++ .../lock/service/message/TokenSubService.java | 2 +- .../lock/service/policy/PolicyDownloader.java | 73 +++ 21 files changed, 811 insertions(+), 968 deletions(-) create mode 100644 jans-core/message/src/main/java/io/jans/service/policy/consumer/MessagePolicyInterface.java create mode 100644 jans-core/message/src/main/java/io/jans/service/policy/consumer/PolicyConsumer.java create mode 100644 jans-core/service/src/main/java/io/jans/model/net/HttpServiceResponse.java create mode 100644 jans-core/service/src/main/java/io/jans/service/net/BaseHttpService.java create mode 100644 jans-lock/server/src/main/java/io/jans/lock/service/net/HttpService.java delete mode 100644 jans-lock/server/src/main/resources/config-api-rs-protect.json rename jans-lock/service/src/main/java/io/jans/lock/service/consumer/{ => message}/MessageConsumerFactory.java (66%) rename jans-lock/service/src/main/java/io/jans/lock/service/consumer/{ => message}/NullMessageConsumer.java (86%) create mode 100644 jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/NullPolicyConsumer.java create mode 100644 jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/PolicyConsumerFactory.java create mode 100644 jans-lock/service/src/main/java/io/jans/lock/service/policy/PolicyDownloader.java diff --git a/jans-auth-server/server/pom.xml b/jans-auth-server/server/pom.xml index 686d0bf1355..c7fedf2da8a 100644 --- a/jans-auth-server/server/pom.xml +++ b/jans-auth-server/server/pom.xml @@ -392,11 +392,13 @@ + diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/net/HttpService2.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/net/HttpService2.java index 57cbe839131..7a4c9c8fd70 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/net/HttpService2.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/net/HttpService2.java @@ -7,55 +7,8 @@ package io.jans.as.server.service.net; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.Map; -import java.util.Map.Entry; - -import javax.net.ssl.SSLContext; - -import org.apache.commons.codec.binary.Base64; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.routing.HttpRoutePlanner; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.ssl.SSLContexts; -import org.apache.http.ssl.TrustStrategy; -import org.apache.http.util.EntityUtils; -import org.slf4j.Logger; - -import io.jans.as.server.model.net.HttpServiceResponse; -import io.jans.util.StringHelper; -import io.jans.util.Util; -import jakarta.annotation.PostConstruct; +import io.jans.service.net.BaseHttpService; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.servlet.http.HttpServletRequest; /** * Provides operations with http/https requests @@ -63,273 +16,6 @@ * @author Yuriy Movchan Date: 04/10/2023 */ @ApplicationScoped -public class HttpService2 implements Serializable { - - private static final long serialVersionUID = -2398422090669045605L; - - @Inject - private Logger log; - - private Base64 base64; - - private PoolingHttpClientConnectionManager connectionManager; - - @PostConstruct - public void init() { - connectionManager = new PoolingHttpClientConnectionManager(); - connectionManager.setMaxTotal(200); // Increase max total connection to 200 - connectionManager.setDefaultMaxPerRoute(50); // Increase default max connection per route to 50 - - this.base64 = new Base64(); - } - - public CloseableHttpClient getHttpsClientTrustAll() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { - log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); - - TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; - SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build(); - SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext, - NoopHostnameVerifier.INSTANCE); - - return HttpClients.custom().setSSLSocketFactory(sslConSocFactory) - .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()) - .setConnectionManager(connectionManager).build(); - } - - public CloseableHttpClient getHttpsClient() { - return getHttpsClient(RequestConfig.custom().build()); - } - - public CloseableHttpClient getHttpsClient(RequestConfig requestConfig) { - log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); - - return HttpClients.custom() - .setDefaultRequestConfig(RequestConfig.copy(requestConfig).setCookieSpec(CookieSpecs.STANDARD).build()) - .setConnectionManager(connectionManager).build(); - } - - public CloseableHttpClient getHttpsClient(HttpRoutePlanner routerPlanner) { - log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); - - return getHttpsClient(RequestConfig.custom().build(), routerPlanner); - } - - public CloseableHttpClient getHttpsClient(RequestConfig requestConfig, HttpRoutePlanner routerPlanner) { - log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); - - return HttpClients.custom() - .setDefaultRequestConfig(RequestConfig.copy(requestConfig).setCookieSpec(CookieSpecs.STANDARD).build()) - .setConnectionManager(connectionManager).setRoutePlanner(routerPlanner).build(); - } - - public CloseableHttpClient getHttpsClient(String trustStoreType, String trustStorePath, String trustStorePassword) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { - log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); - - SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new File(trustStorePath), trustStorePassword.toCharArray()).build(); - SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext); - - return HttpClients.custom().setSSLSocketFactory(sslConSocFactory) - .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()) - .setConnectionManager(connectionManager).build(); - } - - public CloseableHttpClient getHttpsClient(String trustStoreType, String trustStorePath, String trustStorePassword, - String keyStoreType, String keyStorePath, String keyStorePassword) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException { - log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); - - SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new File(trustStorePath), trustStorePassword.toCharArray()) - .loadKeyMaterial(new File(keyStorePath), keyStorePassword.toCharArray(), keyStorePassword.toCharArray()).build(); - SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext); - - return HttpClients.custom().setSSLSocketFactory(sslConSocFactory) - .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()) - .setConnectionManager(connectionManager).build(); - } - - public HttpServiceResponse executePost(HttpClient httpClient, String uri, String authData, Map headers, String postData, ContentType contentType) { - HttpPost httpPost = new HttpPost(uri); - if (StringHelper.isNotEmpty(authData)) { - httpPost.setHeader("Authorization", "Basic " + authData); - } - - if (headers != null) { - for (Entry headerEntry : headers.entrySet()) { - httpPost.setHeader(headerEntry.getKey(), headerEntry.getValue()); - } - } - - StringEntity stringEntity = new StringEntity(postData, contentType); - httpPost.setEntity(stringEntity); - - try { - HttpResponse httpResponse = httpClient.execute(httpPost); - - return new HttpServiceResponse(httpPost, httpResponse); - } catch (IOException ex) { - log.error("Failed to execute post request", ex); - } - - return null; - } - - public HttpServiceResponse executePost(HttpClient httpClient, String uri, String authData, Map headers, String postData) { - return executePost(httpClient, uri, authData, headers, postData, null); - } - - public HttpServiceResponse executePost(HttpClient httpClient, String uri, String authData, String postData, ContentType contentType) { - return executePost(httpClient, uri, authData, null, postData, contentType); - } - - public String encodeBase64(String value) { - try { - return new String(base64.encode((value).getBytes(Util.UTF8)), Util.UTF8); - } catch (UnsupportedEncodingException ex) { - log.error("Failed to convert '{}' to base64", value, ex); - } - - return null; - } - - public String encodeUrl(String value) { - try { - return URLEncoder.encode(value, Util.UTF8); - } catch (UnsupportedEncodingException ex) { - log.error("Failed to encode url '{}'", value, ex); - } - - return null; - } - - public HttpServiceResponse executeGet(HttpClient httpClient, String requestUri, Map headers) { - HttpGet httpGet = new HttpGet(requestUri); - - if (headers != null) { - for (Entry headerEntry : headers.entrySet()) { - httpGet.setHeader(headerEntry.getKey(), headerEntry.getValue()); - } - } - - try { - HttpResponse httpResponse = httpClient.execute(httpGet); - - return new HttpServiceResponse(httpGet, httpResponse); - } catch (IOException ex) { - log.error("Failed to execute get request", ex); - } - - return null; - } - - public HttpServiceResponse executeGet(HttpClient httpClient, String requestUri) throws ClientProtocolException, IOException { - return executeGet(httpClient, requestUri, null); - } - - public byte[] getResponseContent(HttpResponse httpResponse) throws IOException { - if ((httpResponse == null) || !isResponseStastusCodeOk(httpResponse)) { - return null; - } - - HttpEntity entity = httpResponse.getEntity(); - byte[] responseBytes = new byte[0]; - if (entity != null) { - responseBytes = EntityUtils.toByteArray(entity); - } - - // Consume response content - if (entity != null) { - EntityUtils.consume(entity); - } - - return responseBytes; - } - - public void consume(HttpResponse httpResponse) throws IOException { - if ((httpResponse == null) || !isResponseStastusCodeOk(httpResponse)) { - return; - } - - // Consume response content - HttpEntity entity = httpResponse.getEntity(); - if (entity != null) { - EntityUtils.consume(entity); - } - } - - public String convertEntityToString(byte[] responseBytes) { - if (responseBytes == null) { - return null; - } - - return new String(responseBytes); - } - - public String convertEntityToString(byte[] responseBytes, Charset charset) { - if (responseBytes == null) { - return null; - } - - return new String(responseBytes, charset); - } - - public String convertEntityToString(byte[] responseBytes, String charsetName) throws UnsupportedEncodingException { - if (responseBytes == null) { - return null; - } - - return new String(responseBytes, charsetName); - } - - public boolean isResponseStastusCodeOk(HttpResponse httpResponse) { - int responseStastusCode = httpResponse.getStatusLine().getStatusCode(); - if ((responseStastusCode == HttpStatus.SC_OK) || (responseStastusCode == HttpStatus.SC_CREATED) || (responseStastusCode == HttpStatus.SC_ACCEPTED) - || (responseStastusCode == HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION) || (responseStastusCode == HttpStatus.SC_NO_CONTENT) || (responseStastusCode == HttpStatus.SC_RESET_CONTENT) - || (responseStastusCode == HttpStatus.SC_PARTIAL_CONTENT) || (responseStastusCode == HttpStatus.SC_MULTI_STATUS)) { - return true; - } - - return false; - } - - public boolean isResponseStatusCodeOk(HttpResponse httpResponse) { - return isResponseStastusCodeOk(httpResponse); - } - - public boolean isContentTypeXml(HttpResponse httpResponse) { - Header contentType = httpResponse.getEntity().getContentType(); - if (contentType == null) { - return false; - } - - String contentTypeValue = contentType.getValue(); - if (StringHelper.equals(contentTypeValue, ContentType.APPLICATION_XML.getMimeType()) || StringHelper.equals(contentTypeValue, ContentType.TEXT_XML.getMimeType())) { - return true; - } - - return false; - } - - public String constructServerUrl(final HttpServletRequest request) { - int serverPort = request.getServerPort(); - - String redirectUrl; - if ((serverPort == 80) || (serverPort == 443)) { - redirectUrl = String.format("%s://%s%s", request.getScheme(), request.getServerName(), request.getContextPath()); - } else { - redirectUrl = String.format("%s://%s:%s%s", request.getScheme(), request.getServerName(), request.getServerPort(), request.getContextPath()); - } - - return redirectUrl.toLowerCase(); - } - - public HttpRoutePlanner buildDefaultRoutePlanner(final String hostname, final int port, final String scheme) { - //Creating an HttpHost object for proxy - HttpHost proxyHost = new HttpHost(hostname, port, scheme); - - return new DefaultProxyRoutePlanner(proxyHost); - } - - public HttpRoutePlanner buildDefaultRoutePlanner(final String proxy) { - return buildDefaultRoutePlanner(proxy, -1, null); - } +public class HttpService2 extends BaseHttpService { } diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ws/rs/ClientInfoRestWebServiceEmbeddedTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ws/rs/ClientInfoRestWebServiceEmbeddedTest.java index bd227d912fd..a1de58e6943 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/ws/rs/ClientInfoRestWebServiceEmbeddedTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ws/rs/ClientInfoRestWebServiceEmbeddedTest.java @@ -317,7 +317,7 @@ public void requestClientInfoStep2PasswordFlow(final String clientInfoPath) thro assertTrue(jsonObj.has("jansAppType"), "Unexpected result: oxAuthAppType not found"); assertTrue(jsonObj.has("jansIdTknSignedRespAlg"), "Unexpected result: oxAuthIdTokenSignedResponseAlg not found"); - assertTrue(jsonObj.has("jansRedirectURI"), "Unexpected result: oxAuthRedirectURI not found"); + assertTrue(jsonObj.has("jansRedirectURI"), "Unexpected result: jansRedirectURI not found"); assertTrue(jsonObj.has("jansScope"), "Unexpected result: oxAuthScope not found"); } catch (JSONException e) { e.printStackTrace(); diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java b/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java index d27a0a80dd3..74d0b66038c 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java @@ -123,6 +123,7 @@ public Set> getClasses() { classes.add(AcrsResource.class); classes.add(AttributesResource.class); classes.add(CacheConfigurationResource.class); + classes.add(MessageConfigurationResource.class); classes.add(ClientsResource.class); classes.add(AuthConfigResource.class); classes.add(ConfigSmtpResource.class); diff --git a/jans-config-api/server/src/main/resources/config-api-rs-protect.json b/jans-config-api/server/src/main/resources/config-api-rs-protect.json index 3cbd440a9d5..8c261703220 100644 --- a/jans-config-api/server/src/main/resources/config-api-rs-protect.json +++ b/jans-config-api/server/src/main/resources/config-api-rs-protect.json @@ -2550,6 +2550,180 @@ ] } ] + }, + { + "path": "/jans-config-api/api/v1/config/message", + "conditions": [ + { + "httpMethods": [ + "GET" + ], + "scopes": [ + { + "inum": "1800.01.68", + "name": "https://jans.io/oauth/config/message.readonly" + } + ], + "groupScopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "superScopes": [ + { + "inum": "1800.03.1", + "name": "https://jans.io/oauth/config/read-all" + } + ] + }, + { + "httpMethods": [ + "PATCH" + ], + "scopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + } + ] + }, + { + "path": "/jans-config-api/api/v1/config/message/redis", + "conditions": [ + { + "httpMethods": [ + "GET" + ], + "scopes": [ + { + "inum": "1800.01.68", + "name": "https://jans.io/oauth/config/message.readonly" + } + ], + "groupScopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "superScopes": [ + { + "inum": "1800.03.1", + "name": "https://jans.io/oauth/config/read-all" + } + ] + }, + { + "httpMethods": [ + "PUT" + ], + "scopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + }, + { + "httpMethods": [ + "PATCH" + ], + "scopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + } + ] + }, + { + "path": "/jans-config-api/api/v1/config/message/postgres", + "conditions": [ + { + "httpMethods": [ + "GET" + ], + "scopes": [ + { + "inum": "1800.01.68", + "name": "https://jans.io/oauth/config/message.readonly" + } + ], + "groupScopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "superScopes": [ + { + "inum": "1800.03.1", + "name": "https://jans.io/oauth/config/read-all" + } + ] + }, + { + "httpMethods": [ + "PUT" + ], + "scopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + }, + { + "httpMethods": [ + "PATCH" + ], + "scopes": [ + { + "inum": "1800.01.69", + "name": "https://jans.io/oauth/config/message.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + } + ] } ] } \ No newline at end of file diff --git a/jans-core/message/src/main/java/io/jans/service/policy/consumer/MessagePolicyInterface.java b/jans-core/message/src/main/java/io/jans/service/policy/consumer/MessagePolicyInterface.java new file mode 100644 index 00000000000..2e32bba0192 --- /dev/null +++ b/jans-core/message/src/main/java/io/jans/service/policy/consumer/MessagePolicyInterface.java @@ -0,0 +1,22 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2023, Janssen Project + */ + +package io.jans.service.policy.consumer; + +import org.json.JSONArray; + +/** + * Interface for each policy consumer + * + * @author Yuriy Movchan Date: 12/20/2023 + */ +public interface MessagePolicyInterface { + + public boolean putPolicies(String sourceUri, JSONArray policies); + + public boolean removePolicies(String sourceUri); + +} diff --git a/jans-core/message/src/main/java/io/jans/service/policy/consumer/PolicyConsumer.java b/jans-core/message/src/main/java/io/jans/service/policy/consumer/PolicyConsumer.java new file mode 100644 index 00000000000..072ef0e1540 --- /dev/null +++ b/jans-core/message/src/main/java/io/jans/service/policy/consumer/PolicyConsumer.java @@ -0,0 +1,18 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2023, Janssen Project + */ + +package io.jans.service.policy.consumer; + +/** + * Base message consumer + * + * @author Yuriy Movchan Date: 12/20/2023 + */ +public abstract class PolicyConsumer implements MessagePolicyInterface { + + public abstract String getPolicyConsumerType(); + +} diff --git a/jans-core/service/src/main/java/io/jans/model/net/HttpServiceResponse.java b/jans-core/service/src/main/java/io/jans/model/net/HttpServiceResponse.java new file mode 100644 index 00000000000..39bd129228b --- /dev/null +++ b/jans-core/service/src/main/java/io/jans/model/net/HttpServiceResponse.java @@ -0,0 +1,45 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.model.net; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpRequestBase; + +import java.io.Serializable; + +/** + * @author Yuriy Movchan Date: 07/14/2015 + */ +public class HttpServiceResponse implements Serializable { + + private static final long serialVersionUID = 2218884738060554709L; + + private final HttpRequestBase httpRequest; + private final HttpResponse httpResponse; + + public HttpServiceResponse(HttpRequestBase httpRequest, HttpResponse httpResponse) { + this.httpRequest = httpRequest; + this.httpResponse = httpResponse; + } + + public HttpRequestBase getHttpRequest() { + return httpRequest; + } + + public HttpResponse getHttpResponse() { + return httpResponse; + } + + public void closeConnection() { + if (httpRequest == null) { + return; + } + + httpRequest.releaseConnection(); + } + +} diff --git a/jans-core/service/src/main/java/io/jans/service/net/BaseHttpService.java b/jans-core/service/src/main/java/io/jans/service/net/BaseHttpService.java new file mode 100644 index 00000000000..8c800f40405 --- /dev/null +++ b/jans-core/service/src/main/java/io/jans/service/net/BaseHttpService.java @@ -0,0 +1,333 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.service.net; + + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.Map; +import java.util.Map.Entry; + +import javax.net.ssl.SSLContext; + +import org.apache.commons.codec.binary.Base64; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.routing.HttpRoutePlanner; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.ssl.TrustStrategy; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; + +import io.jans.model.net.HttpServiceResponse; +import io.jans.util.StringHelper; +import io.jans.util.Util; +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import jakarta.servlet.http.HttpServletRequest; + +/** + * Provides operations with http/https requests + * + * @author Yuriy Movchan Date: 04/10/2023 + */ +public abstract class BaseHttpService implements Serializable { + + private static final long serialVersionUID = -2398422090669045605L; + + @Inject + private Logger log; + + private Base64 base64; + + private PoolingHttpClientConnectionManager connectionManager; + + @PostConstruct + public void init() { + connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(200); // Increase max total connection to 200 + connectionManager.setDefaultMaxPerRoute(50); // Increase default max connection per route to 50 + + this.base64 = new Base64(); + } + + public CloseableHttpClient getHttpsClientTrustAll() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { + log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); + + TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; + SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build(); + SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext, + NoopHostnameVerifier.INSTANCE); + + return HttpClients.custom().setSSLSocketFactory(sslConSocFactory) + .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()) + .setConnectionManager(connectionManager).build(); + } + + public CloseableHttpClient getHttpsClient() { + return getHttpsClient(RequestConfig.custom().build()); + } + + public CloseableHttpClient getHttpsClient(RequestConfig requestConfig) { + log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); + + return HttpClients.custom() + .setDefaultRequestConfig(RequestConfig.copy(requestConfig).setCookieSpec(CookieSpecs.STANDARD).build()) + .setConnectionManager(connectionManager).build(); + } + + public CloseableHttpClient getHttpsClient(HttpRoutePlanner routerPlanner) { + log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); + + return getHttpsClient(RequestConfig.custom().build(), routerPlanner); + } + + public CloseableHttpClient getHttpsClient(RequestConfig requestConfig, HttpRoutePlanner routerPlanner) { + log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); + + return HttpClients.custom() + .setDefaultRequestConfig(RequestConfig.copy(requestConfig).setCookieSpec(CookieSpecs.STANDARD).build()) + .setConnectionManager(connectionManager).setRoutePlanner(routerPlanner).build(); + } + + public CloseableHttpClient getHttpsClient(String trustStoreType, String trustStorePath, String trustStorePassword) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { + log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); + + SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new File(trustStorePath), trustStorePassword.toCharArray()).build(); + SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext); + + return HttpClients.custom().setSSLSocketFactory(sslConSocFactory) + .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()) + .setConnectionManager(connectionManager).build(); + } + + public CloseableHttpClient getHttpsClient(String trustStoreType, String trustStorePath, String trustStorePassword, + String keyStoreType, String keyStorePath, String keyStorePassword) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException { + log.trace("Connection manager stats: {}", connectionManager.getTotalStats()); + + SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new File(trustStorePath), trustStorePassword.toCharArray()) + .loadKeyMaterial(new File(keyStorePath), keyStorePassword.toCharArray(), keyStorePassword.toCharArray()).build(); + SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext); + + return HttpClients.custom().setSSLSocketFactory(sslConSocFactory) + .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()) + .setConnectionManager(connectionManager).build(); + } + + public HttpServiceResponse executePost(HttpClient httpClient, String uri, String authData, Map headers, String postData, ContentType contentType) { + HttpPost httpPost = new HttpPost(uri); + if (StringHelper.isNotEmpty(authData)) { + httpPost.setHeader("Authorization", "Basic " + authData); + } + + if (headers != null) { + for (Entry headerEntry : headers.entrySet()) { + httpPost.setHeader(headerEntry.getKey(), headerEntry.getValue()); + } + } + + StringEntity stringEntity = new StringEntity(postData, contentType); + httpPost.setEntity(stringEntity); + + try { + HttpResponse httpResponse = httpClient.execute(httpPost); + + return new HttpServiceResponse(httpPost, httpResponse); + } catch (IOException ex) { + log.error("Failed to execute post request", ex); + } + + return null; + } + + public HttpServiceResponse executePost(HttpClient httpClient, String uri, String authData, Map headers, String postData) { + return executePost(httpClient, uri, authData, headers, postData, null); + } + + public HttpServiceResponse executePost(HttpClient httpClient, String uri, String authData, String postData, ContentType contentType) { + return executePost(httpClient, uri, authData, null, postData, contentType); + } + + public String encodeBase64(String value) { + try { + return new String(base64.encode((value).getBytes(Util.UTF8)), Util.UTF8); + } catch (UnsupportedEncodingException ex) { + log.error("Failed to convert '{}' to base64", value, ex); + } + + return null; + } + + public String encodeUrl(String value) { + try { + return URLEncoder.encode(value, Util.UTF8); + } catch (UnsupportedEncodingException ex) { + log.error("Failed to encode url '{}'", value, ex); + } + + return null; + } + + public HttpServiceResponse executeGet(HttpClient httpClient, String requestUri, Map headers) { + HttpGet httpGet = new HttpGet(requestUri); + + if (headers != null) { + for (Entry headerEntry : headers.entrySet()) { + httpGet.setHeader(headerEntry.getKey(), headerEntry.getValue()); + } + } + + try { + HttpResponse httpResponse = httpClient.execute(httpGet); + + return new HttpServiceResponse(httpGet, httpResponse); + } catch (IOException ex) { + log.error("Failed to execute get request", ex); + } + + return null; + } + + public HttpServiceResponse executeGet(HttpClient httpClient, String requestUri) throws ClientProtocolException, IOException { + return executeGet(httpClient, requestUri, null); + } + + public byte[] getResponseContent(HttpResponse httpResponse) throws IOException { + if ((httpResponse == null) || !isResponseStastusCodeOk(httpResponse)) { + return null; + } + + HttpEntity entity = httpResponse.getEntity(); + byte[] responseBytes = new byte[0]; + if (entity != null) { + responseBytes = EntityUtils.toByteArray(entity); + } + + // Consume response content + if (entity != null) { + EntityUtils.consume(entity); + } + + return responseBytes; + } + + public void consume(HttpResponse httpResponse) throws IOException { + if ((httpResponse == null) || !isResponseStastusCodeOk(httpResponse)) { + return; + } + + // Consume response content + HttpEntity entity = httpResponse.getEntity(); + if (entity != null) { + EntityUtils.consume(entity); + } + } + + public String convertEntityToString(byte[] responseBytes) { + if (responseBytes == null) { + return null; + } + + return new String(responseBytes); + } + + public String convertEntityToString(byte[] responseBytes, Charset charset) { + if (responseBytes == null) { + return null; + } + + return new String(responseBytes, charset); + } + + public String convertEntityToString(byte[] responseBytes, String charsetName) throws UnsupportedEncodingException { + if (responseBytes == null) { + return null; + } + + return new String(responseBytes, charsetName); + } + + public boolean isResponseStastusCodeOk(HttpResponse httpResponse) { + int responseStastusCode = httpResponse.getStatusLine().getStatusCode(); + if ((responseStastusCode == HttpStatus.SC_OK) || (responseStastusCode == HttpStatus.SC_CREATED) || (responseStastusCode == HttpStatus.SC_ACCEPTED) + || (responseStastusCode == HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION) || (responseStastusCode == HttpStatus.SC_NO_CONTENT) || (responseStastusCode == HttpStatus.SC_RESET_CONTENT) + || (responseStastusCode == HttpStatus.SC_PARTIAL_CONTENT) || (responseStastusCode == HttpStatus.SC_MULTI_STATUS)) { + return true; + } + + return false; + } + + public boolean isResponseStatusCodeOk(HttpResponse httpResponse) { + return isResponseStastusCodeOk(httpResponse); + } + + public boolean isContentTypeXml(HttpResponse httpResponse) { + Header contentType = httpResponse.getEntity().getContentType(); + if (contentType == null) { + return false; + } + + String contentTypeValue = contentType.getValue(); + if (StringHelper.equals(contentTypeValue, ContentType.APPLICATION_XML.getMimeType()) || StringHelper.equals(contentTypeValue, ContentType.TEXT_XML.getMimeType())) { + return true; + } + + return false; + } + + public String constructServerUrl(final HttpServletRequest request) { + int serverPort = request.getServerPort(); + + String redirectUrl; + if ((serverPort == 80) || (serverPort == 443)) { + redirectUrl = String.format("%s://%s%s", request.getScheme(), request.getServerName(), request.getContextPath()); + } else { + redirectUrl = String.format("%s://%s:%s%s", request.getScheme(), request.getServerName(), request.getServerPort(), request.getContextPath()); + } + + return redirectUrl.toLowerCase(); + } + + public HttpRoutePlanner buildDefaultRoutePlanner(final String hostname, final int port, final String scheme) { + //Creating an HttpHost object for proxy + HttpHost proxyHost = new HttpHost(hostname, port, scheme); + + return new DefaultProxyRoutePlanner(proxyHost); + } + + public HttpRoutePlanner buildDefaultRoutePlanner(final String proxy) { + return buildDefaultRoutePlanner(proxy, -1, null); + } + +} diff --git a/jans-lock/server/src/main/java/io/jans/lock/service/net/HttpService.java b/jans-lock/server/src/main/java/io/jans/lock/service/net/HttpService.java new file mode 100644 index 00000000000..d120f1444bc --- /dev/null +++ b/jans-lock/server/src/main/java/io/jans/lock/service/net/HttpService.java @@ -0,0 +1,21 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.lock.service.net; + + +import io.jans.service.net.BaseHttpService; +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Provides operations with http/https requests + * + * @author Yuriy Movchan Date: 21/12/2023 + */ +@ApplicationScoped +public class HttpService extends BaseHttpService { + +} diff --git a/jans-lock/server/src/main/java/io/jans/lock/service/status/StatusCheckerTimer.java b/jans-lock/server/src/main/java/io/jans/lock/service/status/StatusCheckerTimer.java index 262e5caf285..2e064adbb55 100644 --- a/jans-lock/server/src/main/java/io/jans/lock/service/status/StatusCheckerTimer.java +++ b/jans-lock/server/src/main/java/io/jans/lock/service/status/StatusCheckerTimer.java @@ -99,9 +99,8 @@ public void process(@Observes @Scheduled StatusCheckerTimerEvent statusCheckerTi private void processInt() { log.debug("Starting update of sever status"); - StatsData statsData = new StatsData(); - Date currentDateTime = new Date(); - statsData.setLastUpdate(currentDateTime); + statsData = new StatsData(); + statsData.setLastUpdate(new Date()); statsData.setFacterData(getFacterData()); statsData.setDbType(configurationFactory.getBaseConfiguration().getString("persistence.type")); diff --git a/jans-lock/server/src/main/java/io/jans/lock/ws/rs/HealthCheckController.java b/jans-lock/server/src/main/java/io/jans/lock/ws/rs/HealthCheckController.java index f15bdee7c3f..dfa1dd84c0d 100644 --- a/jans-lock/server/src/main/java/io/jans/lock/ws/rs/HealthCheckController.java +++ b/jans-lock/server/src/main/java/io/jans/lock/ws/rs/HealthCheckController.java @@ -16,6 +16,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; @@ -39,6 +40,7 @@ public class HealthCheckController { Logger logger; @Inject + @RequestScoped private StatsData statsData; @Operation(summary = "Returns application server status", description = "Returns application server status", operationId = "get-server-stat", tags = { diff --git a/jans-lock/server/src/main/resources/config-api-rs-protect.json b/jans-lock/server/src/main/resources/config-api-rs-protect.json deleted file mode 100644 index dcacfbd867c..00000000000 --- a/jans-lock/server/src/main/resources/config-api-rs-protect.json +++ /dev/null @@ -1,620 +0,0 @@ -{"resources":[ - { - "path":"/jans-config-api/api/v1/acrs", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/acrs.readonly" - ] - }, - { - "httpMethods":["PUT"], - "scopes":[ - "https://jans.io/oauth/config/acrs.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/attributes", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/attributes.readonly" - ] - }, - { - "httpMethods":["PATCH","POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/attributes.write" - ] - } - , - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/attributes.delete" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/cache", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/cache.readonly" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/cache/redis", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/cache.readonly" - ] - }, - { - "httpMethods":["PUT"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/cache/in-memory", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/cache.readonly" - ] - }, - { - "httpMethods":["PUT"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/cache/native-persistence", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/cache.readonly" - ] - }, - { - "httpMethods":["PUT"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/cache/memcached", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/cache.readonly" - ] - }, - { - "httpMethods":["PUT"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/cache.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/openid/clients", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/openid/clients.readonly" - ] - }, - { - "httpMethods":["PATCH","POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/openid/clients.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/openid/clients.delete" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/jans-auth-server/config", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/jans-auth-server/config/properties.readonly" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/jans-auth-server/config/properties.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/jans-auth-server/config/persistence", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/jans-auth-server/config/properties.readonly" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/smtp", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/smtp.readonly" - ] - }, - { - "httpMethods":["POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/smtp.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/smtp.delete" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/smtp/test", - "conditions":[ - { - "httpMethods":["POST"], - "scopes":[ - "https://jans.io/oauth/config/smtp.readonly" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/scripts", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/scripts.readonly" - ] - }, - { - "httpMethods":["POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/scripts.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/scripts/type/{type}", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/scripts.readonly" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/scripts/inum/{inum}", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/scripts.readonly" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/scripts/{inum}", - "conditions":[ - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/scripts.delete" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/scripts.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/fido2/config", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/fido2.readonly" - ] - }, - { - "httpMethods":["PUT"], - "scopes":[ - "https://jans.io/oauth/config/fido2.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/jwks", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/jwks.readonly" - ] - }, - { - "httpMethods":["PATCH","PUT"], - "scopes":[ - "https://jans.io/oauth/config/jwks.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/jwks.delete" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/database/ldap", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/database/ldap.readonly" - ] - }, - { - "httpMethods":["POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/database/ldap.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/database/ldap/{name}", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/database/ldap.readonly" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/database/ldap.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/database/ldap.delete" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/config/database/ldap/test", - "conditions":[ - { - "httpMethods":["POST"], - "scopes":[ - "https://jans.io/oauth/config/database/ldap.readonly" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/logging", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/logging.readonly" - ] - }, - { - "httpMethods":["PUT"], - "scopes":[ - "https://jans.io/oauth/config/logging.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/scopes", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/scopes.readonly" - ] - }, - { - "httpMethods":["POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/scopes.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/scopes/{inum}", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/scopes.readonly" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/scopes.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/scopes.delete" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/uma/resources", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/uma/resources.readonly" - ] - }, - { - "httpMethods":["POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/uma/resources.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/uma/resources/{id}", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/uma/resources.readonly" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/uma/resources.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/uma/resources.delete" - ] - } - - ] - }, - { - "path":"/jans-config-api/api/v1/stat", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/stats.readonly", - "jans_stat" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/health", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/health/live", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/health/ready", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ ] - } - ] - }, - { - "path":"/jans-config-api/scim/scim-config", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/scim/config.readonly" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/scim/config.write" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/org", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/organization.readonly" - ] - }, - { - "httpMethods":["PATCH"], - "scopes":[ - "https://jans.io/oauth/config/organization.write" - ] - } - ] - }, - { - "path":"/jans-config-api/mgt/configuser", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/user.readonly" - ] - }, - { - "httpMethods":["PATCH","POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/user.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/user.delete" - ] - } - ] - }, - { - "path":"/jans-config-api/api/v1/agama", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/config/agama.readonly" - ] - }, - { - "httpMethods":["POST","PUT"], - "scopes":[ - "https://jans.io/oauth/config/agama.write" - ] - }, - { - "httpMethods":["DELETE"], - "scopes":[ - "https://jans.io/oauth/config/agama.delete" - ] - } - ] - } - , - { - "path":"/jans-config-api/api/v1/jans-auth-server/session", - "conditions":[ - { - "httpMethods":["GET"], - "scopes":[ - "https://jans.io/oauth/jans-auth-server/session.readonly" - ] - }, - { - "httpMethods":["POST"], - "scopes":[ - "https://jans.io/oauth/jans-auth-server/session.delete" - ] - } - ] - } -] -} \ No newline at end of file diff --git a/jans-lock/server/src/main/resources/log4j2.xml b/jans-lock/server/src/main/resources/log4j2.xml index 917917813d6..0faca8a8bbb 100644 --- a/jans-lock/server/src/main/resources/log4j2.xml +++ b/jans-lock/server/src/main/resources/log4j2.xml @@ -5,7 +5,7 @@ - + @@ -14,7 +14,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -44,7 +44,7 @@ - + @@ -56,52 +56,51 @@ - + - - + - + - + - + - + - + - + - + - + - + - + diff --git a/jans-lock/service/src/main/java/io/jans/lock/model/config/AppConfiguration.java b/jans-lock/service/src/main/java/io/jans/lock/model/config/AppConfiguration.java index a27d2b51959..75d1768bf7b 100644 --- a/jans-lock/service/src/main/java/io/jans/lock/model/config/AppConfiguration.java +++ b/jans-lock/service/src/main/java/io/jans/lock/model/config/AppConfiguration.java @@ -42,6 +42,7 @@ public class AppConfiguration implements Configuration { private int cleanServiceInterval; private String messageConsumerType; + private String policyConsumerType; public String getBaseDN() { return baseDN; @@ -135,4 +136,12 @@ public void setMessageConsumerType(String messageConsumerType) { this.messageConsumerType = messageConsumerType; } + public String getPolicyConsumerType() { + return policyConsumerType; + } + + public void setPolicyConsumerType(String policyConsumerType) { + this.policyConsumerType = policyConsumerType; + } + } diff --git a/jans-lock/service/src/main/java/io/jans/lock/service/consumer/MessageConsumerFactory.java b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/message/MessageConsumerFactory.java similarity index 66% rename from jans-lock/service/src/main/java/io/jans/lock/service/consumer/MessageConsumerFactory.java rename to jans-lock/service/src/main/java/io/jans/lock/service/consumer/message/MessageConsumerFactory.java index 8a77dc9b074..89de626d512 100644 --- a/jans-lock/service/src/main/java/io/jans/lock/service/consumer/MessageConsumerFactory.java +++ b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/message/MessageConsumerFactory.java @@ -4,7 +4,7 @@ * Copyright (c) 2023, Janssen Project */ -package io.jans.lock.service.consumer; +package io.jans.lock.service.consumer.message; import org.slf4j.Logger; @@ -28,17 +28,17 @@ public class MessageConsumerFactory { @Inject @Any - private Instance consumerProviderInstances; + private Instance messageConsumerProviderInstances; public MessageConsumer getMessageConsumer(String messageConsumerType) { - for (MessageConsumer consumerProvider : consumerProviderInstances) { - String serviceMessageConsumerType = consumerProvider.getMessageConsumerType(); + for (MessageConsumer messageConsumerProvider : messageConsumerProviderInstances) { + String serviceMessageConsumerType = messageConsumerProvider.getMessageConsumerType(); if (StringHelper.equalsIgnoreCase(serviceMessageConsumerType, messageConsumerType)) { - return consumerProvider; + return messageConsumerProvider; } } - return consumerProviderInstances.select(NullMessageConsumer.class).get(); + return messageConsumerProviderInstances.select(NullMessageConsumer.class).get(); } } diff --git a/jans-lock/service/src/main/java/io/jans/lock/service/consumer/NullMessageConsumer.java b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/message/NullMessageConsumer.java similarity index 86% rename from jans-lock/service/src/main/java/io/jans/lock/service/consumer/NullMessageConsumer.java rename to jans-lock/service/src/main/java/io/jans/lock/service/consumer/message/NullMessageConsumer.java index 5cce44e63b1..f1200ed2117 100644 --- a/jans-lock/service/src/main/java/io/jans/lock/service/consumer/NullMessageConsumer.java +++ b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/message/NullMessageConsumer.java @@ -1,4 +1,4 @@ -package io.jans.lock.service.consumer; +package io.jans.lock.service.consumer.message; import org.slf4j.Logger; @@ -10,7 +10,7 @@ @ApplicationScoped public class NullMessageConsumer extends MessageConsumer { - public static String CONSUMER_TYPE = "NULL"; + public static String MESSAGE_CONSUMER_TYPE = "NULL"; @Inject private Logger log; @@ -33,7 +33,7 @@ public void onUnsubscribe(String channel, int subscribedChannels) { @Override public String getMessageConsumerType() { - return CONSUMER_TYPE; + return MESSAGE_CONSUMER_TYPE; } } diff --git a/jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/NullPolicyConsumer.java b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/NullPolicyConsumer.java new file mode 100644 index 00000000000..573b19ac731 --- /dev/null +++ b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/NullPolicyConsumer.java @@ -0,0 +1,35 @@ +package io.jans.lock.service.consumer.policy; + +import org.json.JSONArray; +import org.slf4j.Logger; + +import io.jans.service.policy.consumer.PolicyConsumer; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class NullPolicyConsumer extends PolicyConsumer { + + public static String POLICY_CONSUMER_TYPE = "NULL"; + + @Inject + private Logger log; + + @Override + public boolean putPolicies(String sourceUri, JSONArray policies) { + log.debug("putPolicies from {}, count {}", sourceUri, policies.length()); + return true; + } + + @Override + public boolean removePolicies(String sourceUri) { + log.debug("removePolicies from {}", sourceUri); + return true; + } + + @Override + public String getPolicyConsumerType() { + return POLICY_CONSUMER_TYPE; + } + +} diff --git a/jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/PolicyConsumerFactory.java b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/PolicyConsumerFactory.java new file mode 100644 index 00000000000..c90b08346c6 --- /dev/null +++ b/jans-lock/service/src/main/java/io/jans/lock/service/consumer/policy/PolicyConsumerFactory.java @@ -0,0 +1,44 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2023, Janssen Project + */ + +package io.jans.lock.service.consumer.policy; + +import org.slf4j.Logger; + +import io.jans.service.policy.consumer.PolicyConsumer; +import io.jans.util.StringHelper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +/** + * Message consumer factory + * + * @author Yuriy Movchan Date: 12/20/2023 + */ +@ApplicationScoped +public class PolicyConsumerFactory { + + @Inject + private Logger log; + + @Inject + @Any + private Instance policyConsumerProviderInstances; + + public PolicyConsumer getMessageConsumer(String messageConsumerType) { + for (PolicyConsumer policyConsumerProvider : policyConsumerProviderInstances) { + String serviceMessageConsumerType = policyConsumerProvider.getPolicyConsumerType(); + if (StringHelper.equalsIgnoreCase(serviceMessageConsumerType, messageConsumerType)) { + return policyConsumerProvider; + } + } + + return policyConsumerProviderInstances.select(NullPolicyConsumer.class).get(); + } + +} diff --git a/jans-lock/service/src/main/java/io/jans/lock/service/message/TokenSubService.java b/jans-lock/service/src/main/java/io/jans/lock/service/message/TokenSubService.java index 4af52032728..37dbe26049f 100644 --- a/jans-lock/service/src/main/java/io/jans/lock/service/message/TokenSubService.java +++ b/jans-lock/service/src/main/java/io/jans/lock/service/message/TokenSubService.java @@ -9,7 +9,7 @@ import org.slf4j.Logger; import io.jans.lock.model.config.AppConfiguration; -import io.jans.lock.service.consumer.MessageConsumerFactory; +import io.jans.lock.service.consumer.message.MessageConsumerFactory; import io.jans.service.message.consumer.MessageConsumer; import io.jans.service.message.provider.MessageProvider; import jakarta.enterprise.context.ApplicationScoped; diff --git a/jans-lock/service/src/main/java/io/jans/lock/service/policy/PolicyDownloader.java b/jans-lock/service/src/main/java/io/jans/lock/service/policy/PolicyDownloader.java new file mode 100644 index 00000000000..5f15dc9463c --- /dev/null +++ b/jans-lock/service/src/main/java/io/jans/lock/service/policy/PolicyDownloader.java @@ -0,0 +1,73 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2023, Janssen Project + */ + +package io.jans.lock.service.policy; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.slf4j.Logger; + +import io.jans.service.cdi.async.Asynchronous; +import io.jans.service.cdi.event.ConfigurationEvent; +import io.jans.service.cdi.event.Scheduled; +import io.jans.service.net.BaseHttpService; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; + +/** + * Policy loader service + * + * @author Yuriy Movchan Date: 12/21/2023 + */ +@ApplicationScoped +public class PolicyDownloader { + + @Inject + private Logger log; + + @Inject + private BaseHttpService httpService; + + private AtomicBoolean isActive; + + @PostConstruct + public void init() { + log.info("Initializing PolicyDownloader ..."); + this.isActive = new AtomicBoolean(true); + try { + reloadPolicies(); + } finally { + this.isActive.set(false); + } + } + + @Asynchronous + public void reloadConfigurationTimerEvent(@Observes @Scheduled ConfigurationEvent configurationEvent) { + if (this.isActive.get()) { + return; + } + + if (!this.isActive.compareAndSet(false, true)) { + return; + } + + try { + reloadPolicies(); + } catch (Throwable ex) { + log.error("Exception happened while reloading policies", ex); + } finally { + this.isActive.set(false); + } + } + + private void reloadPolicies() { + // Download and cache checksum into local cache + + } + +}