1
|
|
|
package org.apereo.cas.pm.web.flow; |
2
|
|
|
|
3
|
|
|
import org.apereo.cas.configuration.CasConfigurationProperties; |
4
|
|
|
import org.apereo.cas.pm.PasswordChangeBean; |
5
|
|
|
import org.apereo.cas.pm.web.flow.actions.PasswordChangeAction; |
6
|
|
|
import org.apereo.cas.pm.web.flow.actions.SendPasswordResetInstructionsAction; |
7
|
|
|
import org.apereo.cas.util.CollectionUtils; |
8
|
|
|
import org.apereo.cas.web.flow.CasWebflowConfigurer; |
9
|
|
|
import org.apereo.cas.web.flow.CasWebflowConstants; |
10
|
|
|
import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer; |
11
|
|
|
import org.springframework.context.ApplicationContext; |
12
|
|
|
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry; |
13
|
|
|
import org.springframework.webflow.engine.ActionState; |
14
|
|
|
import org.springframework.webflow.engine.Flow; |
15
|
|
|
import org.springframework.webflow.engine.SubflowState; |
16
|
|
|
import org.springframework.webflow.engine.Transition; |
17
|
|
|
import org.springframework.webflow.engine.TransitionableState; |
18
|
|
|
import org.springframework.webflow.engine.ViewState; |
19
|
|
|
import org.springframework.webflow.engine.builder.BinderConfiguration; |
20
|
|
|
import org.springframework.webflow.engine.builder.support.FlowBuilderServices; |
21
|
|
|
import org.springframework.webflow.execution.Action; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* This is {@link PasswordManagementWebflowConfigurer}. |
25
|
|
|
* |
26
|
|
|
* @author Misagh Moayyed |
27
|
|
|
* @since 5.0.0 |
28
|
|
|
*/ |
29
|
|
|
public class PasswordManagementWebflowConfigurer extends AbstractCasWebflowConfigurer { |
30
|
|
|
/** |
31
|
|
|
* Flow id for password reset. |
32
|
|
|
*/ |
33
|
|
|
public static final String FLOW_ID_PASSWORD_RESET = "pswdreset"; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Flow id for password reset. |
37
|
|
|
*/ |
38
|
|
|
public static final String FLOW_VAR_ID_PASSWORD = "password"; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Name of parameter that can be supplied to login url to force display of password change during login. |
42
|
|
|
*/ |
43
|
|
|
public static final String DO_CHANGE_PASSWORD_PARAMETER = "doChangePassword"; |
44
|
|
|
|
45
|
|
|
private static final String PASSWORD_CHANGE_ACTION = "passwordChangeAction"; |
46
|
|
|
private static final String SEND_PASSWORD_RESET_INSTRUCTIONS_ACTION = "sendInstructions"; |
47
|
|
|
|
48
|
|
|
private final Action initPasswordChangeAction; |
49
|
|
|
|
50
|
|
|
public PasswordManagementWebflowConfigurer(final FlowBuilderServices flowBuilderServices, |
51
|
|
|
final FlowDefinitionRegistry loginFlowDefinitionRegistry, |
52
|
|
|
final ApplicationContext applicationContext, |
53
|
|
|
final CasConfigurationProperties casProperties, |
54
|
|
|
final Action initPasswordChangeAction) { |
55
|
|
|
super(flowBuilderServices, loginFlowDefinitionRegistry, applicationContext, casProperties); |
56
|
|
|
this.initPasswordChangeAction = initPasswordChangeAction; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
@Override |
60
|
|
|
protected void doInitialize() { |
61
|
|
|
final Flow flow = getLoginFlow(); |
62
|
|
|
if (flow != null) { |
63
|
|
|
createAccountStatusViewStates(flow); |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
private void createAccountStatusViewStates(final Flow flow) { |
68
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_AUTHENTICATION_BLOCKED, CasWebflowConstants.VIEW_ID_AUTHENTICATION_BLOCKED); |
69
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_INVALID_WORKSTATION, CasWebflowConstants.VIEW_ID_INVALID_WORKSTATION); |
70
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_INVALID_AUTHENTICATION_HOURS, |
71
|
|
|
CasWebflowConstants.VIEW_ID_INVALID_AUTHENTICATION_HOURS); |
72
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_ACCOUNT_LOCKED, CasWebflowConstants.VIEW_ID_ACCOUNT_LOCKED); |
73
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_ACCOUNT_DISABLED, CasWebflowConstants.VIEW_ID_ACCOUNT_DISABLED); |
74
|
|
|
createViewState(flow, CasWebflowConstants.STATE_ID_PASSWORD_UPDATE_SUCCESS, CasWebflowConstants.VIEW_ID_PASSWORD_UPDATE_SUCCESS); |
75
|
|
|
|
76
|
|
|
if (casProperties.getAuthn().getPm().isEnabled()) { |
77
|
|
|
configurePasswordResetFlow(flow, CasWebflowConstants.VIEW_ID_EXPIRED_PASSWORD); |
78
|
|
|
configurePasswordResetFlow(flow, CasWebflowConstants.VIEW_ID_MUST_CHANGE_PASSWORD); |
79
|
|
|
configurePasswordMustChangeForAuthnWarnings(flow); |
80
|
|
|
createPasswordResetFlow(); |
81
|
|
|
} else { |
82
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_EXPIRED_PASSWORD, CasWebflowConstants.VIEW_ID_EXPIRED_PASSWORD); |
83
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_MUST_CHANGE_PASSWORD, CasWebflowConstants.VIEW_ID_MUST_CHANGE_PASSWORD); |
84
|
|
|
} |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
private void configurePasswordMustChangeForAuthnWarnings(final Flow flow) { |
88
|
|
|
final TransitionableState warningState = getTransitionableState(flow, CasWebflowConstants.VIEW_ID_SHOW_AUTHN_WARNING_MSGS); |
89
|
|
|
warningState.getEntryActionList().add(createEvaluateAction("flowScope.pswdChangePostLogin=true")); |
90
|
|
|
createTransitionForState(warningState, "changePassword", CasWebflowConstants.VIEW_ID_MUST_CHANGE_PASSWORD); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
private void createPasswordResetFlow() { |
94
|
|
|
final Flow flow = getLoginFlow(); |
95
|
|
|
if (flow != null) { |
96
|
|
|
final boolean autoLogin = casProperties.getAuthn().getPm().isAutoLogin(); |
97
|
|
|
|
98
|
|
|
final ViewState state = getState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, ViewState.class); |
99
|
|
|
createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_RESET_PASSWORD, |
100
|
|
|
CasWebflowConstants.VIEW_ID_SEND_RESET_PASSWORD_ACCT_INFO); |
101
|
|
|
final ViewState accountInfo = createViewState(flow, CasWebflowConstants.VIEW_ID_SEND_RESET_PASSWORD_ACCT_INFO, |
102
|
|
|
CasWebflowConstants.VIEW_ID_SEND_RESET_PASSWORD_ACCT_INFO); |
103
|
|
|
createTransitionForState(accountInfo, "findAccount", SEND_PASSWORD_RESET_INSTRUCTIONS_ACTION); |
104
|
|
|
final ActionState sendInst = createActionState(flow, SEND_PASSWORD_RESET_INSTRUCTIONS_ACTION, |
105
|
|
|
createEvaluateAction("sendPasswordResetInstructionsAction")); |
106
|
|
|
createTransitionForState(sendInst, CasWebflowConstants.TRANSITION_ID_SUCCESS, |
107
|
|
|
CasWebflowConstants.VIEW_ID_SENT_RESET_PASSWORD_ACCT_INFO); |
108
|
|
|
createTransitionForState(sendInst, CasWebflowConstants.TRANSITION_ID_ERROR, accountInfo.getId()); |
109
|
|
|
createViewState(flow, CasWebflowConstants.VIEW_ID_SENT_RESET_PASSWORD_ACCT_INFO, |
110
|
|
|
CasWebflowConstants.VIEW_ID_SENT_RESET_PASSWORD_ACCT_INFO); |
111
|
|
|
|
112
|
|
|
final Flow pswdFlow = buildFlow("classpath:/webflow/pswdreset/pswdreset-webflow.xml", FLOW_ID_PASSWORD_RESET); |
113
|
|
|
createViewState(pswdFlow, "passwordResetErrorView", CasWebflowConstants.VIEW_ID_PASSWORD_RESET_ERROR); |
114
|
|
|
createViewState(pswdFlow, CasWebflowConstants.STATE_ID_PASSWORD_UPDATE_SUCCESS, |
115
|
|
|
CasWebflowConstants.VIEW_ID_PASSWORD_UPDATE_SUCCESS); |
116
|
|
|
configurePasswordResetFlow(pswdFlow, CasWebflowConstants.VIEW_ID_MUST_CHANGE_PASSWORD); |
117
|
|
|
loginFlowDefinitionRegistry.registerFlowDefinition(pswdFlow); |
118
|
|
|
|
119
|
|
|
final ActionState initializeLoginFormState = getState(flow, CasWebflowConstants.STATE_ID_INIT_LOGIN_FORM, ActionState.class); |
120
|
|
|
final String originalTargetState = initializeLoginFormState.getTransition(CasWebflowConstants.STATE_ID_SUCCESS).getTargetStateId(); |
121
|
|
|
final SubflowState pswdResetSubFlowState = createSubflowState(flow, CasWebflowConstants.STATE_ID_PASSWORD_RESET_SUBFLOW, FLOW_ID_PASSWORD_RESET); |
122
|
|
|
|
123
|
|
|
getTransitionableState(flow, CasWebflowConstants.STATE_ID_REAL_SUBMIT).getEntryActionList() |
124
|
|
|
.add(createEvaluateAction("flowScope." + DO_CHANGE_PASSWORD_PARAMETER |
125
|
|
|
+ " = requestParameters." + DO_CHANGE_PASSWORD_PARAMETER + " != null")); |
126
|
|
|
|
127
|
|
|
createDecisionState(flow, CasWebflowConstants.CHECK_FOR_PASSWORD_RESET_TOKEN_ACTION, |
128
|
|
|
"requestParameters." + SendPasswordResetInstructionsAction.PARAMETER_NAME_TOKEN + " != null", |
129
|
|
|
CasWebflowConstants.STATE_ID_PASSWORD_RESET_SUBFLOW, |
130
|
|
|
originalTargetState); |
131
|
|
|
createTransitionForState(initializeLoginFormState, |
132
|
|
|
CasWebflowConstants.STATE_ID_SUCCESS, |
133
|
|
|
CasWebflowConstants.CHECK_FOR_PASSWORD_RESET_TOKEN_ACTION, true); |
134
|
|
|
createEndState(pswdFlow, CasWebflowConstants.STATE_ID_PASSWORD_RESET_FLOW_COMPLETE); |
135
|
|
|
createTransitionForState( |
136
|
|
|
getTransitionableState(pswdFlow, CasWebflowConstants.STATE_ID_PASSWORD_UPDATE_SUCCESS), |
137
|
|
|
CasWebflowConstants.TRANSITION_ID_PROCEED, |
138
|
|
|
CasWebflowConstants.STATE_ID_PASSWORD_RESET_FLOW_COMPLETE); |
139
|
|
|
createEndState(flow, CasWebflowConstants.STATE_ID_REDIRECT_TO_LOGIN, "'" + CasWebflowConfigurer.FLOW_ID_LOGIN + "'", true); |
140
|
|
|
|
141
|
|
|
createTransitionForState( |
142
|
|
|
pswdResetSubFlowState, |
143
|
|
|
CasWebflowConstants.STATE_ID_PASSWORD_RESET_FLOW_COMPLETE, |
144
|
|
|
autoLogin ? CasWebflowConstants.STATE_ID_REAL_SUBMIT : CasWebflowConstants.STATE_ID_REDIRECT_TO_LOGIN); |
145
|
|
|
|
146
|
|
|
createDecisionState(flow, |
147
|
|
|
CasWebflowConstants.STATE_ID_CHECK_DO_CHANGE_PASSWORD, |
148
|
|
|
"flowScope." + DO_CHANGE_PASSWORD_PARAMETER + " == true", |
149
|
|
|
CasWebflowConstants.VIEW_ID_MUST_CHANGE_PASSWORD, |
150
|
|
|
getTransitionableState(flow, CasWebflowConstants.STATE_ID_REAL_SUBMIT) |
151
|
|
|
.getTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS).getTargetStateId()) |
152
|
|
|
.getEntryActionList().add(createEvaluateAction("flowScope.pswdChangePostLogin=true")); |
153
|
|
|
|
154
|
|
|
createTransitionForState(getTransitionableState(flow, CasWebflowConstants.STATE_ID_REAL_SUBMIT), |
155
|
|
|
CasWebflowConstants.TRANSITION_ID_SUCCESS, CasWebflowConstants.STATE_ID_CHECK_DO_CHANGE_PASSWORD, true); |
156
|
|
|
|
157
|
|
|
createDecisionState(flow, |
158
|
|
|
CasWebflowConstants.STATE_ID_PSWD_CHANGE_CHECK_POST_LOGIN, |
159
|
|
|
"flowScope.pswdChangePostLogin == true", |
160
|
|
|
getTransitionableState(flow, CasWebflowConstants.VIEW_ID_SHOW_AUTHN_WARNING_MSGS) |
161
|
|
|
.getTransition(CasWebflowConstants.TRANSITION_ID_PROCEED).getTargetStateId(), |
162
|
|
|
autoLogin ? CasWebflowConstants.STATE_ID_REAL_SUBMIT : CasWebflowConstants.STATE_ID_REDIRECT_TO_LOGIN); |
163
|
|
|
|
164
|
|
|
createTransitionForState( |
165
|
|
|
getTransitionableState(flow, CasWebflowConstants.STATE_ID_PASSWORD_UPDATE_SUCCESS), |
166
|
|
|
CasWebflowConstants.TRANSITION_ID_PROCEED, |
167
|
|
|
CasWebflowConstants.STATE_ID_PSWD_CHANGE_CHECK_POST_LOGIN); |
168
|
|
|
|
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
private void configurePasswordResetFlow(final Flow flow, final String id) { |
173
|
|
|
createFlowVariable(flow, FLOW_VAR_ID_PASSWORD, PasswordChangeBean.class); |
174
|
|
|
|
175
|
|
|
final BinderConfiguration binder = createStateBinderConfiguration(CollectionUtils.wrapList(FLOW_VAR_ID_PASSWORD, "confirmedPassword")); |
176
|
|
|
final ViewState viewState = createViewState(flow, id, id, binder); |
177
|
|
|
createStateModelBinding(viewState, FLOW_VAR_ID_PASSWORD, PasswordChangeBean.class); |
178
|
|
|
|
179
|
|
|
viewState.getEntryActionList().add(this.initPasswordChangeAction); |
180
|
|
|
final Transition transition = createTransitionForState(viewState, CasWebflowConstants.TRANSITION_ID_SUBMIT, PASSWORD_CHANGE_ACTION); |
181
|
|
|
transition.getAttributes().put("bind", Boolean.TRUE); |
182
|
|
|
transition.getAttributes().put("validate", Boolean.TRUE); |
183
|
|
|
|
184
|
|
|
createStateDefaultTransition(viewState, id); |
185
|
|
|
|
186
|
|
|
final ActionState pswChangeAction = createActionState(flow, PASSWORD_CHANGE_ACTION, createEvaluateAction(PASSWORD_CHANGE_ACTION)); |
187
|
|
|
pswChangeAction.getTransitionSet().add( |
188
|
|
|
createTransition(PasswordChangeAction.PASSWORD_UPDATE_SUCCESS, CasWebflowConstants.STATE_ID_PASSWORD_UPDATE_SUCCESS)); |
189
|
|
|
pswChangeAction.getTransitionSet().add(createTransition(CasWebflowConstants.TRANSITION_ID_ERROR, id)); |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|