diff --git a/oxd-common/src/main/java/org/xdi/oxd/common/CoreUtils.java b/oxd-common/src/main/java/org/xdi/oxd/common/CoreUtils.java index 77f249723..471aca483 100644 --- a/oxd-common/src/main/java/org/xdi/oxd/common/CoreUtils.java +++ b/oxd-common/src/main/java/org/xdi/oxd/common/CoreUtils.java @@ -32,6 +32,7 @@ import java.security.*; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.Executors; @@ -46,6 +47,10 @@ */ public class CoreUtils { + public static boolean isExpired(Date expiredAt) { + return expiredAt.before(new Date()); + } + /** * Lazy initialization of jackson mapper via static holder */ diff --git a/oxd-common/src/main/java/org/xdi/oxd/common/ErrorResponseCode.java b/oxd-common/src/main/java/org/xdi/oxd/common/ErrorResponseCode.java index 78febda7c..e9a609487 100644 --- a/oxd-common/src/main/java/org/xdi/oxd/common/ErrorResponseCode.java +++ b/oxd-common/src/main/java/org/xdi/oxd/common/ErrorResponseCode.java @@ -39,6 +39,7 @@ public enum ErrorResponseCode { INVALID_AUTHORIZATION_CODE_BAD_HASH("invalid_authorization_code_bad_hash", "Authorization code is invalid. Hash of authorization code does not match hash from id_token (c_hash)."), INVALID_REGISTRATION_CLIENT_URL("invalid_registration_client_url", "Registration client URL is invalid. Please check registration_client_url response parameter from IDP (http://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse)."), INVALID_OXD_ID("invalid_oxd_id", "Invalid oxd_id. Unable to find site for oxd_id. It does not exist or removed from the server. Please use register_site command to register a site."), + EXPIRED_CLIENT("expired_client", "Invalid oxd_id. Client is expired."), INVALID_REQUEST("invalid_request", "Request is invalid. It doesn't contains all required parameters or otherwise is malformed."), INVALID_REQUEST_SCOPES_REQUIRED("invalid_request", "Request is invalid. Scopes are required parameter in request."), RPT_NOT_AUTHORIZED("rpt_not_authorized", "Unable to authorize RPT."), diff --git a/oxd-server/src/main/java/org/xdi/oxd/server/Configuration.java b/oxd-server/src/main/java/org/xdi/oxd/server/Configuration.java index 8167d6994..482cf6a11 100644 --- a/oxd-server/src/main/java/org/xdi/oxd/server/Configuration.java +++ b/oxd-server/src/main/java/org/xdi/oxd/server/Configuration.java @@ -67,6 +67,16 @@ public class Configuration { private String storage; @JsonProperty(value = "storage_configuration") private JsonNode storageConfiguration; + @JsonProperty(value = "remove_expired_clients") + private Boolean removeExpiredClients = false; + + public Boolean getRemoveExpiredClients() { + return removeExpiredClients; + } + + public void setRemoveExpiredClients(Boolean removeExpiredClients) { + this.removeExpiredClients = removeExpiredClients; + } public String getCryptProviderKeyStorePath() { return cryptProviderKeyStorePath; diff --git a/oxd-server/src/main/java/org/xdi/oxd/server/Processor.java b/oxd-server/src/main/java/org/xdi/oxd/server/Processor.java index d21f46a43..98ce1ce6c 100644 --- a/oxd-server/src/main/java/org/xdi/oxd/server/Processor.java +++ b/oxd-server/src/main/java/org/xdi/oxd/server/Processor.java @@ -7,14 +7,14 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.xdi.oxd.common.Command; -import org.xdi.oxd.common.CommandResponse; -import org.xdi.oxd.common.CoreUtils; -import org.xdi.oxd.common.ErrorResponseException; +import org.xdi.oxd.common.*; import org.xdi.oxd.common.params.IParams; +import org.xdi.oxd.server.license.LicenseService; import org.xdi.oxd.server.op.IOperation; import org.xdi.oxd.server.op.OperationFactory; +import org.xdi.oxd.server.service.Rp; import org.xdi.oxd.server.service.ValidationService; +import org.xdi.util.Pair; import java.io.IOException; @@ -71,10 +71,13 @@ public CommandResponse process(Command command) { final IOperation operation = (IOperation) OperationFactory.create(command, ServerLauncher.getInjector()); if (operation != null) { IParams iParams = Convertor.asParams(operation.getParameterClass(), command); - validationService.validate(iParams); + Pair rpWithIsClientLocalPair = validationService.validate(iParams); CommandResponse operationResponse = operation.execute(iParams); if (operationResponse != null) { + if (operationResponse.getStatus() == ResponseStatus.OK) { // report usage only of operation is ok + ServerLauncher.getInjector().getInstance(LicenseService.class).notifyClientUsed(rpWithIsClientLocalPair.getFirst(), rpWithIsClientLocalPair.getSecond()); + } return operationResponse; } else { LOG.error("No response from operation. Command: " + command); diff --git a/oxd-server/src/main/java/org/xdi/oxd/server/service/RpService.java b/oxd-server/src/main/java/org/xdi/oxd/server/service/RpService.java index 9c4837aac..93cf2b5c8 100644 --- a/oxd-server/src/main/java/org/xdi/oxd/server/service/RpService.java +++ b/oxd-server/src/main/java/org/xdi/oxd/server/service/RpService.java @@ -7,6 +7,9 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.xdi.oxd.common.CoreUtils; +import org.xdi.oxd.common.ErrorResponseCode; +import org.xdi.oxd.common.ErrorResponseException; import org.xdi.oxd.server.persistence.PersistenceService; import java.io.IOException; @@ -30,10 +33,13 @@ public class RpService { private PersistenceService persistenceService; + private ConfigurationService configurationService; + @Inject - public RpService(ValidationService validationService, PersistenceService persistenceService) { + public RpService(ValidationService validationService, PersistenceService persistenceService, ConfigurationService configurationService) { this.validationService = validationService; this.persistenceService = persistenceService; + this.configurationService = configurationService; } public void removeAllRps() { @@ -57,7 +63,33 @@ public Rp getRp(String oxdId) { rpMap.put(oxdId, rp); } } - return validationService.validate(rp); + rp = validationService.validate(rp); + checkClientExpiredWithException(rp); + return rp; + } + + private void checkClientExpiredWithException(Rp rp) { + if (checkClientExpired(rp)) { + throw new ErrorResponseException(ErrorResponseCode.EXPIRED_CLIENT); + } + } + + private boolean checkClientExpired(Rp rp) { + if (CoreUtils.isExpired(rp.getClientSecretExpiresAt())) { + try { + Boolean removeExpiredClients = configurationService.get().getRemoveExpiredClients(); + if (removeExpiredClients != null && removeExpiredClients) { + LOG.debug("Removing client because it's expired ... rp: " + rp); + rpMap.remove(rp.getOxdId()); + persistenceService.remove(rp.getOxdId()); + LOG.debug("Removed client because it's expired, rp: " + rp); + } + } catch (Exception e) { + LOG.error("Failed to remove expired client, rp: " + rp, e); + } + return true; + } + return false; } public Map getRps() { diff --git a/oxd-server/src/main/java/org/xdi/oxd/server/service/UmaTokenService.java b/oxd-server/src/main/java/org/xdi/oxd/server/service/UmaTokenService.java index 82279723a..3d9d7613d 100644 --- a/oxd-server/src/main/java/org/xdi/oxd/server/service/UmaTokenService.java +++ b/oxd-server/src/main/java/org/xdi/oxd/server/service/UmaTokenService.java @@ -16,6 +16,7 @@ import org.xdi.oxauth.model.uma.UmaScopeType; import org.xdi.oxauth.model.uma.UmaTokenResponse; import org.xdi.oxauth.model.util.Util; +import org.xdi.oxd.common.CoreUtils; import org.xdi.oxd.common.ErrorResponseCode; import org.xdi.oxd.common.ErrorResponseException; import org.xdi.oxd.common.introspection.CorrectRptIntrospectionResponse; @@ -70,7 +71,7 @@ public RpGetRptResponse getRpt(RpGetRptParams params) throws UnsupportedEncoding UmaMetadata discovery = discoveryService.getUmaDiscoveryByOxdId(params.getOxdId()); if (!Strings.isNullOrEmpty(rp.getRpt()) && rp.getRptExpiresAt() != null) { - if (!isExpired(rp.getRptExpiresAt())) { + if (!CoreUtils.isExpired(rp.getRptExpiresAt())) { LOG.debug("RPT from rp, RPT: " + rp.getRpt() + ", rp: " + rp); RpGetRptResponse result = new RpGetRptResponse(); @@ -123,10 +124,6 @@ public RpGetRptResponse getRpt(RpGetRptParams params) throws UnsupportedEncoding throw new ErrorResponseException(ErrorResponseCode.FAILED_TO_GET_RPT); } - public static boolean isExpired(Date expiredAt) { - return expiredAt.before(new Date()); - } - public Pat getPat(String oxdId) { validationService.notBlankOxdId(oxdId); @@ -137,7 +134,7 @@ public Pat getPat(String oxdId) { expiredAt.setTime(site.getPatCreatedAt()); expiredAt.add(Calendar.SECOND, site.getPatExpiresIn()); - if (!isExpired(expiredAt.getTime())) { + if (!CoreUtils.isExpired(expiredAt.getTime())) { LOG.debug("PAT from site configuration, PAT: " + site.getPat()); return new Pat(site.getPat(), "", site.getPatExpiresIn()); } diff --git a/oxd-server/src/main/java/org/xdi/oxd/server/service/ValidationService.java b/oxd-server/src/main/java/org/xdi/oxd/server/service/ValidationService.java index c48d6d25a..b59b224e4 100644 --- a/oxd-server/src/main/java/org/xdi/oxd/server/service/ValidationService.java +++ b/oxd-server/src/main/java/org/xdi/oxd/server/service/ValidationService.java @@ -10,7 +10,7 @@ import org.xdi.oxd.common.params.*; import org.xdi.oxd.server.Configuration; import org.xdi.oxd.server.ServerLauncher; -import org.xdi.oxd.server.license.LicenseService; +import org.xdi.util.Pair; /** * @author Yuriy Zabrovarnyy @@ -38,7 +38,7 @@ public void notBlankOpHost(String opHost) { } } - public void validate(IParams params) { + public Pair validate(IParams params) { Boolean isClientLocal = null; notNull(params); if (params instanceof HasOxdIdParams) { @@ -58,15 +58,19 @@ public void validate(IParams params) { final RpService rpService = ServerLauncher.getInjector().getInstance(RpService.class); final Rp rp = rpService.getRp(oxdId); if (rp != null) { - ServerLauncher.getInjector().getInstance(LicenseService.class).notifyClientUsed(rp, isClientLocal); + return new Pair<>(rp, isClientLocal); } } } catch (ErrorResponseException e) { + if (e.getErrorResponseCode() == ErrorResponseCode.EXPIRED_CLIENT) { + throw e; + } // ignore } catch (Exception e) { LOG.error("Failed to invoke license service client update. Message: " + e.getMessage(), e); } } + return null; } /** diff --git a/oxd-server/src/main/resources/oxd-conf.json b/oxd-server/src/main/resources/oxd-conf.json index 1ddc1bbb8..d8218941c 100644 --- a/oxd-server/src/main/resources/oxd-conf.json +++ b/oxd-server/src/main/resources/oxd-conf.json @@ -24,5 +24,6 @@ "storage":"h2", "storage_configuration": { "dbFileLocation":"/opt/oxd-server/data/oxd_db" - } + }, + "remove_expired_clients":false } diff --git a/oxd-server/src/test/java/org/xdi/oxd/server/service/UmaTokenServiceTest.java b/oxd-server/src/test/java/org/xdi/oxd/server/service/UmaTokenServiceTest.java index ae3660c31..d6e4bd0a8 100644 --- a/oxd-server/src/test/java/org/xdi/oxd/server/service/UmaTokenServiceTest.java +++ b/oxd-server/src/test/java/org/xdi/oxd/server/service/UmaTokenServiceTest.java @@ -1,6 +1,7 @@ package org.xdi.oxd.server.service; import org.testng.annotations.Test; +import org.xdi.oxd.common.CoreUtils; import java.util.Calendar; @@ -22,7 +23,7 @@ public void isExpired() { Calendar past = Calendar.getInstance(); past.add(Calendar.HOUR, -1); - assertFalse(UmaTokenService.isExpired(future.getTime())); - assertTrue(UmaTokenService.isExpired(past.getTime())); + assertFalse(CoreUtils.isExpired(future.getTime())); + assertTrue(CoreUtils.isExpired(past.getTime())); } } diff --git a/oxd-server/src/test/resources/oxd-conf-test.json b/oxd-server/src/test/resources/oxd-conf-test.json index 0437be2c8..75dff3686 100644 --- a/oxd-server/src/test/resources/oxd-conf-test.json +++ b/oxd-server/src/test/resources/oxd-conf-test.json @@ -24,5 +24,6 @@ "storage":"h2", "storage_configuration": { "dbFileLocation":"./oxd_db" - } + }, + "remove_expired_clients":false } \ No newline at end of file