Skip to content

Commit 8ea7916

Browse files
committed
Merge branch 'cslzchen/fix-flow-init-and-400s' into feature/institution-sso
2 parents 4f1c07b + c1cf96f commit 8ea7916

21 files changed

Lines changed: 423 additions & 25 deletions

etc/cas/config/cas.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ cas.logout.remove-descendant-tickets=false
7171
#
7272
cas.authn.osf-url.home=https://{{ .Values.osfDomain }}/
7373
cas.authn.osf-url.dashboard=https://{{ .Values.osfDomain }}/dashboard/
74+
cas.authn.osf-url.login-with-next=https://{{ .Values.osfDomain }}/login?next=
7475
cas.authn.osf-url.logout=https://{{ .Values.osfDomain }}/logout/
7576
cas.authn.osf-url.resend-confirmation=https://{{ .Values.osfDomain }}/resend/
7677
cas.authn.osf-url.forgot-password=https://{{ .Values.osfDomain }}/forgotpassword/

etc/cas/config/local/cas-local.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ cas.logout.remove-descendant-tickets=false
7272
#
7373
cas.authn.osf-url.home=http://localhost:5000/
7474
cas.authn.osf-url.dashboard=http://localhost:5000/dashboard/
75+
cas.authn.osf-url.login-with-next=http://localhost:5000/login?next=
7576
cas.authn.osf-url.logout=http://localhost:5000/logout/
7677
cas.authn.osf-url.resend-confirmation=http://localhost:5000/resend/
7778
cas.authn.osf-url.forgot-password=http://localhost:5000/forgotpassword/

src/main/java/io/cos/cas/osf/configuration/model/OsfUrlProperties.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import lombok.experimental.Accessors;
66

77
import java.io.Serializable;
8+
import java.net.URLEncoder;
9+
import java.nio.charset.StandardCharsets;
810

911
/**
1012
* This is {@link OsfUrlProperties}.
@@ -39,6 +41,11 @@ public class OsfUrlProperties implements Serializable {
3941
*/
4042
private String register;
4143

44+
/**
45+
* OSF login endpoint with "?next=".
46+
*/
47+
private String loginWithNext;
48+
4249
/**
4350
* OSF logout endpoint URL.
4451
*/
@@ -88,4 +95,11 @@ public class OsfUrlProperties implements Serializable {
8895
* OSF / COS donation page URL.
8996
*/
9097
private String donate;
98+
99+
/**
100+
* Build the default service URL using OSF login endpoint with OSF home page as destination.
101+
*/
102+
public String constructDefaultServiceUrl() {
103+
return loginWithNext + URLEncoder.encode(home, StandardCharsets.UTF_8);
104+
}
91105
}

src/main/java/io/cos/cas/osf/web/config/OsfCasSupportActionsConfiguration.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.cos.cas.osf.dao.JpaOsfDao;
44
import io.cos.cas.osf.web.flow.login.OsfDefaultLoginPreparationAction;
55
import io.cos.cas.osf.web.flow.login.OsfInstitutionLoginPreparationAction;
6+
import io.cos.cas.osf.web.flow.login.OsfCasPreInitialFlowSetupAction;
67
import io.cos.cas.osf.web.flow.login.OsfPrincipalFromNonInteractiveCredentialsAction;
78

89
import org.apereo.cas.CentralAuthenticationService;
@@ -22,7 +23,6 @@
2223
import org.springframework.webflow.execution.Action;
2324

2425
import java.util.LinkedHashMap;
25-
import java.util.LinkedList;
2626
import java.util.List;
2727
import java.util.Map;
2828

@@ -48,7 +48,6 @@ public class OsfCasSupportActionsConfiguration extends CasSupportActionsConfigur
4848
@Qualifier("initialAuthenticationAttemptWebflowEventResolver")
4949
private ObjectProvider<CasDelegatingWebflowEventResolver> initialAuthenticationAttemptWebflowEventResolver;
5050

