Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KEYCLOAK-11746] More unit tests: browser flow #18

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import java.util.List;

public class ConditionalBlockRoleAuthenticatorFactory implements ConditionalBlockAuthenticatorFactory {
private static final String PROVIDER_ID = "conditional-user-role";
public static final String PROVIDER_ID = "conditional-user-role";
protected static final String CONDITIONAL_USER_ROLE = "condUserRole";

private static List<ProviderConfigProperty> commonConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import org.keycloak.provider.ProviderConfigProperty;

public class ConditionalBlockUserConfiguredAuthenticatorFactory implements ConditionalBlockAuthenticatorFactory {
private static final String PROVIDER_ID = "conditional-user-configured";
public static final String PROVIDER_ID = "conditional-user-configured";
protected static final String CONDITIONAL_USER_ROLE = "condUserConfigured";

@Override
Expand Down
2 changes: 1 addition & 1 deletion testsuite/integration-arquillian/HOW-TO-RUN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ How To Run various testsuite configurations

## Base steps

It's recomended to build the workspace including distribution.
It's recommended to build the workspace including distribution.


cd $KEYCLOAK_SOURCES
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
package org.keycloak.testsuite.broker;

import java.util.List;

import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.authentication.authenticators.broker.IdpAutoLinkAuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.OTPFormAuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.PasswordFormFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationExecutionModel.Requirement;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.resources.admin.AuthenticationManagementResource;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.pages.PasswordPage;
import org.keycloak.testsuite.runonserver.RunOnServer;
import org.keycloak.testsuite.util.FlowUtil;
import org.keycloak.testsuite.util.UserBuilder;

import static org.junit.Assert.assertEquals;
Expand All @@ -33,7 +27,7 @@
*
* Especially for re-authentication of user, which is linking to IDP broker, it uses "Password Form" authenticator instead of default IdpUsernamePasswordForm.
* It tests various variants with OTP( Conditional OTP, Password-or-OTP) .
*
* <p>
* TODO: in latest master, the KcOidcBrokerTest is final class. This class will need to be changed to extend from AbstractBrokerTest
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
Expand Down Expand Up @@ -147,7 +141,7 @@ public void testReAuthenticateWithPasswordOrOTP_otpConfigured_passwordUsed() {

// Create user and link him with TOTP
String consumerRealmUserId = createUser("consumer");
String totpSecret = addTOTPToUser("consumer");
addTOTPToUser("consumer");

loginWithBrokerAndConfirmLinkAccount();

Expand Down Expand Up @@ -243,7 +237,6 @@ public void testBackButtonWithOTPEnabled() {
}



// Add OTP to the user. Return TOTP secret
private String addTOTPToUser(String username) {

Expand Down Expand Up @@ -292,116 +285,43 @@ private void assertUserAuthenticatedInConsumer(String consumerRealmUserId) {
// Configure the variant of firstBrokerLogin flow, which will use PasswordForm instead of IdpUsernamePasswordForm.
// In other words, the form with password-only instead of username/password.
private static RunOnServer configureBrokerFlowToReAuthenticationWithPasswordForm(String idpAlias, String newFlowAlias) {
return (session -> {
// Copy existing firstBrokerLogin flow
RealmModel appRealm = session.getContext().getRealm();
AuthenticationFlowModel existingFBLFlow = appRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW);

AuthenticationFlowModel newFBLFlow = AuthenticationManagementResource.copyFlow(appRealm, existingFBLFlow, newFlowAlias);

//
AuthenticationFlowModel reauthenticateSubflow = appRealm.getFlowByAlias(newFlowAlias + " Verify Existing Account by Re-authentication");
List<AuthenticationExecutionModel> executions = appRealm.getAuthenticationExecutions(reauthenticateSubflow.getId());

// Remove first execution (IdpUsernamePasswordForm)
appRealm.removeAuthenticatorExecution(executions.get(0));

// Increase priority of the second execution (Conditional OTP Subflow)
executions.get(1).setPriority(30);
appRealm.updateAuthenticatorExecution(executions.get(1));

// Add AutoLink Authenticator as first (It will automatically setup user to authentication context)
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticatorFlow(false);
execution.setAuthenticator(IdpAutoLinkAuthenticatorFactory.PROVIDER_ID);
execution.setPriority(10);
execution.setParentFlow(reauthenticateSubflow.getId());
execution = appRealm.addAuthenticatorExecution(execution);

// Add PasswordForm execution
execution = new AuthenticationExecutionModel();
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticatorFlow(false);
execution.setAuthenticator(PasswordFormFactory.PROVIDER_ID);
execution.setPriority(20);
execution.setParentFlow(reauthenticateSubflow.getId());
execution = appRealm.addAuthenticatorExecution(execution);

// Setup new FirstBrokerLogin to identity provider
IdentityProviderModel idp = appRealm.getIdentityProviderByAlias(idpAlias);
idp.setFirstBrokerLoginFlowId(newFBLFlow.getId());
appRealm.updateIdentityProvider(idp);
});
return session -> FlowUtil.inCurrentRealm(session)
.copyFirstBrokerLoginFlow(newFlowAlias)
.inVerifyExistingAccountByReAuthentication(subFlow -> subFlow
// Remove first execution (IdpUsernamePasswordForm)
.removeExecution(0)
// Edit new first execution (Conditional OTP Subflow)
.updateExecution(0, exec -> exec.setPriority(30))
// Add AutoLink Authenticator as first (It will automatically setup user to authentication context)
.addAuthenticatorExecution(Requirement.REQUIRED, IdpAutoLinkAuthenticatorFactory.PROVIDER_ID, 10)
// Add PasswordForm execution
.addAuthenticatorExecution(Requirement.REQUIRED, PasswordFormFactory.PROVIDER_ID, 20)
)
.usesInIdentityProvider(idpAlias);
}


// Configure the variant of firstBrokerLogin flow, which will allow to reauthenticate user with password OR totp
// TOTP will be available just if configured for the user
private static RunOnServer configureBrokerFlowToReAuthenticationWithPasswordOrTotp(String idpAlias, String newFlowAlias) {
return (session -> {
// Copy existing firstBrokerLogin flow
RealmModel appRealm = session.getContext().getRealm();
AuthenticationFlowModel existingFBLFlow = appRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW);

AuthenticationFlowModel newFBLFlow = AuthenticationManagementResource.copyFlow(appRealm, existingFBLFlow, newFlowAlias);

//
AuthenticationFlowModel reauthenticateSubflow = appRealm.getFlowByAlias(newFlowAlias + " Verify Existing Account by Re-authentication");
List<AuthenticationExecutionModel> executions = appRealm.getAuthenticationExecutions(reauthenticateSubflow.getId());

// Remove both executions (IdpUsernamePasswordForm and Conditional OTP subflow)
appRealm.removeAuthenticatorExecution(executions.get(0));
appRealm.removeAuthenticatorExecution(executions.get(1));

// Add AutoLink Authenticator as first (It will automatically setup user to authentication context)
AuthenticationExecutionModel execution1 = new AuthenticationExecutionModel();
execution1.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution1.setAuthenticatorFlow(false);
execution1.setAuthenticator(IdpAutoLinkAuthenticatorFactory.PROVIDER_ID);
execution1.setPriority(10);
execution1.setParentFlow(reauthenticateSubflow.getId());
execution1 = appRealm.addAuthenticatorExecution(execution1);

// Add "Password-or-OTP" subflow
AuthenticationFlowModel passwordOrOtpFlow = new AuthenticationFlowModel();
passwordOrOtpFlow.setTopLevel(false);
passwordOrOtpFlow.setBuiltIn(true);
passwordOrOtpFlow.setAlias("password or otp");
passwordOrOtpFlow.setDescription("Flow to authenticate user with password or otp");
passwordOrOtpFlow.setProviderId("basic-flow");
passwordOrOtpFlow = appRealm.addAuthenticationFlow(passwordOrOtpFlow);
AuthenticationExecutionModel execution2 = new AuthenticationExecutionModel();
execution2.setParentFlow(reauthenticateSubflow.getId());
execution2.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution2.setFlowId(passwordOrOtpFlow.getId());
execution2.setPriority(20);
execution2.setAuthenticatorFlow(true);
appRealm.addAuthenticatorExecution(execution2);

// Add PasswordForm ALTERNATIVE execution
AuthenticationExecutionModel execution21 = new AuthenticationExecutionModel();
execution21.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
execution21.setAuthenticatorFlow(false);
execution21.setAuthenticator(PasswordFormFactory.PROVIDER_ID);
execution21.setPriority(10);
execution21.setParentFlow(passwordOrOtpFlow.getId());
execution21 = appRealm.addAuthenticatorExecution(execution21);

// Add OTPForm ALTERNATIVE execution
AuthenticationExecutionModel execution22 = new AuthenticationExecutionModel();
execution22.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
execution22.setAuthenticatorFlow(false);
execution22.setAuthenticator(OTPFormAuthenticatorFactory.PROVIDER_ID);
execution22.setPriority(20);
execution22.setParentFlow(passwordOrOtpFlow.getId());
execution22 = appRealm.addAuthenticatorExecution(execution22);

// Setup new FirstBrokerLogin to identity provider
IdentityProviderModel idp = appRealm.getIdentityProviderByAlias(idpAlias);
idp.setFirstBrokerLoginFlowId(newFBLFlow.getId());
appRealm.updateIdentityProvider(idp);
});
return session -> {
AuthenticationFlowModel flowModel = FlowUtil.createFlowModel("password or otp", "basic-flow", "Flow to authenticate user with password or otp", false, true);
FlowUtil.inCurrentRealm(session)
// Copy existing firstBrokerLogin flow
.copyFirstBrokerLoginFlow(newFlowAlias)
.inVerifyExistingAccountByReAuthentication(flowUtil -> flowUtil
.clear()
// Add AutoLink Authenticator as first (It will automatically setup user to authentication context)
.addAuthenticatorExecution(Requirement.REQUIRED, IdpAutoLinkAuthenticatorFactory.PROVIDER_ID)
// Add "Password-or-OTP" subflow
.addSubFlowExecution(flowModel, Requirement.REQUIRED, subFlow -> subFlow
// Add PasswordForm ALTERNATIVE execution
.addAuthenticatorExecution(Requirement.ALTERNATIVE, PasswordFormFactory.PROVIDER_ID)
// Add OTPForm ALTERNATIVE execution
.addAuthenticatorExecution(Requirement.ALTERNATIVE, OTPFormAuthenticatorFactory.PROVIDER_ID)
)
)
// Setup new FirstBrokerLogin to identity provider
.usesInIdentityProvider(idpAlias);
};
}

}
Loading