Skip to content

Commit

Permalink
rework captcha for password resets
Browse files Browse the repository at this point in the history
  • Loading branch information
mmoayyed committed May 27, 2020
1 parent 7f1ad4d commit c5d0f2a
Show file tree
Hide file tree
Showing 23 changed files with 378 additions and 151 deletions.
56 changes: 28 additions & 28 deletions .github/workflows/cas-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Setup tmate session
uses: mxschmitt/action-tmate@v2
# - name: Setup tmate session
# uses: mxschmitt/action-tmate@v2
- name: Set up JDK
uses: actions/setup-java@v1
with:
Expand Down Expand Up @@ -144,8 +144,8 @@ jobs:
- checkstyleTest
steps:
- uses: actions/checkout@v2
- name: Setup tmate session
uses: mxschmitt/action-tmate@v2
# - name: Setup tmate session
# uses: mxschmitt/action-tmate@v2
- name: Set up JDK
uses: actions/setup-java@v1
with:
Expand Down Expand Up @@ -454,8 +454,8 @@ jobs:
needs: [cache]
steps:
- uses: actions/checkout@v2
- name: Setup tmate session
uses: mxschmitt/action-tmate@v2
# - name: Setup tmate session
# uses: mxschmitt/action-tmate@v2
- name: Set up JDK
uses: actions/setup-java@v1
with:
Expand Down Expand Up @@ -668,28 +668,28 @@ jobs:

##########################################################################

renovate-dependency-updates:
runs-on: ubuntu-latest
needs: [cache]
steps:
- uses: actions/checkout@v2
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: ${{ env.JDK_CURRENT }}
- name: Initialize
run: chmod -R 777 ./ci/*.sh && ./ci/init-build.sh
- uses: actions/cache@v1
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- uses: renovatebot/[email protected]
if: ${{ !contains(github.head_ref, 'renovate') && github.event_name == 'push' }}
with:
token: ${{ secrets.RENOVATE_TOKEN }}
configurationFile: .github/renovate.js
# renovate-dependency-updates:
# runs-on: ubuntu-latest
# needs: [cache]
# steps:
# - uses: actions/checkout@v2
# - name: Set up JDK
# uses: actions/setup-java@v1
# with:
# java-version: ${{ env.JDK_CURRENT }}
# - name: Initialize
# run: chmod -R 777 ./ci/*.sh && ./ci/init-build.sh
# - uses: actions/cache@v1
# with:
# path: ~/.gradle/caches
# key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
# restore-keys: |
# ${{ runner.os }}-gradle-
# - uses: renovatebot/[email protected]
# if: ${{ !contains(github.head_ref, 'renovate') && github.event_name == 'push' }}
# with:
# token: ${{ secrets.RENOVATE_TOKEN }}
# configurationFile: .github/renovate.js

##########################################################################

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ public interface CasWebflowConstants {
* Transitions.
****************************************
*/


/**
* The transition state 'captchaError'.
*/
String TRANSITION_ID_CAPTCHA_ERROR = "captchaError";

