|
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
|
|
|
|