Skip to content

Commit

Permalink
Fix Pac4j delegation issue in the event that provider cancels.
Browse files Browse the repository at this point in the history
  • Loading branch information
SavvasMisaghMoayyed committed Sep 27, 2016
1 parent b24cb62 commit 46c8769
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 14 deletions.
@@ -0,0 +1,44 @@
package org.apereo.cas.web.flow;

import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.web.DefaultErrorViewResolver;
import org.springframework.boot.autoconfigure.web.ErrorViewResolver;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
* This is {@link Pac4jErrorViewResolver}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public class Pac4jErrorViewResolver implements ErrorViewResolver {

@Autowired
@Qualifier("conventionErrorViewResolver")
private ErrorViewResolver conventionErrorViewResolver;

@Override
public ModelAndView resolveErrorView(final HttpServletRequest request,
final HttpStatus status, final Map<String, Object> map) {

final Map<String, String[]> params = request.getParameterMap();
if (params.containsKey("error") && params.containsKey("error_code") && params.containsKey("error_description")) {
final Map<String, Object> model = Maps.newHashMap();
model.put("code", status.value());
model.put("error", request.getParameter("error"));
model.put("description", request.getParameter("error_description"));
model.put("client", request.getParameter("client_name"));
model.putAll(map);
return new ModelAndView("casPac4jStopWebflow", model);
}
return conventionErrorViewResolver.resolveErrorView(request, status, map);
}
}
@@ -1,5 +1,6 @@
package org.apereo.cas.web.flow;

import org.apereo.cas.support.pac4j.web.flow.ClientAction;
import org.springframework.webflow.engine.ActionState;
import org.springframework.webflow.engine.Flow;

Expand All @@ -10,20 +11,18 @@
* @author Misagh Moayyed
* @since 4.2
*/
public class Pac4jWebflowConfigurer extends AbstractCasWebflowConfigurer {

private static final String CLIENT_ACTION = "clientAction";
private static final String STOP_WEBFLOW = "stopWebflow";
public class Pac4jWebflowConfigurer extends AbstractCasWebflowConfigurer {

@Override
protected void doInitialize() throws Exception {
final Flow flow = getLoginFlow();
final ActionState actionState = createActionState(flow, CLIENT_ACTION, createEvaluateAction(CLIENT_ACTION));
final ActionState actionState = createActionState(flow, ClientAction.CLIENT_ACTION,
createEvaluateAction(ClientAction.CLIENT_ACTION));
actionState.getTransitionSet().add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS,
CasWebflowConstants.TRANSITION_ID_SEND_TICKET_GRANTING_TICKET));
actionState.getTransitionSet().add(createTransition(CasWebflowConstants.TRANSITION_ID_ERROR, getStartState(flow).getId()));
actionState.getTransitionSet().add(createTransition("stop", STOP_WEBFLOW));
actionState.getTransitionSet().add(createTransition(ClientAction.STOP, ClientAction.STOP_WEBFLOW));
setStartState(flow, actionState);
createViewState(flow, STOP_WEBFLOW, STOP_WEBFLOW);
createViewState(flow, ClientAction.STOP_WEBFLOW, "casPac4jStopWebflow");
}
}
Expand Up @@ -2,10 +2,12 @@