51-
5251
@Autowired
5352
@Qualifier("adaptiveAuthenticationPolicy")
5453
private ObjectProvider<AdaptiveAuthenticationPolicy> adaptiveAuthenticationPolicy;
@@ -60,6 +59,16 @@ public class OsfCasSupportActionsConfiguration extends CasSupportActionsConfigur
6059
@Autowired
6160
private ObjectProvider<JpaOsfDao> jpaOsfDao;
6261

62+
/**
63+
* Bean configuration for {@link OsfCasPreInitialFlowSetupAction}.
64+
*
65+
* @return the initialized action
66+
*/
67+
@Bean
68+
public Action osfCasPreInitialFlowSetupAction() {
69+
return new OsfCasPreInitialFlowSetupAction(casProperties.getAuthn().getOsfUrl());
70+
}
71+
6372
/**
6473
* Bean configuration for {@link OsfPrincipalFromNonInteractiveCredentialsAction}.
6574
*

src/main/java/io/cos/cas/osf/web/flow/config/OsfCasWebflowContextConfiguration.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.cos.cas.osf.web.flow.config;
22

33
import io.cos.cas.osf.web.flow.configurer.OsfCasLoginWebflowConfigurer;
4+
import io.cos.cas.osf.web.flow.configurer.OsfCasLogoutWebFlowConfigurer;
45

56
import org.apereo.cas.configuration.CasConfigurationProperties;
67
import org.apereo.cas.web.flow.CasWebflowConfigurer;
@@ -46,4 +47,16 @@ public CasWebflowConfigurer defaultWebflowConfigurer() {
4647
osfCasLoginWebflowConfigurer.setOrder(Ordered.HIGHEST_PRECEDENCE);
4748
return osfCasLoginWebflowConfigurer;
4849
}
50+
51+
@Override
52+
@Bean
53+
@Order(DEFAULT_WEB_FLOW_CONFIGURER_ORDER)
54+
@Lazy(false)
55+
public CasWebflowConfigurer defaultLogoutWebflowConfigurer() {
56+
OsfCasLogoutWebFlowConfigurer osfCasLogoutWebFlowConfigurer
57+
= new OsfCasLogoutWebFlowConfigurer(builder(), loginFlowRegistry(), applicationContext, casProperties);
58+
osfCasLogoutWebFlowConfigurer.setLogoutFlowDefinitionRegistry(logoutFlowRegistry());
59+
osfCasLogoutWebFlowConfigurer.setOrder(Ordered.HIGHEST_PRECEDENCE);
60+
return osfCasLogoutWebFlowConfigurer;
61+
}
4962
}

src/main/java/io/cos/cas/osf/web/flow/configurer/OsfCasLoginWebflowConfigurer.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.context.ConfigurableApplicationContext;
2727
import org.springframework.webflow.core.collection.MutableAttributeMap;
2828
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
29+
import org.springframework.webflow.engine.ActionList;
2930
import org.springframework.webflow.engine.ActionState;
3031
import org.springframework.webflow.engine.Flow;
3132
import org.springframework.webflow.engine.History;
@@ -66,13 +67,21 @@ public OsfCasLoginWebflowConfigurer(
6667
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
6768
}
6869

70+
@Override
71+
protected void createInitialFlowActions(final Flow flow) {
72+
final ActionList startActionList = flow.getStartActionList();
73+
startActionList.add(createEvaluateAction(OsfCasWebflowConstants.ACTION_ID_OSF_PRE_INITIAL_FLOW_SETUP));
74+
super.createInitialFlowActions(flow);
75+
}
76+
6977
@Override
7078
protected void createDefaultViewStates(final Flow flow) {
7179
super.createDefaultViewStates(flow);
7280
// Create OSF customized view states
7381
createTwoFactorLoginFormView(flow);
7482
createInstitutionLoginView(flow);
7583
createOrcidLoginAutoRedirectView(flow);
84+
createDefaultServiceLoginAutoRedirectView(flow);
7685
createOsfCasAuthenticationExceptionViewStates(flow);
7786
}
7887

@@ -316,6 +325,11 @@ private void createOsfDefaultLoginCheckAction(final Flow flow) {
316325
OsfCasWebflowConstants.TRANSITION_ID_ORCID_LOGIN_AUTO_REDIRECT,
317326
OsfCasWebflowConstants.VIEW_ID_ORCID_LOGIN_AUTO_REDIRECT
318327
);
328+
createTransitionForState(
329+
action,
330+
OsfCasWebflowConstants.TRANSITION_ID_DEFAULT_SERVICE_LOGIN_AUTO_REDIRECT,
331+
OsfCasWebflowConstants.VIEW_ID_DEFAULT_SERVICE_LOGIN_AUTO_REDIRECT
332+
);
319333
createTransitionForState(
320334
action,
321335
CasWebflowConstants.TRANSITION_ID_ERROR,
@@ -426,6 +440,19 @@ protected void createOrcidLoginAutoRedirectView(final Flow flow) {
426440
);
427441
}
428442

443+
/**
444+
* Create the ORCiD login auto-redirect view to support the OSF feature "sign-up via ORCiD".
445+
*
446+
* @param flow the flow
447+
*/
448+
protected void createDefaultServiceLoginAutoRedirectView(final Flow flow) {
449+
createViewState(
450+
flow,
451+
OsfCasWebflowConstants.VIEW_ID_DEFAULT_SERVICE_LOGIN_AUTO_REDIRECT,
452+
OsfCasWebflowConstants.VIEW_ID_DEFAULT_SERVICE_LOGIN_AUTO_REDIRECT
453+
);
454+
}
455+
429456
/**
430457
* Create the institution SSO init view state to support the OSF feature "sign-in via institutions".
431458
*
@@ -438,5 +465,4 @@ protected void createInstitutionLoginView(final Flow flow) {
438465
OsfCasWebflowConstants.VIEW_ID_INSTITUTION_SSO_INIT
439466
);
440467
}
441-
442468
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.cos.cas.osf.web.flow.configurer;
2+
3+
import io.cos.cas.osf.web.flow.support.OsfCasWebflowConstants;
4+
5+
import org.apereo.cas.configuration.CasConfigurationProperties;
6+
import org.apereo.cas.web.flow.configurer.DefaultLogoutWebflowConfigurer;
7+
8+
import org.springframework.context.ConfigurableApplicationContext;
9+
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
10+
import org.springframework.webflow.engine.ActionList;
11+
import org.springframework.webflow.engine.Flow;
12+
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
13+
14+
/**
15+
* This is {@link OsfCasLogoutWebFlowConfigurer}.
16+
*
17+
* @author Longze Chen
18+
* @since 20.2.0
19+
*/
20+
public class OsfCasLogoutWebFlowConfigurer extends DefaultLogoutWebflowConfigurer {
21+
22+
public OsfCasLogoutWebFlowConfigurer(
23+
final FlowBuilderServices flowBuilderServices,
24+
final FlowDefinitionRegistry flowDefinitionRegistry,
25+
final ConfigurableApplicationContext applicationContext,
26+
final CasConfigurationProperties casProperties
27+
) {
28+
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
29+
}
30+
31+
@Override
32+
protected void doInitialize() {
33+
final Flow flow = getLogoutFlow();
34+
if (flow != null) {
35+
createInitialFlowActions(flow);
36+
}
37+
super.doInitialize();
38+
}
39+
40+
/**
41+
* Create initial flow actions similar to {@link OsfCasLoginWebflowConfigurer#createInitialFlowActions(Flow)}.
42+
*
43+
* @param flow the flow
44+
*/
45+
protected void createInitialFlowActions(final Flow flow) {
46+
final ActionList startActionList = flow.getStartActionList();
47+
startActionList.add(createEvaluateAction(OsfCasWebflowConstants.ACTION_ID_OSF_PRE_INITIAL_FLOW_SETUP));
48+
}
49+
}

