diff --git a/pom.xml b/pom.xml
index 92bbef6a7..fd9b9849e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -182,6 +182,11 @@
oxauth-client
${oxauth.version}
+
+ org.gluu
+ stat-exporter
+ ${oxauth.version}
+
diff --git a/server/pom.xml b/server/pom.xml
index e685c0f96..fc04d3f46 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -331,6 +331,10 @@
org.gluu
oxauth-client
+
+ org.gluu
+ stat-exporter
+
diff --git a/server/src/main/java/org/gluu/oxtrust/action/ConfigurationStatusAction.java b/server/src/main/java/org/gluu/oxtrust/action/ConfigurationStatusAction.java
index d1fb90c7f..ec0225cc6 100644
--- a/server/src/main/java/org/gluu/oxtrust/action/ConfigurationStatusAction.java
+++ b/server/src/main/java/org/gluu/oxtrust/action/ConfigurationStatusAction.java
@@ -7,21 +7,61 @@
package org.gluu.oxtrust.action;
import java.io.Serializable;
+import java.security.cert.CertificateException;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
+import org.gluu.config.oxtrust.AppConfiguration;
+import org.gluu.oxauth.client.OpenIdConfigurationResponse;
+import org.gluu.oxauth.client.service.ClientFactory;
+import org.gluu.oxauth.client.service.StatService;
import org.gluu.oxtrust.model.GluuConfiguration;
+import org.gluu.oxtrust.model.OxAuthClient;
+import org.gluu.oxtrust.service.ClientService;
import org.gluu.oxtrust.service.ConfigurationService;
+import org.gluu.oxtrust.service.EncryptionService;
+import org.gluu.oxtrust.service.OpenIdService;
import org.gluu.oxtrust.util.OxTrustConstants;
import org.gluu.service.security.Secure;
+import org.gluu.stat.exporter.Months;
+import org.gluu.stat.exporter.RegisterRequest;
+import org.gluu.stat.exporter.RegisterResponse;
+import org.gluu.stat.exporter.StatExporterResponse;
+import org.gluu.stat.exporter.TokenResponse;
+import org.gluu.util.security.StringEncrypter.EncryptionException;
import org.slf4j.Logger;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import okhttp3.Credentials;
+import okhttp3.FormBody;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
/**
* Action class for health check display
*
@@ -36,13 +76,35 @@ public class ConfigurationStatusAction implements Serializable {
@Inject
private Logger log;
+
+ @Inject
+ ClientService clientService;
@Inject
private ConfigurationService configurationService;
+
+ @Inject
+ protected AppConfiguration appConfiguration;
+
+ @Inject
+ private OpenIdService openIdService;
private String health;
+
+ private Map statsData;
+
+ @Inject
+ private EncryptionService encryptionService;
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ private OpenIdConfigurationResponse openIdConfiguration;
public String init() {
+ openIdConfiguration = openIdService.getOpenIdConfiguration();
+ if(statsData == null)
+ getStats();
+
return OxTrustConstants.RESULT_SUCCESS;
}
@@ -72,6 +134,14 @@ public String getHealth() {
public void setHealth(String health) {
this.health = health;
}
+
+ public Map getStatsData() {
+ return statsData;
+ }
+
+ public void setStatsData(Map statsData) {
+ this.statsData = statsData;
+ }
public String getHostName(String hostName) {
if (hostName == null || StringUtils.isEmpty(hostName)) {
@@ -80,5 +150,205 @@ public String getHostName(String hostName) {
}
return hostName;
}
+
+ private void getStats() {
+ try {
+ String at = requestToken();
+ log.debug("Access Token : 0" + at);
+
+ //call Stats Service
+ StatService service = ClientFactory.instance().createStatService(openIdConfiguration.getIssuer() + "/restv1/internal/stat");
+ JsonNode node = service.stat("Bearer " + at, Months.getLastMonthsAsString(12), null);
+
+ StatExporterResponse statExporterResponse = prepareResponse(node);
+ this.setStatsData(covertStatFormat(statExporterResponse.getData()));
+ log.debug("Stat Result:" + this.getStatsData());
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ log.error("Failed to get stats " + e.getMessage());
+ }
+
+ }
+
+ private StatExporterResponse prepareResponse(JsonNode node) {
+ StatExporterResponse response = new StatExporterResponse();
+ response.setData(new HashMap<>());
+
+ int totalMau = 42;
+ final JsonNode r = node.get("response");
+ if (r == null) {
+ log.debug("Unable to parse response");
+ return response;
+ }
+
+ for (Map.Entry entry : ImmutableList.copyOf(r.fields())) {
+ final int mau = entry.getValue().get("monthly_active_users").asInt(-1);
+ if (mau == -1) {
+ continue;
+ }
+
+ response.getData().put(entry.getKey(), mau);
+ totalMau += mau;
+ }
+
+ response.setMauSignature(DigestUtils.sha256Hex((Integer.toString(totalMau))));
+ return response;
+ }
+
+ private String requestToken() {
+ OkHttpClient client = getOkHttpClient();
+ String tokenUrl = openIdConfiguration.getTokenEndpoint();
+
+ OxAuthClient oxAuthClient = clientService.getClientByDisplayName("stat exporter");
+ String clientId;
+ String clientSecret;
+ if (oxAuthClient != null) {
+ clientId = oxAuthClient.getInum();
+ clientSecret = oxAuthClient.getEncodedClientSecret();
+ try {
+ clientSecret = encryptionService.decrypt(clientSecret);
+ } catch (EncryptionException e) {
+ log.error("Failed to decrypt client secret :" + e.getMessage() );
+ }
+ } else {
+ RegisterResponse registerResponse = registerClient(client, openIdConfiguration.getRegistrationEndpoint());
+ clientId = registerResponse.getClientId();
+ clientSecret = registerResponse.getClientSecret();
+ }
+
+ log.debug("Requesting token at " + tokenUrl + " with client_id: " + clientId);
+ RequestBody formBody = new FormBody.Builder()
+ .add("grant_type", "client_credentials")
+ .add("username", clientId)
+ .add("password", clientSecret)
+ .add("scope", "openid jans_stat")
+ .build();
+
+ Request request = new Request.Builder()
+ .url(tokenUrl)
+ .post(formBody)
+ .addHeader("Content-Type", "application/x-www-form-urlencoded")
+ .addHeader("Authorization", Credentials.basic(clientId, clientSecret))
+ .build();
+
+ try (Response response = client.newCall(request).execute()) {
+ final String asString = response.body().string();
+ if (response.isSuccessful()) {
+
+ final TokenResponse tokenResponse = objectMapper.readValue(asString, TokenResponse.class);
+
+ final String token = tokenResponse.getAccessToken();
+ if (token != null && !token.isEmpty()) {
+ log.debug("Obtained token successfully with scopes '" + tokenResponse.getScope() + "'");
+ return token;
+ }
+ } else {
+ log.debug("Failed with response code " + response.code() + ", body: " + asString);
+ }
+ } catch (Exception e) {
+ log.error("Failed to obtain token for client :" + clientId + " due to " + e.getMessage());
+ }
+
+ log.debug("Failed to obtain token using client_credentials grant with client_id: " + clientId);
+ return null;
+ }
+
+ private static OkHttpClient getOkHttpClient() {
+ try {
+ // Create a trust manager that does not validate certificate chains
+ final TrustManager[] trustAllCerts = new TrustManager[]{
+ new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return new java.security.cert.X509Certificate[]{};
+ }
+ }
+ };
+
+ // Install the all-trusting trust manager
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+ // Create an ssl socket factory with our all-trusting manager
+ final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+
+ OkHttpClient.Builder builder = new OkHttpClient.Builder();
+ builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
+ builder.hostnameVerifier(new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ });
+
+ OkHttpClient okHttpClient = builder.build();
+ return okHttpClient;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private RegisterResponse registerClient(OkHttpClient client, String registrationEndpoint) {
+ log.debug("Registering client at " + registrationEndpoint);
+
+ RegisterRequest registerRequest = new RegisterRequest();
+ registerRequest.setScope("openid jans_stat");
+ registerRequest.setRedirectUris(Lists.newArrayList("https://stat_exporter"));
+ registerRequest.setGrantTypes(Lists.newArrayList("client_credentials"));
+ registerRequest.setClientName("stat exporter");
+
+ try {
+ //ObjectMapper objectMapper = new ObjectMapper();
+ String payload = objectMapper.writeValueAsString(registerRequest);
+ RequestBody body = RequestBody.create(payload, MediaType.parse("application/json"));
+
+ Request request = new Request.Builder()
+ .url(registrationEndpoint)
+ .post(body)
+ .addHeader("Content-Type", "application/json")
+ .build();
+
+ try (Response response = client.newCall(request).execute()) {
+ final String asString = response.body().string();
+ if (response.isSuccessful() || response.code() == 201) {
+ RegisterResponse registerResponse = objectMapper.readValue(asString, RegisterResponse.class);
+ log.debug("Registered client_id " + registerResponse.getClientId());
+ return registerResponse;
+ } else {
+ log.debug("Failed with register client, status code " + response.code() + ", body: " + asString);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ log.debug("Failed to dynamically register client.");
+ return null;
+ }
+
+ private Map covertStatFormat(Map monthlyStats){
+ Map result = new HashMap();
+
+ for (Map.Entry entry : monthlyStats.entrySet()) {
+ log.debug(entry.getKey() + ":" + entry.getValue());
+ result.put(convert(entry.getKey()),entry.getValue());
+ }
+
+ return result;
+ }
+
+ private String convert(String monthInSomeYear) {
+ DateTimeFormatter inputParser = DateTimeFormatter.ofPattern("uuuuMM");
+ YearMonth yearMonth = YearMonth.parse(monthInSomeYear, inputParser);
+ DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("MMMM, uuuu", Locale.ENGLISH);
+ return yearMonth.format(outputFormatter);
+ }
+
}
diff --git a/server/src/main/webapp/status/applicationStatus.xhtml b/server/src/main/webapp/status/applicationStatus.xhtml
index 65c7f77fc..3e82d7077 100644
--- a/server/src/main/webapp/status/applicationStatus.xhtml
+++ b/server/src/main/webapp/status/applicationStatus.xhtml
@@ -105,6 +105,27 @@
+
+
+
+
+