package org.apereo.cas.logout;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.authentication.principal.WebApplicationService;
import org.apereo.cas.ticket.TicketGrantingTicket;
import org.apereo.cas.ticket.proxy.ProxyGrantingTicket;
import org.apereo.cas.util.CompressionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* This logout manager handles the Single Log Out process.
*
* @author Jerome Leleu
* @since 4.0.0
*/
public class DefaultLogoutManager implements LogoutManager {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLogoutManager.class);
private final boolean singleLogoutCallbacksDisabled;
private final LogoutMessageCreator logoutMessageBuilder;
private final SingleLogoutServiceMessageHandler singleLogoutServiceMessageHandler;
private final LogoutExecutionPlan logoutExecutionPlan;
/**
* Build the logout manager.
*
* @param logoutMessageBuilder the builder to construct logout messages.
* @param singleLogoutServiceMessageHandler who actually perform the logout request
* @param singleLogoutCallbacksDisabled Set if the logout is disabled.
* @param logoutExecutionPlan the logout execution plan
*/
public DefaultLogoutManager(final LogoutMessageCreator logoutMessageBuilder, final SingleLogoutServiceMessageHandler singleLogoutServiceMessageHandler,
final boolean singleLogoutCallbacksDisabled, final LogoutExecutionPlan logoutExecutionPlan) {
this.logoutMessageBuilder = logoutMessageBuilder;
this.singleLogoutServiceMessageHandler = singleLogoutServiceMessageHandler;
this.singleLogoutCallbacksDisabled = singleLogoutCallbacksDisabled;
this.logoutExecutionPlan = logoutExecutionPlan;
}
/**
* Perform a back channel logout for a given ticket granting ticket and returns all the logout requests.
*
* @param ticket a given ticket granting ticket.
* @return all logout requests.
*/
@Override
public List<LogoutRequest> performLogout(final TicketGrantingTicket ticket) {
LOGGER.info("Performing logout operations for [{}]", ticket.getId());
if (this.singleLogoutCallbacksDisabled) {
LOGGER.info("Single logout callbacks are disabled");
return Collections.emptyList();
}
final List<LogoutRequest> logoutRequests = new ArrayList<>();
performLogoutForTicket(ticket, logoutRequests);
this.logoutExecutionPlan.getLogoutHandlers().forEach(h -> {
LOGGER.debug("Invoking logout handler [{}] to process ticket [{}]", h.getClass().getSimpleName(), ticket.getId());
h.handle(ticket);
});
LOGGER.info("[{}] logout requests were processed", logoutRequests.size());
return logoutRequests;
}
private void performLogoutForTicket(final TicketGrantingTicket ticket, final List<LogoutRequest> logoutRequests) {
ticket.getServices().entrySet()
.stream()
.filter(entry -> entry.getValue() instanceof WebApplicationService)
.forEach(entry -> {
final Service service = entry.getValue();
LOGGER.debug("Handling single logout callback for [{}]", service);
final LogoutRequest logoutRequest = this.singleLogoutServiceMessageHandler.handle((WebApplicationService) service, entry.getKey());
if (logoutRequest != null) {
LOGGER.debug("Captured logout request [{}]", logoutRequest);
logoutRequests.add(logoutRequest);
}
});
final Collection<ProxyGrantingTicket> proxyGrantingTickets = ticket.getProxyGrantingTickets();
if (proxyGrantingTickets.isEmpty()) {
LOGGER.debug("There are no proxy-granting tickets associated with [{}] to process for single logout", ticket.getId());
} else {
proxyGrantingTickets.forEach(proxyGrantingTicket -> performLogoutForTicket(proxyGrantingTicket, logoutRequests));
}
}
/**
* Create a logout message for front channel logout.
*
* @param logoutRequest the logout request.
* @return a front SAML logout message.
*/
@Override
public String createFrontChannelLogoutMessage(final LogoutRequest logoutRequest) {
final String logoutMessage = this.logoutMessageBuilder.create(logoutRequest);
LOGGER.trace("Attempting to deflate the logout message [{}]", logoutMessage);
return CompressionUtils.deflate(logoutMessage);
}
}