src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
import org.springframework.webflow.execution.Event;
99
import org.springframework.webflow.execution.RequestContext;
1010

11+
import java.util.Arrays;
12+
import java.util.LinkedList;
13+
import java.util.List;
14+
1115
/**
1216
* This is {@link OsfAbstractLoginPreparationAction}.
1317
*
@@ -39,6 +43,10 @@ public abstract class OsfAbstractLoginPreparationAction extends AbstractAuthenti
3943

4044
protected static final String PARAMETER_ORCID_REDIRECT_VALUE = "true";
4145

46+
protected static final String PARAMETER_ERROR_SOURCE = "errorSource";
47+
48+
protected static final List<String> EXPECTED_ERROR_CODES = new LinkedList<>(Arrays.asList("401", "403", "404", "405", "423"));
49+
4250
public OsfAbstractLoginPreparationAction(
4351
final CasDelegatingWebflowEventResolver initialAuthenticationAttemptWebflowEventResolver,
4452
final CasWebflowEventResolver serviceTicketRequestWebflowEventResolver,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.cos.cas.osf.web.flow.login;
2+
3+
import io.cos.cas.osf.configuration.model.OsfUrlProperties;
4+
5+
import io.cos.cas.osf.web.flow.support.OsfCasWebflowConstants;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.slf4j.Slf4j;
8+
9+
import org.springframework.webflow.action.AbstractAction;
10+
import org.springframework.webflow.execution.Event;
11+
import org.springframework.webflow.execution.RequestContext;
12+
13+
import javax.validation.constraints.NotNull;
14+
import java.util.Optional;
15+
16+
/**
17+
* This is {@link OsfCasPreInitialFlowSetupAction}.
18+
*
19+
* @author Longze Chen
20+
* @since 20.2.0
21+
*/
22+
@RequiredArgsConstructor
23+
@Slf4j
24+
public class OsfCasPreInitialFlowSetupAction extends AbstractAction {
25+
26+
@NotNull
27+
private final OsfUrlProperties osfUrlProperties;
28+
29+
@Override
30+
protected Event doExecute(final RequestContext context) {
31+
final OsfUrlProperties osfUrl = Optional.of(context).map(
32+
requestContext -> (OsfUrlProperties) requestContext.getFlowScope().get(OsfCasWebflowConstants.FLOW_PARAMETER_OSF_URL)
33+
).orElse(null);
34+
if (osfUrl == null) {
35+
context.getFlowScope().put(OsfCasWebflowConstants.FLOW_PARAMETER_OSF_URL, osfUrlProperties);
36+
}
37+
return success();
38+
}
39+
}

src/main/java/io/cos/cas/osf/web/flow/login/OsfPrincipalFromNonInteractiveCredentialsAction.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
import java.util.Date;
8181
import java.util.List;
8282
import java.util.Map;
83-
import java.util.Optional;
8483

8584
/**
8685
* This is {@link OsfPrincipalFromNonInteractiveCredentialsAction}.
@@ -265,12 +264,6 @@ protected Credential constructCredentialsFromRequest(final RequestContext contex
265264

266265
@Override
267266
protected Event doPreExecute(final RequestContext context) throws Exception {
268-
// Put server-specific OSF URLs into the flow scope
269-
OsfUrlProperties osfUrl = Optional.of(context).map(requestContext
270-
-> (OsfUrlProperties) requestContext.getFlowScope().get(OSF_URL_FLOW_PARAMETER)).orElse(null);
271-
if (osfUrl == null) {
272-
context.getFlowScope().put(OSF_URL_FLOW_PARAMETER, osfUrlProperties);
273-
}
274267
// super.doPreExecute() calls constructCredentialsFromRequest() whose exception must be caught and returned as a flow event
275268
try {
276269
return super.doPreExecute(context);

0 commit comments

Comments
 (0)