import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.apereo.cas.web.flow.Pac4jErrorViewResolver;
import org.apereo.cas.web.flow.Pac4jWebflowConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.ErrorViewResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -28,7 +30,7 @@ public class Pac4jWebflowConfiguration {

@Autowired
private FlowBuilderServices flowBuilderServices;

@ConditionalOnMissingBean(name="pac4jWebflowConfigurer")
@Bean
public CasWebflowConfigurer pac4jWebflowConfigurer() {
Expand All @@ -37,4 +39,9 @@ public CasWebflowConfigurer pac4jWebflowConfigurer() {
r.setFlowBuilderServices(flowBuilderServices);
return r;
}

@Bean
public ErrorViewResolver pac4jErrorViewResolver() {
return new Pac4jErrorViewResolver();
}
}
Expand Up @@ -21,6 +21,7 @@
import org.pac4j.core.profile.CommonProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;
import org.springframework.webflow.action.AbstractAction;
Expand Down Expand Up @@ -48,6 +49,18 @@
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class ClientAction extends AbstractAction {

/** Stop the webflow for pac4j and route to view. */
public static final String STOP_WEBFLOW = "stopWebflow";

/** Stop the webflow. */
public static final String STOP= "stop";

/**
* Client action state id in the webflow.
*/
public static final String CLIENT_ACTION = "clientAction";

/**
* All the urls and names of the pac4j clients.
*/
Expand Down Expand Up @@ -91,19 +104,19 @@ protected Event doExecute(final RequestContext context) throws Exception {
final Credentials credentials;
try {
credentials = client.getCredentials(webContext);
logger.debug("credentials: {}", credentials);
logger.debug("Retrieved credentials: {}", credentials);
} catch (final Exception e) {
logger.debug("requires http action", e);
logger.debug("The request requires http action", e);
response.flushBuffer();
final ExternalContext externalContext = ExternalContextHolder.getExternalContext();
externalContext.recordResponseComplete();
return new Event(this, "stop");
return new Event(this, STOP_WEBFLOW);
}

// retrieve parameters from web session
final Service service = (Service) session.getAttribute(CasProtocolConstants.PARAMETER_SERVICE);
context.getFlowScope().put(CasProtocolConstants.PARAMETER_SERVICE, service);
logger.debug("retrieve service: {}", service);
logger.debug("Retrieve service: {}", service);
if (service != null) {
request.setAttribute(CasProtocolConstants.PARAMETER_SERVICE, service.getId());
}
Expand All @@ -126,6 +139,9 @@ protected Event doExecute(final RequestContext context) throws Exception {

// no or aborted authentication : go to login page
prepareForLoginPage(context);
if (response.getStatus() == HttpStatus.UNAUTHORIZED.value()) {
return new Event(this, STOP);
}
return error();
}

Expand Down Expand Up @@ -161,14 +177,20 @@ protected void prepareForLoginPage(final RequestContext context) throws HttpActi
final String redirectionUrl = indirectClient.getRedirectAction(webContext).getLocation();
logger.debug("{} -> {}", name, redirectionUrl);
urls.add(new ProviderLoginPageConfiguration(name, redirectionUrl, name.toLowerCase()));
} catch (final HttpAction e) {
if (e.getCode() == HttpStatus.UNAUTHORIZED.value()) {
logger.debug("Authentication request was denied from the provider {}", client.getName());
} else {
logger.warn(e.getMessage(), e);
}
} catch (final Exception e) {
logger.error("Cannot process client {}", client, e);
}
}
if (!urls.isEmpty()) {
context.getFlowScope().put(PAC4J_URLS, urls);
} else {
logger.warn("No clients could be determined based on the provided configuration");
} else if (response.getStatus() != HttpStatus.UNAUTHORIZED.value()) {
logger.info("No clients could be determined based on the provided configuration");
}
}

Expand Down
Expand Up @@ -129,6 +129,12 @@ screen.badworkstation.message=Please contact the system administrator to regain
screen.pm.success.header=Password Change Successful
screen.pm.success.message=Your account password is successfully updated.

screen.pac4j.unauthz.pagetitle=Unauthorized Access
screen.pac4j.unauthz.login=Login Again
screen.pac4j.unauthz.heading=Unauthorized Access
screen.pac4j.unauthz.message=Either the authentication request was rejected/cancelled, or the authentication provider denied access due \
to permissions, etc.

# GAuth
screen.authentication.gauth.register=Your account is not registered. Use the below settings to register your device with CAS.
screen.authentication.gauth.key=Secret key to register is {0}
Expand Down
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout">

<head>
<title th:text="#{screen.pac4j.unauthz.pagetitle}"></title>
</head>

<body id="cas">
<div layout:fragment="content">
<div class="alert alert-danger">
<h2 th:utext="#{screen.pac4j.unauthz.heading}"/>
<p th:utext="#{screen.pac4j.unauthz.message}"/>

<div>
<p/>
<pre th:text="'Error: ' + ${error} + ', Code: ' + ${error} + ', Description: ' + ${description} + ', Client: ' + ${client}"/>
</div>
</div>
<p><a class="btn btn-success" th:href="@{/login}" th:text="#{screen.pac4j.unauthz.login}"/></p>
</div>
</body>
</html>

0 comments on commit 46c8769

Please sign in to comment.