Merge pull request #1527 from patriot1burke/master
binding custom flows
This commit is contained in:
commit
75793a36cf
31 changed files with 1074 additions and 32 deletions
|
@ -43,6 +43,15 @@
|
||||||
<column name="OTP_POLICY_TYPE" type="VARCHAR(36)" defaultValue="totp">
|
<column name="OTP_POLICY_TYPE" type="VARCHAR(36)" defaultValue="totp">
|
||||||
<constraints nullable="true"/>
|
<constraints nullable="true"/>
|
||||||
</column>
|
</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>
|
</addColumn>
|
||||||
|
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
|
@ -87,6 +87,9 @@ public class RealmRepresentation {
|
||||||
protected List<AuthenticationFlowRepresentation> authenticationFlows;
|
protected List<AuthenticationFlowRepresentation> authenticationFlows;
|
||||||
protected List<AuthenticatorConfigRepresentation> authenticatorConfig;
|
protected List<AuthenticatorConfigRepresentation> authenticatorConfig;
|
||||||
protected List<RequiredActionProviderRepresentation> requiredActions;
|
protected List<RequiredActionProviderRepresentation> requiredActions;
|
||||||
|
protected String browserFlow;
|
||||||
|
protected String registrationFlow;
|
||||||
|
protected String directGrantFlow;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected Boolean social;
|
protected Boolean social;
|
||||||
|
@ -708,4 +711,28 @@ public class RealmRepresentation {
|
||||||
public void setOtpPolicyPeriod(Integer otpPolicyPeriod) {
|
public void setOtpPolicyPeriod(Integer otpPolicyPeriod) {
|
||||||
this.otpPolicyPeriod = 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1087,6 +1087,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'AuthenticationFlowsCtrl'
|
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', {
|
.when('/realms/:realm/authentication/flows/:flow', {
|
||||||
templateUrl : resourceUrl + '/partials/authentication-flows.html',
|
templateUrl : resourceUrl + '/partials/authentication-flows.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
|
|
@ -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");
|
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) {
|
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");
|
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,
|
module.controller('CreateFlowCtrl', function($scope, realm,
|
||||||
AuthenticationFlows,
|
AuthenticationFlows,
|
||||||
Notifications, $location) {
|
Notifications, $location) {
|
||||||
|
@ -1770,8 +1778,9 @@ module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flo
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.removeFlow = function() {
|
$scope.removeFlow = function() {
|
||||||
AuthenticationFlows.remove({realm: realm, flow: flow.id}, function() {
|
console.log('Remove flow:' + $scope.flow.alias);
|
||||||
$route.reload();
|
AuthenticationFlows.remove({realm: realm.realm, flow: $scope.flow.id}, function() {
|
||||||
|
$location.url("/realms/" + realm.realm + '/authentication/flows');
|
||||||
Notifications.success("Flow removed");
|
Notifications.success("Flow removed");
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -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>
|
|
@ -1,5 +1,6 @@
|
||||||
<ul class="nav nav-tabs">
|
<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] == '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] == '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] == '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>
|
<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>
|
||||||
|
|
|
@ -24,7 +24,10 @@ public class MigrateTo1_5_0 {
|
||||||
public void migrate(KeycloakSession session) {
|
public void migrate(KeycloakSession session) {
|
||||||
List<RealmModel> realms = session.realms().getRealms();
|
List<RealmModel> realms = session.realms().getRealms();
|
||||||
for (RealmModel realm : realms) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,15 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
|
|
||||||
void setSmtpConfig(Map<String, String> smtpConfig);
|
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();
|
List<AuthenticationFlowModel> getAuthenticationFlows();
|
||||||
AuthenticationFlowModel getFlowByAlias(String alias);
|
AuthenticationFlowModel getFlowByAlias(String alias);
|
||||||
AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model);
|
AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model);
|
||||||
|
|
|
@ -86,6 +86,9 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
||||||
private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
|
private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
|
||||||
private List<AuthenticatorConfigEntity> authenticatorConfigs = new ArrayList<>();
|
private List<AuthenticatorConfigEntity> authenticatorConfigs = new ArrayList<>();
|
||||||
private List<RequiredActionProviderEntity> requiredActionProviders = new ArrayList<>();
|
private List<RequiredActionProviderEntity> requiredActionProviders = new ArrayList<>();
|
||||||
|
private String browserFlow;
|
||||||
|
private String registrationFlow;
|
||||||
|
private String directGrantFlow;
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -566,6 +569,30 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
||||||
public void setOtpPolicyPeriod(int otpPolicyPeriod) {
|
public void setOtpPolicyPeriod(int otpPolicyPeriod) {
|
||||||
this.otpPolicyPeriod = 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ public class DefaultAuthenticationFlows {
|
||||||
registrationFlow.setTopLevel(true);
|
registrationFlow.setTopLevel(true);
|
||||||
registrationFlow.setBuiltIn(true);
|
registrationFlow.setBuiltIn(true);
|
||||||
registrationFlow = realm.addAuthenticationFlow(registrationFlow);
|
registrationFlow = realm.addAuthenticationFlow(registrationFlow);
|
||||||
|
realm.setRegistrationFlow(registrationFlow);
|
||||||
|
|
||||||
AuthenticationFlowModel registrationFormFlow = new AuthenticationFlowModel();
|
AuthenticationFlowModel registrationFormFlow = new AuthenticationFlowModel();
|
||||||
registrationFormFlow.setAlias(REGISTRATION_FORM_FLOW);
|
registrationFormFlow.setAlias(REGISTRATION_FORM_FLOW);
|
||||||
|
@ -125,6 +126,7 @@ public class DefaultAuthenticationFlows {
|
||||||
grant.setTopLevel(true);
|
grant.setTopLevel(true);
|
||||||
grant.setBuiltIn(true);
|
grant.setBuiltIn(true);
|
||||||
grant = realm.addAuthenticationFlow(grant);
|
grant = realm.addAuthenticationFlow(grant);
|
||||||
|
realm.setDirectGrantFlow(grant);
|
||||||
|
|
||||||
// username
|
// username
|
||||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||||
|
@ -171,6 +173,8 @@ public class DefaultAuthenticationFlows {
|
||||||
browser.setTopLevel(true);
|
browser.setTopLevel(true);
|
||||||
browser.setBuiltIn(true);
|
browser.setBuiltIn(true);
|
||||||
browser = realm.addAuthenticationFlow(browser);
|
browser = realm.addAuthenticationFlow(browser);
|
||||||
|
realm.setBrowserFlow(browser);
|
||||||
|
|
||||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||||
execution.setParentFlow(browser.getId());
|
execution.setParentFlow(browser.getId());
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||||
|
|
|
@ -160,6 +160,9 @@ public class ModelToRepresentation {
|
||||||
rep.setOtpPolicyInitialCounter(otpPolicy.getInitialCounter());
|
rep.setOtpPolicyInitialCounter(otpPolicy.getInitialCounter());
|
||||||
rep.setOtpPolicyType(otpPolicy.getType());
|
rep.setOtpPolicyType(otpPolicy.getType());
|
||||||
rep.setOtpPolicyLookAheadWindow(otpPolicy.getLookAheadWindow());
|
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();
|
List<String> defaultRoles = realm.getDefaultRoles();
|
||||||
if (!defaultRoles.isEmpty()) {
|
if (!defaultRoles.isEmpty()) {
|
||||||
|
|
|
@ -343,7 +343,22 @@ public class RepresentationToModel {
|
||||||
newRealm.addAuthenticatorExecution(execution);
|
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){
|
if(rep.getDefaultLocale() != null){
|
||||||
realm.setDefaultLocale(rep.getDefaultLocale());
|
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
|
// Basic realm stuff
|
||||||
|
|
|
@ -1230,6 +1230,46 @@ public class RealmAdapter implements RealmModel {
|
||||||
return mapping;
|
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
|
@Override
|
||||||
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
||||||
List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
|
List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
|
||||||
|
|
|
@ -1031,6 +1031,45 @@ public class RealmAdapter implements RealmModel {
|
||||||
return null;
|
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
|
@Override
|
||||||
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
||||||
if (updated != null) return updated.getAuthenticationFlows();
|
if (updated != null) return updated.getAuthenticationFlows();
|
||||||
|
|
|
@ -91,6 +91,10 @@ public class CachedRealm implements Serializable {
|
||||||
private MultivaluedHashMap<String, AuthenticationExecutionModel> authenticationExecutions = new MultivaluedHashMap<>();
|
private MultivaluedHashMap<String, AuthenticationExecutionModel> authenticationExecutions = new MultivaluedHashMap<>();
|
||||||
private Map<String, AuthenticationExecutionModel> executionsById = new HashMap<>();
|
private Map<String, AuthenticationExecutionModel> executionsById = new HashMap<>();
|
||||||
|
|
||||||
|
private AuthenticationFlowModel browserFlow;
|
||||||
|
private AuthenticationFlowModel registrationFlow;
|
||||||
|
private AuthenticationFlowModel directGrantFlow;
|
||||||
|
|
||||||
private boolean eventsEnabled;
|
private boolean eventsEnabled;
|
||||||
private long eventsExpiration;
|
private long eventsExpiration;
|
||||||
private Set<String> eventsListeners = new HashSet<String>();
|
private Set<String> eventsListeners = new HashSet<String>();
|
||||||
|
@ -214,6 +218,10 @@ public class CachedRealm implements Serializable {
|
||||||
requiredActionProvidersByAlias.put(action.getAlias(), action);
|
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() {
|
public OTPPolicy getOtpPolicy() {
|
||||||
return otpPolicy;
|
return otpPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationFlowModel getBrowserFlow() {
|
||||||
|
return browserFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationFlowModel getRegistrationFlow() {
|
||||||
|
return registrationFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationFlowModel getDirectGrantFlow() {
|
||||||
|
return directGrantFlow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1541,6 +1541,45 @@ public class RealmAdapter implements RealmModel {
|
||||||
return mapper;
|
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
|
@Override
|
||||||
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
||||||
TypedQuery<AuthenticationFlowEntity> query = em.createNamedQuery("getAuthenticationFlowsByRealm", AuthenticationFlowEntity.class);
|
TypedQuery<AuthenticationFlowEntity> query = em.createNamedQuery("getAuthenticationFlowsByRealm", AuthenticationFlowEntity.class);
|
||||||
|
|
|
@ -179,6 +179,17 @@ public class RealmEntity {
|
||||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||||
Collection<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
|
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")
|
@Column(name="INTERNATIONALIZATION_ENABLED")
|
||||||
|
@ -643,5 +654,29 @@ public class RealmEntity {
|
||||||
public void setOtpPolicyPeriod(int otpPolicyPeriod) {
|
public void setOtpPolicyPeriod(int otpPolicyPeriod) {
|
||||||
this.otpPolicyPeriod = 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1309,6 +1309,49 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
return mapping;
|
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
|
@Override
|
||||||
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
public List<AuthenticationFlowModel> getAuthenticationFlows() {
|
||||||
List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
|
List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
|
||||||
|
|
|
@ -501,7 +501,7 @@ public class SamlService {
|
||||||
return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode() );
|
return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
|
AuthenticationFlowModel flow = realm.getBrowserFlow();
|
||||||
String flowId = flow.getId();
|
String flowId = flow.getId();
|
||||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||||
processor.setClientSession(clientSession)
|
processor.setClientSession(clientSession)
|
||||||
|
|
|
@ -6,12 +6,36 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* This interface is for users that want to add custom authenticators to an authentication flow.
|
||||||
* @version $Revision: 1 $
|
* 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 {
|
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);
|
void authenticate(AuthenticatorContext context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this authenticator require that the user has already been identified? That AuthenticatorContext.getUser() is not null?
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
boolean requiresUser();
|
boolean requiresUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this authenticator configured for this user.
|
||||||
|
*
|
||||||
|
* @param session
|
||||||
|
* @param realm
|
||||||
|
* @param user
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
|
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);
|
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usually implements a form action.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
void action(AuthenticatorContext context);
|
void action(AuthenticatorContext context);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,12 @@ import org.keycloak.provider.ConfiguredProvider;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,6 +8,11 @@ import org.keycloak.provider.ConfiguredProvider;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
|
public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
|
||||||
|
/**
|
||||||
|
* Friendly name for the authenticator
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
String getDisplayType();
|
String getDisplayType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +22,11 @@ public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
|
||||||
*/
|
*/
|
||||||
String getReferenceCategory();
|
String getReferenceCategory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this authenticator configurable?
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
boolean isConfigurable();
|
boolean isConfigurable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -263,7 +263,7 @@ public class AuthorizationEndpoint {
|
||||||
clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE);
|
clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE);
|
||||||
|
|
||||||
|
|
||||||
AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
|
AuthenticationFlowModel flow = realm.getBrowserFlow();
|
||||||
String flowId = flow.getId();
|
String flowId = flow.getId();
|
||||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||||
processor.setClientSession(clientSession)
|
processor.setClientSession(clientSession)
|
||||||
|
|
|
@ -331,7 +331,7 @@ public class TokenEndpoint {
|
||||||
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||||
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
|
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();
|
String flowId = flow.getId();
|
||||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||||
processor.setClientSession(clientSession)
|
processor.setClientSession(clientSession)
|
||||||
|
|
|
@ -469,7 +469,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
|
|
||||||
protected Response browserAuthentication(ClientSessionModel clientSession, String errorMessage) {
|
protected Response browserAuthentication(ClientSessionModel clientSession, String errorMessage) {
|
||||||
this.event.event(EventType.LOGIN);
|
this.event.event(EventType.LOGIN);
|
||||||
AuthenticationFlowModel flow = realmModel.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
|
AuthenticationFlowModel flow = realmModel.getBrowserFlow();
|
||||||
String flowId = flow.getId();
|
String flowId = flow.getId();
|
||||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||||
processor.setClientSession(clientSession)
|
processor.setClientSession(clientSession)
|
||||||
|
|
|
@ -152,7 +152,7 @@ public class LoginActionsService {
|
||||||
ClientSessionCode clientCode;
|
ClientSessionCode clientCode;
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
boolean verifyCode(String flow, String code, String requiredAction) {
|
boolean verifyCode(AuthenticationFlowModel flow, String code, String requiredAction) {
|
||||||
if (!verifyCode(flow, code)) {
|
if (!verifyCode(flow, code)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (!clientCode.isValidAction(requiredAction)) {
|
} 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)) {
|
if (!verifyCode(flow, code)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (!(clientCode.isValidAction(requiredAction) || clientCode.isValidAction(alternativeRequiredAction))) {
|
} 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()) {
|
if (!checkSsl()) {
|
||||||
event.error(Errors.SSL_REQUIRED);
|
event.error(Errors.SSL_REQUIRED);
|
||||||
response = ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
response = ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||||
|
@ -268,7 +268,7 @@ public class LoginActionsService {
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution) {
|
||||||
event.event(EventType.LOGIN);
|
event.event(EventType.LOGIN);
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
event.detail(Details.CODE_ID, code);
|
event.detail(Details.CODE_ID, code);
|
||||||
|
@ -284,12 +284,10 @@ public class LoginActionsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response processAuthentication(String execution, ClientSessionModel clientSession, String errorMessage) {
|
protected Response processAuthentication(String execution, ClientSessionModel clientSession, String errorMessage) {
|
||||||
String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
|
return processFlow(execution, clientSession, realm.getBrowserFlow(), errorMessage);
|
||||||
return processFlow(execution, clientSession, flowAlias, errorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response processFlow(String execution, ClientSessionModel clientSession, String flowAlias, String errorMessage) {
|
protected Response processFlow(String execution, ClientSessionModel clientSession, AuthenticationFlowModel flow, String errorMessage) {
|
||||||
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
|
|
||||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||||
processor.setClientSession(clientSession)
|
processor.setClientSession(clientSession)
|
||||||
.setFlowId(flow.getId())
|
.setFlowId(flow.getId())
|
||||||
|
@ -325,7 +323,7 @@ public class LoginActionsService {
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution) {
|
||||||
event.event(EventType.LOGIN);
|
event.event(EventType.LOGIN);
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
final ClientSessionCode clientCode = checks.clientCode;
|
final ClientSessionCode clientCode = checks.clientCode;
|
||||||
|
@ -335,8 +333,7 @@ public class LoginActionsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response processRegistration(String execution, ClientSessionModel clientSession, String errorMessage) {
|
protected Response processRegistration(String execution, ClientSessionModel clientSession, String errorMessage) {
|
||||||
String flowAlias = DefaultAuthenticationFlows.REGISTRATION_FLOW;
|
return processFlow(execution, clientSession, realm.getRegistrationFlow(), errorMessage);
|
||||||
return processFlow(execution, clientSession, flowAlias, errorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -357,7 +354,7 @@ public class LoginActionsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
event.detail(Details.CODE_ID, code);
|
event.detail(Details.CODE_ID, code);
|
||||||
|
@ -383,7 +380,7 @@ public class LoginActionsService {
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution) {
|
||||||
event.event(EventType.REGISTER);
|
event.event(EventType.REGISTER);
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
if (!realm.isRegistrationAllowed()) {
|
if (!realm.isRegistrationAllowed()) {
|
||||||
|
@ -484,7 +481,7 @@ public class LoginActionsService {
|
||||||
final MultivaluedMap<String, String> formData) {
|
final MultivaluedMap<String, String> formData) {
|
||||||
event.event(EventType.UPDATE_PROFILE);
|
event.event(EventType.UPDATE_PROFILE);
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
ClientSessionCode accessCode = checks.clientCode;
|
ClientSessionCode accessCode = checks.clientCode;
|
||||||
|
@ -546,7 +543,7 @@ public class LoginActionsService {
|
||||||
final MultivaluedMap<String, String> formData) {
|
final MultivaluedMap<String, String> formData) {
|
||||||
event.event(EventType.UPDATE_TOTP);
|
event.event(EventType.UPDATE_TOTP);
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
ClientSessionCode accessCode = checks.clientCode;
|
ClientSessionCode accessCode = checks.clientCode;
|
||||||
|
@ -598,7 +595,7 @@ public class LoginActionsService {
|
||||||
final MultivaluedMap<String, String> formData) {
|
final MultivaluedMap<String, String> formData) {
|
||||||
event.event(EventType.UPDATE_PASSWORD);
|
event.event(EventType.UPDATE_PASSWORD);
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
ClientSessionCode accessCode = checks.clientCode;
|
ClientSessionCode accessCode = checks.clientCode;
|
||||||
|
@ -661,7 +658,7 @@ public class LoginActionsService {
|
||||||
event.event(EventType.VERIFY_EMAIL);
|
event.event(EventType.VERIFY_EMAIL);
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
ClientSessionCode accessCode = checks.clientCode;
|
ClientSessionCode accessCode = checks.clientCode;
|
||||||
|
@ -688,7 +685,7 @@ public class LoginActionsService {
|
||||||
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
|
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
|
||||||
} else {
|
} else {
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
ClientSessionCode accessCode = checks.clientCode;
|
ClientSessionCode accessCode = checks.clientCode;
|
||||||
|
@ -711,7 +708,7 @@ public class LoginActionsService {
|
||||||
event.event(EventType.RESET_PASSWORD);
|
event.event(EventType.RESET_PASSWORD);
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
Checks checks = new Checks();
|
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;
|
return checks.response;
|
||||||
}
|
}
|
||||||
ClientSessionCode accessCode = checks.clientCode;
|
ClientSessionCode accessCode = checks.clientCode;
|
||||||
|
@ -732,7 +729,7 @@ public class LoginActionsService {
|
||||||
final MultivaluedMap<String, String> formData) {
|
final MultivaluedMap<String, String> formData) {
|
||||||
event.event(EventType.SEND_RESET_PASSWORD);
|
event.event(EventType.SEND_RESET_PASSWORD);
|
||||||
Checks checks = new Checks();
|
Checks checks = new Checks();
|
||||||
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code)) {
|
if (!checks.verifyCode(realm.getBrowserFlow(), code)) {
|
||||||
return checks.response;
|
return checks.response;
|
||||||
}
|
}
|
||||||
final ClientSessionCode accessCode = checks.clientCode;
|
final ClientSessionCode accessCode = checks.clientCode;
|
||||||
|
@ -850,7 +847,7 @@ public class LoginActionsService {
|
||||||
throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
|
throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
|
||||||
}
|
}
|
||||||
Checks checks = new Checks();
|
Checks checks = new Checks();
|
||||||
if (!checks.verifyCode(DefaultAuthenticationFlows.BROWSER_FLOW, code, action)) {
|
if (!checks.verifyCode(realm.getBrowserFlow(), code, action)) {
|
||||||
return checks.response;
|
return checks.response;
|
||||||
}
|
}
|
||||||
final ClientSessionCode clientCode = checks.clientCode;
|
final ClientSessionCode clientCode = checks.clientCode;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
org.keycloak.testsuite.forms.PassThroughAuthenticator
|
||||||
|
org.keycloak.testsuite.forms.PassThroughRegistration
|
Loading…
Reference in a new issue