package org.apereo.cas.web.support;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.util.cipher.NoOpCipherExecutor;
import org.apereo.inspektr.common.web.ClientInfo;
import org.apereo.inspektr.common.web.ClientInfoHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
/**
* The {@link DefaultCasCookieValueManager} is responsible creating
* the CAS SSO cookie and encrypting and signing its value.
*
* @author Misagh Moayyed
* @since 4.1
*/
public class DefaultCasCookieValueManager implements CookieValueManager {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCasCookieValueManager.class);
private static final char COOKIE_FIELD_SEPARATOR = '@';
private static final int COOKIE_FIELDS_LENGTH = 3;
/**
* The cipher exec that is responsible for encryption and signing of the cookie.
*/
private CipherExecutor<Serializable, String> cipherExecutor = NoOpCipherExecutor.getInstance();
/**
* Instantiates a new Cas cookie value manager.
*
* @param cipherExecutor the cipher executor
*/
public DefaultCasCookieValueManager(final CipherExecutor cipherExecutor) {
this.cipherExecutor = cipherExecutor;
}
@Override
public String buildCookieValue(final String givenCookieValue, final HttpServletRequest request) {
final ClientInfo clientInfo = ClientInfoHolder.getClientInfo();
final StringBuilder builder = new StringBuilder(givenCookieValue)
.append(COOKIE_FIELD_SEPARATOR)
.append(clientInfo.getClientIpAddress());
final String userAgent = WebUtils.getHttpServletRequestUserAgent(request);
if (StringUtils.isBlank(userAgent)) {
throw new IllegalStateException("Request does not specify a user-agent");
}
builder.append(COOKIE_FIELD_SEPARATOR).append(userAgent);
final String res = builder.toString();
LOGGER.debug("Encoding cookie value [{}]", res);
return this.cipherExecutor.encode(res);
}
@Override
public String obtainCookieValue(final Cookie cookie, final HttpServletRequest request) {
final String cookieValue = this.cipherExecutor.decode(cookie.getValue());
LOGGER.debug("Decoded cookie value is [{}]", cookieValue);
if (StringUtils.isBlank(cookieValue)) {
LOGGER.debug("Retrieved decoded cookie value is blank. Failed to decode cookie [{}]", cookie.getName());
return null;
}
final String[] cookieParts = cookieValue.split(String.valueOf(COOKIE_FIELD_SEPARATOR));
if (cookieParts.length != COOKIE_FIELDS_LENGTH) {
throw new IllegalStateException("Invalid cookie. Required fields are missing");
}
final String value = cookieParts[0];
final String remoteAddr = cookieParts[1];
final String userAgent = cookieParts[2];
if (StringUtils.isBlank(value) || StringUtils.isBlank(remoteAddr) || StringUtils.isBlank(userAgent)) {
throw new IllegalStateException("Invalid cookie. Required fields are empty");
}
final ClientInfo clientInfo = ClientInfoHolder.getClientInfo();
if (!remoteAddr.equals(clientInfo.getClientIpAddress())) {
throw new IllegalStateException("Invalid cookie. Required remote address "
+ remoteAddr + " does not match " + clientInfo.getClientIpAddress());
}
final String agent = WebUtils.getHttpServletRequestUserAgent(request);
if (!userAgent.equals(agent)) {
throw new IllegalStateException("Invalid cookie. Required user-agent " + userAgent + " does not match " + agent);
}
return value;
}
}