package org.apereo.cas.util.services; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; import com.fasterxml.jackson.databind.type.SimpleType; import com.google.common.base.Throwables; import org.apache.commons.lang3.ClassUtils; import org.apereo.cas.authentication.principal.cache.CachingPrincipalAttributesRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; /** * This is {@link JasigRegisteredServiceDeserializationProblemHandler} * that attempts load JSON definitions assigned to the `org.jasig` * namespace. This component should be registered globally with JSON object mappers. * * @author Misagh Moayyed * @since 5.1.0 */ public class JasigRegisteredServiceDeserializationProblemHandler extends DeserializationProblemHandler { private static final Logger LOGGER = LoggerFactory.getLogger(JasigRegisteredServiceDeserializationProblemHandler.class); @Override public JavaType handleUnknownTypeId(final DeserializationContext ctxt, final JavaType baseType, final String subTypeId, final TypeIdResolver idResolver, final String failureMsg) throws IOException { try { if (subTypeId.contains("org.jasig.")) { final String newTypeName = subTypeId.replaceAll("jasig", "apereo"); LOGGER.warn("Found legacy CAS JSON definition type identified as [{}]. " + "While CAS will attempt to convert the legacy definition to [{}] for the time being, " + "the definition SHOULD manually be upgraded to the new supported syntax", subTypeId, newTypeName); final Class newType = ClassUtils.getClass(newTypeName); return SimpleType.construct(newType); } return null; } catch (final Exception e) { throw Throwables.propagate(e); } } @Override public boolean handleUnknownProperty(final DeserializationContext ctxt, final JsonParser p, final JsonDeserializer<?> deserializer, final Object beanOrClass, final String propertyName) throws IOException { boolean handled = false; if (beanOrClass instanceof CachingPrincipalAttributesRepository) { final CachingPrincipalAttributesRepository repo = CachingPrincipalAttributesRepository.class.cast(beanOrClass); switch (propertyName) { case "duration": p.nextToken(); p.nextToken(); p.nextToken(); p.nextToken(); p.nextToken(); p.nextToken(); final String timeUnit = p.getText(); p.nextToken(); p.nextToken(); p.nextToken(); final int expiration = p.getValueAsInt(); repo.setTimeUnit(timeUnit); repo.setExpiration(expiration); LOGGER.warn("CAS has converted legacy JSON property [{}] for type [{}]. It parsed 'expiration' value [{}] with time unit of [{}]." + "It is STRONGLY recommended that you review the configuration and upgrade the legacy syntax.", propertyName, beanOrClass.getClass().getName(), expiration, timeUnit); handled = true; break; default: break; } } return handled; } }