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