diff --git a/cas-client-core/pom.xml b/cas-client-core/pom.xml index ead56f8..9d5ceae 100644 --- a/cas-client-core/pom.xml +++ b/cas-client-core/pom.xml @@ -35,6 +35,11 @@ true + + com.fasterxml.jackson.core + jackson-databind + + org.springframework spring-beans diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java index bf10e5c..96f8854 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java @@ -28,7 +28,7 @@ import org.jasig.cas.client.util.XmlUtils; * @author Scott Battaglia * @since 3.1 */ -public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator { +public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator implements ProxyTicketValidator { private boolean acceptAnyProxy; @@ -61,7 +61,7 @@ public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator { ); } // this means there was nothing in the proxy chain, which is okay - if ((this.allowEmptyProxyChain && proxies.isEmpty())) { + if (this.allowEmptyProxyChain && proxies.isEmpty()) { logger.debug("Found an empty proxy chain, permitted by client configuration"); return; } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java index e45e50c..fa20991 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java @@ -77,7 +77,7 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick return "serviceValidate"; } - protected final Assertion parseResponseFromServer(final String response) throws TicketValidationException { + protected Assertion parseResponseFromServer(final String response) throws TicketValidationException { final String error = parseAuthenticationFailureFromResponse(response); if (CommonUtils.isNotBlank(error)) { diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30JsonServiceTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30JsonServiceTicketValidator.java deleted file mode 100644 index 39b0fc6..0000000 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30JsonServiceTicketValidator.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.jasig.cas.client.validation; - -import java.util.List; -import java.util.Map; - -/** - * This is {@link Cas30JsonServiceTicketValidator}. - * - * @author Misagh Moayyed - */ -public class Cas30JsonServiceTicketValidator extends Cas30ProxyTicketValidator { - public Cas30JsonServiceTicketValidator(final String casServerUrlPrefix) { - super(casServerUrlPrefix); - getCustomParameters().put("format", "JSON"); - } - - @Override - protected List parseProxiesFromResponse(final String response) { - return super.parseProxiesFromResponse(response); - } - - @Override - protected String parseProxyGrantingTicketFromResponse(final String response) { - return super.parseProxyGrantingTicketFromResponse(response); - } - - @Override - protected String parsePrincipalFromResponse(final String response) { - return super.parsePrincipalFromResponse(response); - } - - @Override - protected String parseAuthenticationFailureFromResponse(final String response) { - return super.parseAuthenticationFailureFromResponse(response); - } - - @Override - protected Map extractCustomAttributes(final String xml) { - return super.extractCustomAttributes(xml); - } -} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ServiceTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ServiceTicketValidator.java index cb155a7..236ea6e 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ServiceTicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ServiceTicketValidator.java @@ -26,7 +26,7 @@ package org.jasig.cas.client.validation; */ public class Cas30ServiceTicketValidator extends Cas20ServiceTicketValidator { - public Cas30ServiceTicketValidator(String casServerUrlPrefix) { + public Cas30ServiceTicketValidator(final String casServerUrlPrefix) { super(casServerUrlPrefix); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30JsonProxyReceivingTicketValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonProxyReceivingTicketValidationFilter.java similarity index 84% rename from cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30JsonProxyReceivingTicketValidationFilter.java rename to cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonProxyReceivingTicketValidationFilter.java index 3d8a41a..76c9d7e 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30JsonProxyReceivingTicketValidationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonProxyReceivingTicketValidationFilter.java @@ -16,7 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.jasig.cas.client.validation; +package org.jasig.cas.client.validation.json; + +import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter; /** * Creates either a Cas30JsonServiceTicketValidator to validate tickets. @@ -28,6 +30,6 @@ public class Cas30JsonProxyReceivingTicketValidationFilter extends Cas30ProxyRec public Cas30JsonProxyReceivingTicketValidationFilter() { super(); this.defaultServiceTicketValidatorClass = Cas30JsonServiceTicketValidator.class; - this.defaultProxyTicketValidatorClass = Cas30JsonServiceTicketValidator.class; + this.defaultProxyTicketValidatorClass = Cas30JsonProxyTicketValidator.class; } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonProxyTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonProxyTicketValidator.java new file mode 100644 index 0000000..8ef4d80 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonProxyTicketValidator.java @@ -0,0 +1,26 @@ +package org.jasig.cas.client.validation.json; + +import org.jasig.cas.client.validation.Assertion; +import org.jasig.cas.client.validation.Cas30ProxyTicketValidator; +import org.jasig.cas.client.validation.ProxyTicketValidator; +import org.jasig.cas.client.validation.TicketValidationException; + +import java.util.List; + +/** + * This is {@link Cas30JsonProxyTicketValidator} that attempts to parse the CAS validation response + * as JSON. Very similar to {@link Cas30JsonServiceTicketValidator}, it also honors proxies as the name suggests. + * + * @author Misagh Moayyed + */ +public class Cas30JsonProxyTicketValidator extends Cas30JsonServiceTicketValidator implements ProxyTicketValidator { + public Cas30JsonProxyTicketValidator(final String casServerUrlPrefix) { + super(casServerUrlPrefix); + getCustomParameters().put("format", "JSON"); + } + + @Override + protected Assertion parseResponseFromServer(final String response) throws TicketValidationException { + return super.parseResponseFromServer(response); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonServiceTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonServiceTicketValidator.java new file mode 100644 index 0000000..4476296 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/Cas30JsonServiceTicketValidator.java @@ -0,0 +1,51 @@ +package org.jasig.cas.client.validation.json; + +import org.jasig.cas.client.authentication.AttributePrincipal; +import org.jasig.cas.client.authentication.AttributePrincipalImpl; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.validation.Assertion; +import org.jasig.cas.client.validation.AssertionImpl; +import org.jasig.cas.client.validation.Cas30ServiceTicketValidator; +import org.jasig.cas.client.validation.TicketValidationException; + +import java.util.Map; + +/** + * This is {@link Cas30JsonServiceTicketValidator} that attempts to parse the CAS validation response + * as JSON. If the response is not formatted as JSON, it shall fallback to the XML default syntax. + * The JSON response provides advantages in terms of naming and parsing CAS attributes that have special + * names that otherwise may not be encoded as XML, such as the invalid {@code value} + * + * @author Misagh Moayyed + */ +public class Cas30JsonServiceTicketValidator extends Cas30ServiceTicketValidator { + + public Cas30JsonServiceTicketValidator(final String casServerUrlPrefix) { + super(casServerUrlPrefix); + getCustomParameters().put("format", "JSON"); + } + + @Override + protected Assertion parseResponseFromServer(final String response) throws TicketValidationException { + final TicketValidationJsonResponse json = new JsonValidationResponseParser().parse(response); + final String proxyGrantingTicketIou = json.getAuthenticationSuccess().getProxyGrantingTicket(); + final String proxyGrantingTicket; + if (CommonUtils.isBlank(proxyGrantingTicketIou) || getProxyGrantingTicketStorage() == null) { + proxyGrantingTicket = null; + } else { + proxyGrantingTicket = getProxyGrantingTicketStorage().retrieve(proxyGrantingTicketIou); + } + + final Assertion assertion; + final Map attributes = json.getAuthenticationSuccess().getAttributes(); + final String principal = json.getAuthenticationSuccess().getUser(); + if (CommonUtils.isNotBlank(proxyGrantingTicket)) { + final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes, + proxyGrantingTicket, getProxyRetriever()); + assertion = new AssertionImpl(attributePrincipal); + } else { + assertion = new AssertionImpl(new AttributePrincipalImpl(principal, attributes)); + } + return assertion; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/JsonValidationResponseParser.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/JsonValidationResponseParser.java new file mode 100644 index 0000000..1977c3a --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/JsonValidationResponseParser.java @@ -0,0 +1,49 @@ +package org.jasig.cas.client.validation.json; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.validation.TicketValidationException; + +import java.util.List; +import java.util.Map; + +/** + * This is {@link JsonValidationResponseParser}. + * + * @author Misagh Moayyed + */ +final class JsonValidationResponseParser { + private final ObjectMapper objectMapper; + + public JsonValidationResponseParser() { + this.objectMapper = new ObjectMapper(); + this.objectMapper.findAndRegisterModules(); + } + + + + public TicketValidationJsonResponse parse(final String response) throws TicketValidationException { + try { + final TicketValidationJsonResponse json = this.objectMapper.readValue(response, TicketValidationJsonResponse.class); + + if (json == null || json.getAuthenticationFailure() != null && json.getAuthenticationSuccess() != null) { + throw new TicketValidationException("Invalid JSON response; either the response is empty or it indicates both a success " + + "and a failure event, which is indicative of a server error. The actual response is " + response); + } + + if (json.getAuthenticationFailure() != null) { + final String error = json.getAuthenticationFailure().getDescription() + + " - " + json.getAuthenticationFailure().getDescription(); + throw new TicketValidationException(error); + } + + final String principal = json.getAuthenticationSuccess().getUser(); + if (CommonUtils.isEmpty(principal)) { + throw new TicketValidationException("No principal was found in the response from the CAS server."); + } + return json; + } catch (final Exception e) { + throw new RuntimeException("Unable to parse JSON validation response", e); + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/TicketValidationJsonResponse.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/TicketValidationJsonResponse.java new file mode 100644 index 0000000..75e83af --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/json/TicketValidationJsonResponse.java @@ -0,0 +1,92 @@ +package org.jasig.cas.client.validation.json; + +import java.util.List; +import java.util.Map; + +/** + * This is {@link TicketValidationJsonResponse}. + * + * @author Misagh Moayyed + */ +final class TicketValidationJsonResponse { + private CasServiceResponseAuthenticationFailure authenticationFailure; + private CasServiceResponseAuthenticationSuccess authenticationSuccess; + + public CasServiceResponseAuthenticationFailure getAuthenticationFailure() { + return this.authenticationFailure; + } + + public void setAuthenticationFailure(final CasServiceResponseAuthenticationFailure authenticationFailure) { + this.authenticationFailure = authenticationFailure; + } + + public CasServiceResponseAuthenticationSuccess getAuthenticationSuccess() { + return this.authenticationSuccess; + } + + public void setAuthenticationSuccess(final CasServiceResponseAuthenticationSuccess authenticationSuccess) { + this.authenticationSuccess = authenticationSuccess; + } + + static class CasServiceResponseAuthenticationSuccess { + private String user; + private String proxyGrantingTicket; + private List proxies; + private Map attributes; + + public String getUser() { + return this.user; + } + + public void setUser(final String user) { + this.user = user; + } + + public String getProxyGrantingTicket() { + return this.proxyGrantingTicket; + } + + public void setProxyGrantingTicket(final String proxyGrantingTicket) { + this.proxyGrantingTicket = proxyGrantingTicket; + } + + public List getProxies() { + return this.proxies; + } + + public void setProxies(final List proxies) { + this.proxies = proxies; + } + + public Map getAttributes() { + return this.attributes; + } + + public void setAttributes(final Map attributes) { + this.attributes = attributes; + } + } + + static class CasServiceResponseAuthenticationFailure { + private String code; + private String description; + + public String getCode() { + return this.code; + } + + public void setCode(final String code) { + this.code = code; + } + + public String getDescription() { + return this.description; + } + + public void setDescription(final String description) { + this.description = description; + } + } +} + + diff --git a/pom.xml b/pom.xml index 7a16ddc..1a9f4d7 100644 --- a/pom.xml +++ b/pom.xml @@ -206,6 +206,11 @@ + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + @@ -261,5 +266,6 @@ 2.2.0 3.0.2 1.7.1 + 2.8.8.1