package org.apereo.cas.web.view; import com.fasterxml.jackson.annotation.JsonInclude; import org.apereo.cas.authentication.Authentication; import org.apereo.cas.authentication.ProtocolAttributeEncoder; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.services.ServicesManager; import org.springframework.web.servlet.view.json.MappingJackson2JsonView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * Renders the model prepared by CAS in JSON format. * Automatically sets the response type and formats * the output for pretty printing. The class relies on * {@link MappingJackson2JsonView} to handle most of the * model processing and as such, does not do anything special. * It is meant and kept to provide a facility for adopters * so that the JSON view can be augmented easily in overlays. * * @author Misagh Moayyed * @since 4.2 */ public class Cas30JsonResponseView extends Cas30ResponseView { public Cas30JsonResponseView(final boolean successResponse, final ProtocolAttributeEncoder protocolAttributeEncoder, final ServicesManager servicesManager, final String authenticationContextAttribute, final boolean releaseProtocolAttributes) { super(successResponse, protocolAttributeEncoder, servicesManager, authenticationContextAttribute, createDelegatedView(), releaseProtocolAttributes); } private static MappingJackson2JsonView createDelegatedView() { final MappingJackson2JsonView view = new MappingJackson2JsonView(); view.setPrettyPrint(true); view.setDisableCaching(true); view.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).findAndRegisterModules(); return view; } @Override public String toString() { return this.getClass().getSimpleName(); } @Override protected void prepareMergedOutputModel(final Map<String, Object> model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final CasServiceResponse casResponse = new CasServiceResponse(); try { super.prepareMergedOutputModel(model, request, response); if (getAssertionFrom(model) != null) { final CasServiceResponseAuthenticationSuccess success = createAuthenticationSuccess(model); casResponse.setAuthenticationSuccess(success); } else { final CasServiceResponseAuthenticationFailure failure = createAuthenticationFailure(model); casResponse.setAuthenticationFailure(failure); } } catch (final Exception e) { final CasServiceResponseAuthenticationFailure failure = createAuthenticationFailure(model); casResponse.setAuthenticationFailure(failure); } finally { final Map<String, Object> casModel = new HashMap<>(); casModel.put("serviceResponse", casResponse); model.clear(); model.putAll(casModel); setView(createDelegatedView()); } } private CasServiceResponseAuthenticationFailure createAuthenticationFailure(final Map<String, Object> model) { final CasServiceResponseAuthenticationFailure failure = new CasServiceResponseAuthenticationFailure(); failure.setCode(getErrorCodeFrom(model)); failure.setDescription(getErrorDescriptionFrom(model)); return failure; } private CasServiceResponseAuthenticationSuccess createAuthenticationSuccess(final Map<String, Object> model) { final CasServiceResponseAuthenticationSuccess success = new CasServiceResponseAuthenticationSuccess(); success.setAttributes(getModelAttributes(model)); final Principal principal = getPrincipal(model); success.setUser(principal.getId()); success.setProxyGrantingTicket(getProxyGrantingTicketIou(model)); final Collection<Authentication> chainedAuthentications = getChainedAuthentications(model); if (chainedAuthentications != null && !chainedAuthentications.isEmpty()) { final List<String> proxies = chainedAuthentications.stream() .map(authn -> authn.getPrincipal().getId()).collect(Collectors.toList()); success.setProxies(proxies); } return success; } private static class CasServiceResponse { 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; } } private 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; } } private 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; } } }