/**
* The transition state 'authenticationFailure'.
*/
Expand Down Expand Up @@ -381,10 +388,12 @@ public interface CasWebflowConstants {
* State id 'acceptableUsagePolicyView.
*/
String STATE_ID_ACCEPTABLE_USAGE_POLICY_VIEW = "acceptableUsagePolicyView";

/**
* State id 'aupAcceptedAction.
*/
String STATE_ID_AUP_ACCEPTED = "aupAcceptedAction";

/**
* State id 'acceptableUsagePolicyCheck.
*/
Expand Down Expand Up @@ -514,10 +523,12 @@ public interface CasWebflowConstants {
* The state id 'spnego'.
*/
String STATE_ID_SPNEGO = "spnego";

/**
* The state id 'startSpnegoAuthenticate'.
*/
String STATE_ID_START_SPNEGO_AUTHENTICATE = "startSpnegoAuthenticate";

/**
* The state id 'evaluateClientRequest'.
*/
Expand All @@ -533,6 +544,21 @@ public interface CasWebflowConstants {
*/
String STATE_ID_WSFED_STOP_WEBFLOW = "casWsFedStopWebflow";

/**
* The view state 'sendPasswordResetInstructions'.
*/
String STATE_ID_SEND_PASSWORD_RESET_INSTRUCTIONS = "sendPasswordResetInstructions";

/**
* The view state 'sendForgotUsernameInstructions'.
*/
String STATE_ID_FORGOT_USERNAME_INSTRUCTIONS = "sendForgotUsernameInstructions";

/**
* The view state 'passwordChangeAction'.
*/
String STATE_ID_PASSWORD_CHANGE_ACTION = "passwordChangeAction";

/*
****************************************
* Views.
Expand Down Expand Up @@ -701,6 +727,26 @@ public interface CasWebflowConstants {
* Actions.
****************************************
*/

/**
* Action id 'validateCaptchaAction'.
*/
String ACTION_ID_VALIDATE_CAPTCHA = "validateCaptchaAction";
/**
* Action id 'initializeCaptchaAction'.
*/
String ACTION_ID_INIT_CAPTCHA = "initializeCaptchaAction";

/**
* Action id 'validateCaptchaAction'.
*/
String ACTION_ID_PASSWORD_RESET_VALIDATE_CAPTCHA = "passwordResetValidateCaptchaAction";

/**
* Action id 'passwordResetInitializeCaptchaAction'.
*/
String ACTION_ID_PASSWORD_RESET_INIT_CAPTCHA = "passwordResetInitializeCaptchaAction";

/**
* Action id 'renderLoginFormAction'.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@ public interface CasWebflowExecutionPlan {
* @return the webflow configurers
*/
Collection<CasWebflowConfigurer> getWebflowConfigurers();

/**
* Execute the plan.
*/
void execute();
}
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,19 @@ public Transition createTransitionForState(final TransitionableState state, fina
return createTransitionForState(state, criteriaOutcome, targetState, false);
}

/**
* Create transition for state transition.
*
* @param flow the flow
* @param stateId the state id
* @param criteriaOutcome the criteria outcome
* @param targetState the target state
* @return the transition
*/
public Transition createTransitionForState(final Flow flow, final String stateId, final String criteriaOutcome, final String targetState) {
return createTransitionForState(getTransitionableState(flow, stateId), criteriaOutcome, targetState, false);
}

/**
* Add transition to action state.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ public void registerWebflowConfigurer(final CasWebflowConfigurer cfg) {
this.webflowConfigurers.add(cfg);
}

/**
* Execute the plan.
*/
@Override
public void execute() {
AnnotationAwareOrderComparator.sortIfNecessary(webflowConfigurers);
webflowConfigurers.forEach(c -> {
Expand Down
14 changes: 12 additions & 2 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,20 @@ ext.libraries = [
}
],
cassandra : [
dependencies.create("com.datastax.oss:java-driver-core:$cassandraDriverVersion"),
dependencies.create("com.datastax.oss:java-driver-mapper-runtime:$cassandraDriverVersion"),
dependencies.create("com.datastax.oss:java-driver-core:$cassandraDriverVersion") {
exclude(group: "org.slf4j", module: "slf4j-api")
exclude(group: "org.codehaus.groovy", module: "groovy-json")
exclude(group: "org.codehaus.groovy", module: "groovy")
},
dependencies.create("com.datastax.oss:java-driver-mapper-runtime:$cassandraDriverVersion") {
exclude(group: "org.slf4j", module: "slf4j-api")
exclude(group: "org.codehaus.groovy", module: "groovy-json")
exclude(group: "com.datastax.oss", module: "java-driver-core")
},
dependencies.create("org.springframework.data:spring-data-cassandra:$springDataCassandraVersion") {
exclude(group: "org.slf4j", module: "slf4j-api")
exclude(group: "org.codehaus.groovy", module: "groovy")
exclude(group: "com.datastax.oss", module: "java-driver-mapper-runtime")
}
],
apachefediz : [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@

import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer;
import org.apereo.cas.web.support.WebUtils;

import lombok.val;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.ActionState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

import java.util.ArrayList;

Expand All @@ -25,8 +21,6 @@
*/
public class CasCaptchaWebflowConfigurer extends AbstractCasWebflowConfigurer {

static final String ACTION_ID_VALIDATE_CAPTCHA = "validateCaptchaAction";

public CasCaptchaWebflowConfigurer(final FlowBuilderServices flowBuilderServices,
final FlowDefinitionRegistry loginFlowDefinitionRegistry,
final ConfigurableApplicationContext applicationContext,
Expand All @@ -50,19 +44,12 @@ private void createValidateRecaptchaAction(final Flow flow) {
actionList.forEach(currentActions::add);
currentActions.forEach(actionList::remove);

actionList.add(createEvaluateAction(ACTION_ID_VALIDATE_CAPTCHA));
actionList.add(createEvaluateAction(CasWebflowConstants.ACTION_ID_VALIDATE_CAPTCHA));
currentActions.forEach(actionList::add);
state.getTransitionSet().add(createTransition("captchaError", CasWebflowConstants.STATE_ID_INIT_LOGIN_FORM));
state.getTransitionSet().add(createTransition(CasWebflowConstants.TRANSITION_ID_CAPTCHA_ERROR, CasWebflowConstants.STATE_ID_INIT_LOGIN_FORM));
}

private void createInitialRecaptchaEnabledAction(final Flow flow) {
flow.getStartActionList().add(new Action() {
@Override
public Event execute(final RequestContext requestContext) {
val googleRecaptcha = casProperties.getGoogleRecaptcha();
WebUtils.putRecaptchaPropertiesFlowScope(requestContext, googleRecaptcha);
return new EventFactorySupport().success(this);
}
});
flow.getStartActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_INIT_CAPTCHA));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.apereo.cas.web.flow;

import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.support.WebUtils;

import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

/**
* This is {@link InitializeCaptchaAction}.
*
* @author Misagh Moayyed
* @since 6.2.0
*/
@RequiredArgsConstructor
public class InitializeCaptchaAction extends AbstractAction {
private final CasConfigurationProperties casProperties;

@Override
protected Event doExecute(final RequestContext requestContext) {
val googleRecaptcha = casProperties.getGoogleRecaptcha();
WebUtils.putRecaptchaPropertiesFlowScope(requestContext, googleRecaptcha);
return new EventFactorySupport().success(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@
@Slf4j
@RequiredArgsConstructor
public class ValidateCaptchaAction extends AbstractAction {
/**
* Captcha error event.
*/
public static final String EVENT_ID_ERROR = "captchaError";

private final GoogleRecaptchaProperties recaptchaProperties;

@Override
Expand All @@ -51,7 +46,11 @@ protected Event doExecute(final RequestContext requestContext) {

private Event getError(final RequestContext requestContext) {
val messageContext = requestContext.getMessageContext();
messageContext.addMessage(new MessageBuilder().error().code(EVENT_ID_ERROR).defaultText(EVENT_ID_ERROR).build());
return getEventFactorySupport().event(this, EVENT_ID_ERROR);
messageContext.addMessage(new MessageBuilder()
.error()
.code(CasWebflowConstants.TRANSITION_ID_CAPTCHA_ERROR)
.defaultText(CasWebflowConstants.TRANSITION_ID_CAPTCHA_ERROR)
.build());
return getEventFactorySupport().event(this, CasWebflowConstants.TRANSITION_ID_CAPTCHA_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.apereo.cas.web.flow.CasCaptchaWebflowConfigurer;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.apereo.cas.web.flow.CasWebflowExecutionPlanConfigurer;
import org.apereo.cas.web.flow.InitializeCaptchaAction;
import org.apereo.cas.web.flow.ValidateCaptchaAction;

import org.springframework.beans.factory.ObjectProvider;
Expand Down Expand Up @@ -61,6 +62,13 @@ public Action validateCaptchaAction() {
return new ValidateCaptchaAction(casProperties.getGoogleRecaptcha());
}

@RefreshScope
@Bean
@ConditionalOnMissingBean(name = "initializeCaptchaAction")
public Action initializeCaptchaAction() {
return new InitializeCaptchaAction(casProperties);
}

@Bean
@ConditionalOnMissingBean(name = "captchaCasWebflowExecutionPlanConfigurer")
public CasWebflowExecutionPlanConfigurer captchaCasWebflowExecutionPlanConfigurer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ public void verifyOperation() {
assertTrue(Arrays.stream(state.getActionList().toArray())
.filter(r -> r instanceof EvaluateAction)
.map(EvaluateAction.class::cast)
.anyMatch(r -> r.toString().contains(CasCaptchaWebflowConfigurer.ACTION_ID_VALIDATE_CAPTCHA)));
.anyMatch(r -> r.toString().contains(CasWebflowConstants.ACTION_ID_VALIDATE_CAPTCHA)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public void verifyCaptchaFails() {

val result = validateAction.execute(context);
assertNotNull(result);
assertEquals(ValidateCaptchaAction.EVENT_ID_ERROR, result.getId());
assertEquals(CasWebflowConstants.TRANSITION_ID_CAPTCHA_ERROR, result.getId());
} catch (final Exception e) {
throw new AssertionError(e.getMessage(), e);
}
Expand Down
1 change: 1 addition & 0 deletions support/cas-server-support-pm-webflow/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies {
implementation project(":core:cas-server-core-tickets-api")
implementation project(":core:cas-server-core-services-authentication")

implementation project(":support:cas-server-support-captcha-core")
implementation project(":support:cas-server-support-pm")
implementation project(":support:cas-server-support-pm-core")
implementation project(":support:cas-server-support-token-core-api")
Expand Down
Loading

0 comments on commit c5d0f2a

Please sign in to comment.