Merge pull request #1527 from patriot1burke/master

binding custom flows
This commit is contained in:
Bill Burke 2015-08-07 21:26:46 -04:00
commit 75793a36cf
31 changed files with 1074 additions and 32 deletions

View file

@ -43,6 +43,15 @@
<column name="OTP_POLICY_TYPE" type="VARCHAR(36)" defaultValue="totp">
<constraints nullable="true"/>
</column>
<column name="BROWSER_FLOW" type="VARCHAR(36)">
<constraints nullable="true"/>
</column>
<column name="REGISTRATION_FLOW" type="VARCHAR(36)">
<constraints nullable="true"/>
</column>
<column name="DIRECT_GRANT_FLOW" type="VARCHAR(36)">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>

View file

@ -87,6 +87,9 @@ public class RealmRepresentation {
protected List<AuthenticationFlowRepresentation> authenticationFlows;
protected List<AuthenticatorConfigRepresentation> authenticatorConfig;
protected List<RequiredActionProviderRepresentation> requiredActions;
protected String browserFlow;
protected String registrationFlow;
protected String directGrantFlow;
@Deprecated
protected Boolean social;
@ -708,4 +711,28 @@ public class RealmRepresentation {
public void setOtpPolicyPeriod(Integer otpPolicyPeriod) {
this.otpPolicyPeriod = otpPolicyPeriod;
}
public String getBrowserFlow() {
return browserFlow;
}
public void setBrowserFlow(String browserFlow) {
this.browserFlow = browserFlow;
}
public String getRegistrationFlow() {
return registrationFlow;
}
public void setRegistrationFlow(String registrationFlow) {
this.registrationFlow = registrationFlow;
}
public String getDirectGrantFlow() {
return directGrantFlow;
}
public void setDirectGrantFlow(String directGrantFlow) {
this.directGrantFlow = directGrantFlow;
}
}

View file

@ -1087,6 +1087,21 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'AuthenticationFlowsCtrl'
})
.when('/realms/:realm/authentication/flow-bindings', {
templateUrl : resourceUrl + '/partials/authentication-flow-bindings.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
flows : function(AuthenticationFlowsLoader) {
return AuthenticationFlowsLoader();
},
serverInfo : function(ServerInfo) {
return ServerInfo.delay;
}
},
controller : 'RealmFlowBindingCtrl'
})
.when('/realms/:realm/authentication/flows/:flow', {
templateUrl : resourceUrl + '/partials/authentication-flows.html',
resolve : {

View file

@ -376,6 +376,7 @@ module.controller('RealmOtpPolicyCtrl', function($scope, Current, Realm, realm,
genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, "/realms/" + realm.realm + "/authentication/otp-policy");
});
module.controller('RealmThemeCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications) {
genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, "/realms/" + realm.realm + "/theme-settings");
@ -1620,6 +1621,13 @@ module.controller('IdentityProviderMapperCreateCtrl', function($scope, realm, id
});
module.controller('RealmFlowBindingCtrl', function($scope, flows, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications) {
$scope.flows = flows;
genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, "/realms/" + realm.realm + "/authentication/flow-bindings");
});
module.controller('CreateFlowCtrl', function($scope, realm,
AuthenticationFlows,
Notifications, $location) {
@ -1770,8 +1778,9 @@ module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flo
};
$scope.removeFlow = function() {
AuthenticationFlows.remove({realm: realm, flow: flow.id}, function() {
$route.reload();
console.log('Remove flow:' + $scope.flow.alias);
AuthenticationFlows.remove({realm: realm.realm, flow: $scope.flow.id}, function() {
$location.url("/realms/" + realm.realm + '/authentication/flows');
Notifications.success("Flow removed");
})

View file

@ -0,0 +1,49 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>Authentication</h1>
<kc-tabs-authentication></kc-tabs-authentication>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<div class="form-group">
<label for="browser" class="col-md-2 control-label">Browser Flow</label>
<div class="col-md-2">
<div>
<select id="browser" ng-model="realm.browserFlow" class="form-control" ng-options="flow.alias as flow.alias for flow in flows">
</select>
</div>
</div>
<kc-tooltip>Select the flow you want to use for browser authentication.</kc-tooltip>
</div>
<div class="form-group">
<label for="registration" class="col-md-2 control-label">Registration Flow</label>
<div class="col-md-2">
<div>
<select id="registration" ng-model="realm.registrationFlow" class="form-control" ng-options="flow.alias as flow.alias for flow in flows">
</select>
</div>
</div>
<kc-tooltip>Select the flow you want to use for registration.</kc-tooltip>
</div>
<div class="form-group">
<label for="grant" class="col-md-2 control-label">Direct Grant Flow</label>
<div class="col-md-2">
<div>
<select id="grant" ng-model="realm.directGrantFlow" class="form-control" ng-options="flow.alias as flow.alias for flow in flows">
</select>
</div>
</div>
<kc-tooltip>Select the flow you want to use for direct grant authentication.</kc-tooltip>
</div>
<div class="form-group" data-ng-show="access.manageRealm">
<div class="col-md-12">
<button kc-save data-ng-disabled="!changed">Save</button>
<button kc-reset data-ng-disabled="!changed">Cancel</button>
</div>
</div>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -1,5 +1,6 @@
<ul class="nav nav-tabs">
<li ng-class="{active: path[3] == 'flows'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/authentication/flows">Flows</a></li>
<li ng-class="{active: path[3] == 'flow-bindings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/authentication/flow-bindings">Bindings</a></li>
<li ng-class="{active: path[3] == 'required-actions'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/authentication/required-actions">Required Actions</a></li>
<li ng-class="{active: path[3] == 'password-policy'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/authentication/password-policy">Password Policy</a></li>
<li ng-class="{active: path[3] == 'otp-policy'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/authentication/otp-policy">OTP Policy</a></li>

View file

@ -24,7 +24,10 @@ public class MigrateTo1_5_0 {
public void migrate(KeycloakSession session) {
List<RealmModel> realms = session.realms().getRealms();
for (RealmModel realm : realms) {
realm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY);
realm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY);
realm.setBrowserFlow(realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW));
realm.setRegistrationFlow(realm.getFlowByAlias(DefaultAuthenticationFlows.REGISTRATION_FLOW));
realm.setDirectGrantFlow(realm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW));
}
}

View file

@ -182,6 +182,15 @@ public interface RealmModel extends RoleContainerModel {
void setSmtpConfig(Map<String, String> smtpConfig);
AuthenticationFlowModel getBrowserFlow();
void setBrowserFlow(AuthenticationFlowModel flow);
AuthenticationFlowModel getRegistrationFlow();
void setRegistrationFlow(AuthenticationFlowModel flow);
AuthenticationFlowModel getDirectGrantFlow();
void setDirectGrantFlow(AuthenticationFlowModel flow);
List<AuthenticationFlowModel> getAuthenticationFlows();
AuthenticationFlowModel getFlowByAlias(String alias);
AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model);

View file

@ -86,6 +86,9 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
private List<AuthenticatorConfigEntity> authenticatorConfigs = new ArrayList<>();
private List<RequiredActionProviderEntity> requiredActionProviders = new ArrayList<>();
private String browserFlow;
private String registrationFlow;
private String directGrantFlow;
public String getName() {
@ -566,6 +569,30 @@ public class RealmEntity extends AbstractIdentifiableEntity {
public void setOtpPolicyPeriod(int otpPolicyPeriod) {
this.otpPolicyPeriod = otpPolicyPeriod;
}
public String getBrowserFlow() {
return browserFlow;
}
public void setBrowserFlow(String browserFlow) {
this.browserFlow = browserFlow;
}
public String getRegistrationFlow() {
return registrationFlow;
}
public void setRegistrationFlow(String registrationFlow) {
this.registrationFlow = registrationFlow;
}
public String getDirectGrantFlow() {
return directGrantFlow;
}
public void setDirectGrantFlow(String directGrantFlow) {
this.directGrantFlow = directGrantFlow;
}
}

View file

@ -39,6 +39,7 @@ public class DefaultAuthenticationFlows {
registrationFlow.setTopLevel(true);
registrationFlow.setBuiltIn(true);
registrationFlow = realm.addAuthenticationFlow(registrationFlow);
realm.setRegistrationFlow(registrationFlow);
AuthenticationFlowModel registrationFormFlow = new AuthenticationFlowModel();
registrationFormFlow.setAlias(REGISTRATION_FORM_FLOW);
@ -125,6 +126,7 @@ public class DefaultAuthenticationFlows {
grant.setTopLevel(true);
grant.setBuiltIn(true);
grant = realm.addAuthenticationFlow(grant);
realm.setDirectGrantFlow(grant);
// username
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
@ -171,6 +173,8 @@ public class DefaultAuthenticationFlows {
browser.setTopLevel(true);
browser.setBuiltIn(true);
browser = realm.addAuthenticationFlow(browser);
realm.setBrowserFlow(browser);
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setParentFlow(browser.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);

View file

@ -160,6 +160,9 @@ public class ModelToRepresentation {
rep.setOtpPolicyInitialCounter(otpPolicy.getInitialCounter());
rep.setOtpPolicyType(otpPolicy.getType());
rep.setOtpPolicyLookAheadWindow(otpPolicy.getLookAheadWindow());
if (realm.getBrowserFlow() != null) rep.setBrowserFlow(realm.getBrowserFlow().getAlias());
if (realm.getRegistrationFlow() != null) rep.setRegistrationFlow(realm.getRegistrationFlow().getAlias());
if (realm.getDirectGrantFlow() != null) rep.setDirectGrantFlow(realm.getDirectGrantFlow().getAlias());
List<String> defaultRoles = realm.getDefaultRoles();
if (!defaultRoles.isEmpty()) {

View file

@ -343,7 +343,22 @@ public class RepresentationToModel {
newRealm.addAuthenticatorExecution(execution);
}
}
}
}
if (rep.getBrowserFlow() == null) {
newRealm.setBrowserFlow(newRealm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW));
} else {
newRealm.setBrowserFlow(newRealm.getFlowByAlias(rep.getBrowserFlow()));
}
if (rep.getRegistrationFlow() == null) {
newRealm.setRegistrationFlow(newRealm.getFlowByAlias(DefaultAuthenticationFlows.REGISTRATION_FLOW));
} else {
newRealm.setRegistrationFlow(newRealm.getFlowByAlias(rep.getRegistrationFlow()));
}
if (rep.getDirectGrantFlow() == null) {
newRealm.setDirectGrantFlow(newRealm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW));
} else {
newRealm.setDirectGrantFlow(newRealm.getFlowByAlias(rep.getDirectGrantFlow()));
}
}
@ -542,6 +557,15 @@ public class RepresentationToModel {
if(rep.getDefaultLocale() != null){
realm.setDefaultLocale(rep.getDefaultLocale());
}
if (rep.getBrowserFlow() != null) {
realm.setBrowserFlow(realm.getFlowByAlias(rep.getBrowserFlow()));
}
if (rep.getRegistrationFlow() != null) {
realm.setRegistrationFlow(realm.getFlowByAlias(rep.getRegistrationFlow()));
}
if (rep.getDirectGrantFlow() != null) {
realm.setDirectGrantFlow(realm.getFlowByAlias(rep.getDirectGrantFlow()));
}
}
// Basic realm stuff

View file

@ -1230,6 +1230,46 @@ public class RealmAdapter implements RealmModel {
return mapping;
}
@Override
public AuthenticationFlowModel getBrowserFlow() {
String flowId = realm.getBrowserFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setBrowserFlow(AuthenticationFlowModel flow) {
realm.setBrowserFlow(flow.getId());
}
@Override
public AuthenticationFlowModel getRegistrationFlow() {
String flowId = realm.getRegistrationFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setRegistrationFlow(AuthenticationFlowModel flow) {
realm.setRegistrationFlow(flow.getId());
}
@Override
public AuthenticationFlowModel getDirectGrantFlow() {
String flowId = realm.getDirectGrantFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setDirectGrantFlow(AuthenticationFlowModel flow) {
realm.setDirectGrantFlow(flow.getId());
}
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();

View file

@ -1031,6 +1031,45 @@ public class RealmAdapter implements RealmModel {
return null;
}
@Override
public AuthenticationFlowModel getBrowserFlow() {
if (updated != null) return updated.getBrowserFlow();
return cached.getBrowserFlow();
}
@Override
public void setBrowserFlow(AuthenticationFlowModel flow) {
getDelegateForUpdate();
updated.setBrowserFlow(flow);
}
@Override
public AuthenticationFlowModel getRegistrationFlow() {
if (updated != null) return updated.getRegistrationFlow();
return cached.getRegistrationFlow();
}
@Override
public void setRegistrationFlow(AuthenticationFlowModel flow) {
getDelegateForUpdate();
updated.setRegistrationFlow(flow);
}
@Override
public AuthenticationFlowModel getDirectGrantFlow() {
if (updated != null) return updated.getDirectGrantFlow();
return cached.getDirectGrantFlow();
}
@Override
public void setDirectGrantFlow(AuthenticationFlowModel flow) {
getDelegateForUpdate();
updated.setDirectGrantFlow(flow);
}
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
if (updated != null) return updated.getAuthenticationFlows();

View file

@ -91,6 +91,10 @@ public class CachedRealm implements Serializable {
private MultivaluedHashMap<String, AuthenticationExecutionModel> authenticationExecutions = new MultivaluedHashMap<>();
private Map<String, AuthenticationExecutionModel> executionsById = new HashMap<>();
private AuthenticationFlowModel browserFlow;
private AuthenticationFlowModel registrationFlow;
private AuthenticationFlowModel directGrantFlow;
private boolean eventsEnabled;
private long eventsExpiration;
private Set<String> eventsListeners = new HashSet<String>();
@ -214,6 +218,10 @@ public class CachedRealm implements Serializable {
requiredActionProvidersByAlias.put(action.getAlias(), action);
}
browserFlow = model.getBrowserFlow();
registrationFlow = model.getRegistrationFlow();
directGrantFlow = model.getDirectGrantFlow();
}
@ -463,4 +471,16 @@ public class CachedRealm implements Serializable {
public OTPPolicy getOtpPolicy() {
return otpPolicy;
}
public AuthenticationFlowModel getBrowserFlow() {
return browserFlow;
}
public AuthenticationFlowModel getRegistrationFlow() {
return registrationFlow;
}
public AuthenticationFlowModel getDirectGrantFlow() {
return directGrantFlow;
}
}

View file

@ -1541,6 +1541,45 @@ public class RealmAdapter implements RealmModel {
return mapper;
}
@Override
public AuthenticationFlowModel getBrowserFlow() {
String flowId = realm.getBrowserFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setBrowserFlow(AuthenticationFlowModel flow) {
realm.setBrowserFlow(flow.getId());
}
@Override
public AuthenticationFlowModel getRegistrationFlow() {
String flowId = realm.getRegistrationFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setRegistrationFlow(AuthenticationFlowModel flow) {
realm.setRegistrationFlow(flow.getId());
}
@Override
public AuthenticationFlowModel getDirectGrantFlow() {
String flowId = realm.getDirectGrantFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setDirectGrantFlow(AuthenticationFlowModel flow) {
realm.setDirectGrantFlow(flow.getId());
}
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
TypedQuery<AuthenticationFlowEntity> query = em.createNamedQuery("getAuthenticationFlowsByRealm", AuthenticationFlowEntity.class);

View file

@ -179,6 +179,17 @@ public class RealmEntity {
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
@Column(name="BROWSER_FLOW")
protected String browserFlow;
@Column(name="REGISTRATION_FLOW")
protected String registrationFlow;
@Column(name="DIRECT_GRANT_FLOW")
protected String directGrantFlow;
@Column(name="INTERNATIONALIZATION_ENABLED")
@ -643,5 +654,29 @@ public class RealmEntity {
public void setOtpPolicyPeriod(int otpPolicyPeriod) {
this.otpPolicyPeriod = otpPolicyPeriod;
}
public String getBrowserFlow() {
return browserFlow;
}
public void setBrowserFlow(String browserFlow) {
this.browserFlow = browserFlow;
}
public String getRegistrationFlow() {
return registrationFlow;
}
public void setRegistrationFlow(String registrationFlow) {
this.registrationFlow = registrationFlow;
}
public String getDirectGrantFlow() {
return directGrantFlow;
}
public void setDirectGrantFlow(String directGrantFlow) {
this.directGrantFlow = directGrantFlow;
}
}

View file

@ -1309,6 +1309,49 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
return mapping;
}
@Override
public AuthenticationFlowModel getBrowserFlow() {
String flowId = realm.getBrowserFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setBrowserFlow(AuthenticationFlowModel flow) {
realm.setBrowserFlow(flow.getId());
updateRealm();
}
@Override
public AuthenticationFlowModel getRegistrationFlow() {
String flowId = realm.getRegistrationFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setRegistrationFlow(AuthenticationFlowModel flow) {
realm.setRegistrationFlow(flow.getId());
updateRealm();
}
@Override
public AuthenticationFlowModel getDirectGrantFlow() {
String flowId = realm.getDirectGrantFlow();
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setDirectGrantFlow(AuthenticationFlowModel flow) {
realm.setDirectGrantFlow(flow.getId());
updateRealm();
}
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();

View file

@ -501,7 +501,7 @@ public class SamlService {
return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode() );
}
}
AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
AuthenticationFlowModel flow = realm.getBrowserFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)

View file

@ -6,12 +6,36 @@ import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
* This interface is for users that want to add custom authenticators to an authentication flow.
* You must implement this interface as well as an AuthenticatorFactory.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface Authenticator extends Provider {
/**
* Initial call for the authenticator. If this is a form, a challenge with a Response rendering the form is usually sent
*
* @param context
*/
void authenticate(AuthenticatorContext context);
/**
* Does this authenticator require that the user has already been identified? That AuthenticatorContext.getUser() is not null?
*
* @return
*/
boolean requiresUser();
/**
* Is this authenticator configured for this user.
*
* @param session
* @param realm
* @param user
* @return
*/
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
/**
@ -20,6 +44,11 @@ public interface Authenticator extends Provider {
*/
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
/**
* Usually implements a form action.
*
* @param context
*/
void action(AuthenticatorContext context);

View file

@ -6,6 +6,12 @@ import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.ProviderFactory;
/**
* Factory for creating Authenticator instances
*
* You must specify a file
* META-INF/services/org.keycloak.authentication.AuthenticatorFactory in the jar that this class is contained in
* This file must have the fully qualified class name of all your AuthentitoryFactory classes
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/

View file

@ -8,6 +8,11 @@ import org.keycloak.provider.ConfiguredProvider;
* @version $Revision: 1 $
*/
public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
/**
* Friendly name for the authenticator
*
* @return
*/
String getDisplayType();
/**
@ -17,6 +22,11 @@ public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
*/
String getReferenceCategory();
/**
* Is this authenticator configurable?
*
* @return
*/
boolean isConfigurable();
/**

View file

@ -263,7 +263,7 @@ public class AuthorizationEndpoint {
clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE);
AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
AuthenticationFlowModel flow = realm.getBrowserFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)

View file

@ -331,7 +331,7 @@ public class TokenEndpoint {
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW);
AuthenticationFlowModel flow = realm.getDirectGrantFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)

View file

@ -469,7 +469,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
protected Response browserAuthentication(ClientSessionModel clientSession, String errorMessage) {
this.event.event(EventType.LOGIN);
AuthenticationFlowModel flow = realmModel.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
AuthenticationFlowModel flow = realmModel.getBrowserFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)

View file

@ -152,7 +152,7 @@ public class LoginActionsService {
ClientSessionCode clientCode;
Response response;
boolean verifyCode(String flow, String code, String requiredAction) {
boolean verifyCode(AuthenticationFlowModel flow, String code, String requiredAction) {
if (!verifyCode(flow, code)) {
return false;
} else if (!clientCode.isValidAction(requiredAction)) {
@ -175,7 +175,7 @@ public class LoginActionsService {
}
}
boolean verifyCode(String flow, String code, String requiredAction, String alternativeRequiredAction) {
boolean verifyCode(AuthenticationFlowModel flow, String code, String requiredAction, String alternativeRequiredAction) {
if (!verifyCode(flow, code)) {
return false;
} else if (!(clientCode.isValidAction(requiredAction) || clientCode.isValidAction(alternativeRequiredAction))) {
@ -201,7 +201,7 @@ public class LoginActionsService {
}
}
public boolean verifyCode(String flow, String code) {
public boolean verifyCode(AuthenticationFlowModel flow, String code) {
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
response = ErrorPage.error(session, Messages.HTTPS_REQUIRED);
@ -268,7 +268,7 @@ public class LoginActionsService {
@QueryParam("execution") String execution) {
event.event(EventType.LOGIN);
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
return checks.response;
}
event.detail(Details.CODE_ID, code);
@ -284,12 +284,10 @@ public class LoginActionsService {
}
protected Response processAuthentication(String execution, ClientSessionModel clientSession, String errorMessage) {
String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
return processFlow(execution, clientSession, flowAlias, errorMessage);
return processFlow(execution, clientSession, realm.getBrowserFlow(), errorMessage);
}
protected Response processFlow(String execution, ClientSessionModel clientSession, String flowAlias, String errorMessage) {
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
protected Response processFlow(String execution, ClientSessionModel clientSession, AuthenticationFlowModel flow, String errorMessage) {
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
.setFlowId(flow.getId())
@ -325,7 +323,7 @@ public class LoginActionsService {
@QueryParam("execution") String execution) {
event.event(EventType.LOGIN);
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, ClientSessionModel.Action.AUTHENTICATE.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), code, ClientSessionModel.Action.AUTHENTICATE.name())) {
return checks.response;
}
final ClientSessionCode clientCode = checks.clientCode;
@ -335,8 +333,7 @@ public class LoginActionsService {
}
protected Response processRegistration(String execution, ClientSessionModel clientSession, String errorMessage) {
String flowAlias = DefaultAuthenticationFlows.REGISTRATION_FLOW;
return processFlow(execution, clientSession, flowAlias, errorMessage);
return processFlow(execution, clientSession, realm.getRegistrationFlow(), errorMessage);
}
@ -357,7 +354,7 @@ public class LoginActionsService {
}
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.REGISTRATION_FLOW, code, ClientSessionModel.Action.AUTHENTICATE.name())) {
if (!checks.verifyCode(realm.getRegistrationFlow(), code, ClientSessionModel.Action.AUTHENTICATE.name())) {
return checks.response;
}
event.detail(Details.CODE_ID, code);
@ -383,7 +380,7 @@ public class LoginActionsService {
@QueryParam("execution") String execution) {
event.event(EventType.REGISTER);
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.REGISTRATION_FLOW, code, ClientSessionModel.Action.AUTHENTICATE.name())) {
if (!checks.verifyCode(realm.getRegistrationFlow(), code, ClientSessionModel.Action.AUTHENTICATE.name())) {
return checks.response;
}
if (!realm.isRegistrationAllowed()) {
@ -484,7 +481,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_PROFILE);
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, ClientSessionModel.Action.UPDATE_PROFILE.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), code, ClientSessionModel.Action.UPDATE_PROFILE.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@ -546,7 +543,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_TOTP);
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, ClientSessionModel.Action.CONFIGURE_TOTP.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), code, ClientSessionModel.Action.CONFIGURE_TOTP.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@ -598,7 +595,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_PASSWORD);
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, ClientSessionModel.Action.UPDATE_PASSWORD.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), code, ClientSessionModel.Action.UPDATE_PASSWORD.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@ -661,7 +658,7 @@ public class LoginActionsService {
event.event(EventType.VERIFY_EMAIL);
if (key != null) {
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, key, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), key, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@ -688,7 +685,7 @@ public class LoginActionsService {
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
} else {
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), code, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@ -711,7 +708,7 @@ public class LoginActionsService {
event.event(EventType.RESET_PASSWORD);
if (key != null) {
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
if (!checks.verifyCode(realm.getBrowserFlow(), key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@ -732,7 +729,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) {
event.event(EventType.SEND_RESET_PASSWORD);
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code)) {
if (!checks.verifyCode(realm.getBrowserFlow(), code)) {
return checks.response;
}
final ClientSessionCode accessCode = checks.clientCode;
@ -850,7 +847,7 @@ public class LoginActionsService {
throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
}
Checks checks = new Checks();
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, action)) {
if (!checks.verifyCode(realm.getBrowserFlow(), code, action)) {
return checks.response;
}
final ClientSessionCode clientCode = checks.clientCode;

View file

@ -0,0 +1,208 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite.forms;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.events.Details;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.util.Time;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import java.util.Map;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class CustomFlowTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
user.setEmail("login@test.com");
user.setEnabled(true);
userId = user.getId();
AuthenticationFlowModel flow = new AuthenticationFlowModel();
flow.setAlias("dummy");
flow.setDescription("dummy pass through flow");
flow.setProviderId("basic-flow");
flow.setTopLevel(true);
flow.setBuiltIn(false);
flow = appRealm.addAuthenticationFlow(flow);
appRealm.setBrowserFlow(flow);
appRealm.setDirectGrantFlow(flow);
AuthenticationExecutionModel execution;
execution = new AuthenticationExecutionModel();
execution.setParentFlow(flow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator(PassThroughAuthenticator.PROVIDER_ID);
execution.setPriority(10);
execution.setAuthenticatorFlow(false);
appRealm.addAuthenticatorExecution(execution);
flow = new AuthenticationFlowModel();
flow.setAlias("dummy registration");
flow.setDescription("dummy pass through registration");
flow.setProviderId("basic-flow");
flow.setTopLevel(true);
flow.setBuiltIn(false);
flow = appRealm.addAuthenticationFlow(flow);
appRealm.setRegistrationFlow(flow);
execution = new AuthenticationExecutionModel();
execution.setParentFlow(flow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator(PassThroughRegistration.PROVIDER_ID);
execution.setPriority(10);
execution.setAuthenticatorFlow(false);
appRealm.addAuthenticatorExecution(execution);
}
});
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected OAuthClient oauth;
@WebResource
protected WebDriver driver;
@WebResource
protected AppPage appPage;
@WebResource
protected LoginPage loginPage;
@WebResource
protected ErrorPage errorPage;
@WebResource
protected LoginPasswordUpdatePage updatePasswordPage;
@WebResource
protected RegisterPage registerPage;
private static String userId;
@Test
public void loginSuccess() {
PassThroughAuthenticator.username = "login-test";
oauth.openLoginForm();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
}
@Test
public void grantTest() throws Exception {
PassThroughAuthenticator.username = "login-test";
grantAccessToken("login-test");
}
private void grantAccessToken(String login) throws Exception {
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", login, "password");
assertEquals(200, response.getStatusCode());
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
RefreshToken refreshToken = oauth.verifyRefreshToken(response.getRefreshToken());
events.expectLogin()
.client("test-app")
.user(userId)
.session(accessToken.getSessionState())
.detail(Details.RESPONSE_TYPE, "token")
.detail(Details.TOKEN_ID, accessToken.getId())
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
.detail(Details.USERNAME, login)
.removeDetail(Details.CODE_ID)
.removeDetail(Details.REDIRECT_URI)
.removeDetail(Details.CONSENT)
.assertEvent();
assertEquals(accessToken.getSessionState(), refreshToken.getSessionState());
OAuthClient.AccessTokenResponse refreshedResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
AccessToken refreshedAccessToken = oauth.verifyToken(refreshedResponse.getAccessToken());
RefreshToken refreshedRefreshToken = oauth.verifyRefreshToken(refreshedResponse.getRefreshToken());
assertEquals(accessToken.getSessionState(), refreshedAccessToken.getSessionState());
assertEquals(accessToken.getSessionState(), refreshedRefreshToken.getSessionState());
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState()).user(userId).client("test-app").assertEvent();
}
}

View file

@ -0,0 +1,125 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite.forms;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.events.Details;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
import static org.junit.Assert.assertEquals;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class CustomRegistrationFlowTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
AuthenticationFlowModel flow = new AuthenticationFlowModel();
flow.setAlias("dummy registration");
flow.setDescription("dummy pass through registration");
flow.setProviderId("basic-flow");
flow.setTopLevel(true);
flow.setBuiltIn(false);
flow = appRealm.addAuthenticationFlow(flow);
appRealm.setRegistrationFlow(flow);
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setParentFlow(flow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator(PassThroughRegistration.PROVIDER_ID);
execution.setPriority(10);
execution.setAuthenticatorFlow(false);
appRealm.addAuthenticatorExecution(execution);
}
});
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected OAuthClient oauth;
@WebResource
protected WebDriver driver;
@WebResource
protected AppPage appPage;
@WebResource
protected LoginPage loginPage;
@WebResource
protected ErrorPage errorPage;
@WebResource
protected LoginPasswordUpdatePage updatePasswordPage;
@WebResource
protected RegisterPage registerPage;
private static String userId;
@Test
public void registerUserSuccess() {
loginPage.open();
loginPage.clickRegister();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
String userId = events.expectRegister(PassThroughRegistration.username, PassThroughRegistration.email).assertEvent().getUserId();
events.expectLogin().detail("username", PassThroughRegistration.username).user(userId).assertEvent();
}
}

View file

@ -0,0 +1,125 @@
package org.keycloak.testsuite.forms;
import org.keycloak.Config;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PassThroughAuthenticator implements Authenticator, AuthenticatorFactory {
public static final String PROVIDER_ID = "dummy-passthrough";
public static String username = "test-user@localhost";
@Override
public void authenticate(AuthenticatorContext context) {
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
if (user == null) {
context.failure(AuthenticationProcessor.Error.UNKNOWN_USER);
return;
}
context.setUser(user);
context.success();
}
@Override
public boolean requiresUser() {
return false;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
@Override
public void action(AuthenticatorContext context) {
}
@Override
public Authenticator create() {
return this;
}
@Override
public String getDisplayType() {
return "Dummy Pass Thru";
}
@Override
public String getReferenceCategory() {
return null;
}
@Override
public boolean isConfigurable() {
return false;
}
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED
};
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override
public String getHelpText() {
return "Dummy authenticator. Just passes through and is hardcoded to a specific user";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
@Override
public void close() {
}
@Override
public Authenticator create(KeycloakSession session) {
return this;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -0,0 +1,144 @@
package org.keycloak.testsuite.forms;
import org.keycloak.Config;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.services.resources.AttributeFormDataProcessor;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PassThroughRegistration implements Authenticator, AuthenticatorFactory {
public static final String PROVIDER_ID = "dummy-registration";
public static String username = "new-user@localhost";
public static String email = "new-user@localhost";
@Override
public void authenticate(AuthenticatorContext context) {
context.getEvent().detail(Details.USERNAME, username)
.detail(Details.REGISTER_METHOD, "form")
.detail(Details.EMAIL, email)
;
UserModel user = context.getSession().users().addUser(context.getRealm(), username);
user.setEnabled(true);
user.setEmail(email);
context.getClientSession().setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
context.setUser(user);
context.getEvent().user(user);
context.getEvent().success();
context.newEvent().event(EventType.LOGIN);
context.getEvent().client(context.getClientSession().getClient().getClientId())
.detail(Details.REDIRECT_URI, context.getClientSession().getRedirectUri())
.detail(Details.AUTH_METHOD, context.getClientSession().getAuthMethod());
String authType = context.getClientSession().getNote(Details.AUTH_TYPE);
if (authType != null) {
context.getEvent().detail(Details.AUTH_TYPE, authType);
}
context.success();
}
@Override
public boolean requiresUser() {
return false;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
@Override
public void action(AuthenticatorContext context) {
}
@Override
public Authenticator create() {
return this;
}
@Override
public String getDisplayType() {
return "Dummy Pass Thru";
}
@Override
public String getReferenceCategory() {
return null;
}
@Override
public boolean isConfigurable() {
return false;
}
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED
};
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override
public String getHelpText() {
return "Dummy authenticator. Just passes through and is hardcoded to a specific user";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
@Override
public void close() {
}
@Override
public Authenticator create(KeycloakSession session) {
return this;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -0,0 +1,2 @@
org.keycloak.testsuite.forms.PassThroughAuthenticator
org.keycloak.testsuite.forms.PassThroughRegistration