allow extension points for JSON validation parsing. add filter and validator

This commit is contained in:
Misagh Moayyed 2017-05-17 15:59:20 -07:00
parent 1fc896c458
commit 5152f40be9
11 changed files with 237 additions and 47 deletions

View File

@ -35,6 +35,11 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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<String> 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<String, Object> extractCustomAttributes(final String xml) {
return super.extractCustomAttributes(xml);
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 <cas:special:attribute>value</cas:special:attribute>}
*
* @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<String, Object> 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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -206,6 +206,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -261,5 +266,6 @@
<ehcache.version>2.2.0</ehcache.version>
<clover.version>3.0.2</clover.version>
<slf4j.version>1.7.1</slf4j.version>
<jackson.version>2.8.8.1</jackson.version>
</properties>
</project>