merge conflicts
This commit is contained in:
commit
94980fff29
37 changed files with 530 additions and 86 deletions
|
@ -14,8 +14,8 @@
|
|||
<column name="PROTOCOL_MAPPER_NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="APPLIED_BY_DEFAULT" type="BOOLEAN(1)"/>
|
||||
<column name="CONSENT_REQUIRED" type="BOOLEAN(1)"/>
|
||||
<column name="APPLIED_BY_DEFAULT" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="CONSENT_TEXT" type="VARCHAR(255)"/>
|
||||
<column name="CLIENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
|
@ -46,13 +46,13 @@
|
|||
<column name="INTERNAL_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ENABLED" type="BOOLEAN(1)"/>
|
||||
<column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="PROVIDER_NONIMAL_ID" type="VARCHAR(255)"/>
|
||||
<column name="PROVIDER_NAME" type="VARCHAR(255)"/>
|
||||
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
|
||||
<column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN(1)"/>
|
||||
<column name="STORE_TOKEN" type="BOOLEAN(1)"/>
|
||||
<column name="AUTHENTICATE_BY_DEFAULT" type="BOOLEAN(1)"/>
|
||||
<column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="STORE_TOKEN" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="AUTHENTICATE_BY_DEFAULT" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="REALM_ID" type="VARCHAR(36)"/>
|
||||
</createTable>
|
||||
<createTable tableName="IDENTITY_PROVIDER_CONFIG">
|
||||
|
@ -71,7 +71,7 @@
|
|||
<column name="IDENTITY_PROVIDER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="RETRIEVE_TOKEN" type="BOOLEAN(1)"/>
|
||||
<column name="RETRIEVE_TOKEN" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
</createTable>
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="FRONTCHANNEL_LOGOUT" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
|
@ -90,5 +90,9 @@
|
|||
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_56ELWNIBJI49AVXSRTUF6XJ23" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
<addUniqueConstraint columnNames="PROVIDER_NONIMAL_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
|
||||
<addUniqueConstraint columnNames="IDENTITY_PROVIDER_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_IDENTITY_PROVIDER_MAPPING"/>
|
||||
|
||||
<addColumn tableName="REALM">
|
||||
<column name="LOGIN_LIFESPAN" type="INT"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -19,6 +19,7 @@ public class RealmRepresentation {
|
|||
protected Integer ssoSessionMaxLifespan;
|
||||
protected Integer accessCodeLifespan;
|
||||
protected Integer accessCodeLifespanUserAction;
|
||||
protected Integer accessCodeLifespanLogin;
|
||||
protected Boolean enabled;
|
||||
protected String sslRequired;
|
||||
protected Boolean passwordCredentialGrantAllowed;
|
||||
|
@ -199,6 +200,14 @@ public class RealmRepresentation {
|
|||
this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
|
||||
}
|
||||
|
||||
public Integer getAccessCodeLifespanLogin() {
|
||||
return accessCodeLifespanLogin;
|
||||
}
|
||||
|
||||
public void setAccessCodeLifespanLogin(Integer accessCodeLifespanLogin) {
|
||||
this.accessCodeLifespanLogin = accessCodeLifespanLogin;
|
||||
}
|
||||
|
||||
public List<String> getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
|
|
@ -36,8 +36,6 @@ keycloak-appliance-dist-all-&project.version;/
|
|||
bin/
|
||||
standalone.sh
|
||||
standalone.bat
|
||||
standalone/deployments/
|
||||
auth-server.war/
|
||||
standalone/configuration/
|
||||
keycloak-server.json
|
||||
themes/
|
||||
|
|
|
@ -826,6 +826,12 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
|
|||
$scope.realm.accessCodeLifespan = TimeUnit.convert($scope.realm.accessCodeLifespan, from, to);
|
||||
});
|
||||
|
||||
$scope.realm.accessCodeLifespanLoginUnit = TimeUnit.autoUnit(realm.accessCodeLifespanLogin);
|
||||
$scope.realm.accessCodeLifespanLogin = TimeUnit.toUnit(realm.accessCodeLifespanLogin, $scope.realm.accessCodeLifespanLoginUnit);
|
||||
$scope.$watch('realm.accessCodeLifespanLoginUnit', function(to, from) {
|
||||
$scope.realm.accessCodeLifespanLogin = TimeUnit.convert($scope.realm.accessCodeLifespanLogin, from, to);
|
||||
});
|
||||
|
||||
$scope.realm.accessCodeLifespanUserActionUnit = TimeUnit.autoUnit(realm.accessCodeLifespanUserAction);
|
||||
$scope.realm.accessCodeLifespanUserAction = TimeUnit.toUnit(realm.accessCodeLifespanUserAction, $scope.realm.accessCodeLifespanUserActionUnit);
|
||||
$scope.$watch('realm.accessCodeLifespanUserActionUnit', function(to, from) {
|
||||
|
@ -848,12 +854,14 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
|
|||
delete realmCopy["accessCodeLifespanUnit"];
|
||||
delete realmCopy["ssoSessionIdleTimeoutUnit"];
|
||||
delete realmCopy["accessCodeLifespanUserActionUnit"];
|
||||
delete realmCopy["accessCodeLifespanLoginUnit"];
|
||||
|
||||
realmCopy.accessTokenLifespan = TimeUnit.toSeconds($scope.realm.accessTokenLifespan, $scope.realm.accessTokenLifespanUnit)
|
||||
realmCopy.ssoSessionIdleTimeout = TimeUnit.toSeconds($scope.realm.ssoSessionIdleTimeout, $scope.realm.ssoSessionIdleTimeoutUnit)
|
||||
realmCopy.ssoSessionMaxLifespan = TimeUnit.toSeconds($scope.realm.ssoSessionMaxLifespan, $scope.realm.ssoSessionMaxLifespanUnit)
|
||||
realmCopy.accessCodeLifespan = TimeUnit.toSeconds($scope.realm.accessCodeLifespan, $scope.realm.accessCodeLifespanUnit)
|
||||
realmCopy.accessCodeLifespanUserAction = TimeUnit.toSeconds($scope.realm.accessCodeLifespanUserAction, $scope.realm.accessCodeLifespanUserActionUnit)
|
||||
realmCopy.accessCodeLifespanLogin = TimeUnit.toSeconds($scope.realm.accessCodeLifespanLogin, $scope.realm.accessCodeLifespanLoginUnit)
|
||||
|
||||
Realm.update(realmCopy, function () {
|
||||
$route.reload();
|
||||
|
|
|
@ -92,6 +92,25 @@
|
|||
</div>
|
||||
<span tooltip-placement="right" tooltip="Max time an application or oauth client has to finish the access token protocol. This should normally be 1 minute." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group input-select">
|
||||
<label class="col-sm-2 control-label" for="accessCodeLifespanLogin" class="two-lines">Login lifespan</label>
|
||||
<div class="col-sm-5">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="number" required min="1" max="31536000" data-ng-model="realm.accessCodeLifespanLogin" id="accessCodeLifespanLogin" name="accessCodeLifespanLogin">
|
||||
</div>
|
||||
<div class="col-sm-4 select-kc">
|
||||
<select name="accessCodeLifespanLoginUnit" data-ng-model="realm.accessCodeLifespanLoginUnit">
|
||||
<option data-ng-selected="!realm.accessCodeLifespanLoginUnit">Seconds</option>
|
||||
<option>Minutes</option>
|
||||
<option>Hours</option>
|
||||
<option>Days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Max time a user has to complete a login. This is recommended to be relatively long. 30 minutes or more." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group input-select">
|
||||
<label class="col-sm-2 control-label" for="accessCodeLifespanUserAction" class="two-lines">Login user action lifespan</label>
|
||||
<div class="col-sm-5">
|
||||
|
@ -109,7 +128,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Max time a user has to complete a login and perform actions like update password or configure totp. This is recommended to be relatively long. 5 minutes or more." class="fa fa-info-circle"></span>
|
||||
<span tooltip-placement="right" tooltip="Max time a user has to complete login related actions like update password or configure totp. This is recommended to be relatively long. 5 minutes or more." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
|
||||
|
|
15
forms/common-themes/src/main/resources/theme/login/base/info.ftl
Executable file
15
forms/common-themes/src/main/resources/theme/login/base/info.ftl
Executable file
|
@ -0,0 +1,15 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout displayMessage=false; section>
|
||||
<#if section = "title">
|
||||
${message.summary}
|
||||
<#elseif section = "header">
|
||||
${message.summary}
|
||||
<#elseif section = "form">
|
||||
<div id="kc-info-message">
|
||||
<p class="instruction">${message.summary}</p>
|
||||
<#if client.baseUrl??>
|
||||
<p><a href="${client.baseUrl}">${rb.backToApplication}</a></p>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
|
@ -25,6 +25,7 @@ rememberMe=Remember me
|
|||
passwordConfirm=Confirm password
|
||||
passwordNew=New Password
|
||||
passwordNewConfirm=New Password confirmation
|
||||
passwordUpdated=Password updated
|
||||
cancel=Cancel
|
||||
accept=Accept
|
||||
submit=Submit
|
||||
|
@ -90,6 +91,7 @@ emailVerifyInstr=An email with instructions to verify your email address has bee
|
|||
emailVerifyInstrQ=Haven't received a verification code in your email?
|
||||
emailVerifyClick=Click here
|
||||
emailVerifyResend=to re-send the email.
|
||||
emailVerified=Email verified
|
||||
|
||||
error=A system error has occured, contact admin
|
||||
errorTitle=We're sorry...
|
||||
|
@ -111,6 +113,7 @@ errorHeader=Error!
|
|||
|
||||
emailForgotHeader=Forgot Your Password?
|
||||
backToLogin=« Back to Login
|
||||
backToApplication=« Back to Application
|
||||
emailUpdateHeader=Update password
|
||||
emailSent=You should receive an email shortly with further instructions.
|
||||
emailSendError=Failed to send email, please try again later
|
||||
|
|
|
@ -5,6 +5,6 @@ package org.keycloak.login;
|
|||
*/
|
||||
public enum LoginFormsPages {
|
||||
|
||||
LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL, OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, ERROR, LOGIN_UPDATE_PROFILE, CODE;
|
||||
LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL, OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, INFO, ERROR, LOGIN_UPDATE_PROFILE, CODE;
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
public Response createRegistration();
|
||||
|
||||
public Response createInfoPage();
|
||||
|
||||
public Response createErrorPage();
|
||||
|
||||
public Response createOAuthGrant();
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
@ -51,7 +52,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(FreeMarkerLoginFormsProvider.class);
|
||||
|
||||
private String message;
|
||||
public static enum MessageType {SUCCESS, WARNING, ERROR}
|
||||
|
||||
private String accessCode;
|
||||
private Response.Status status;
|
||||
private List<RoleModel> realmRolesRequested;
|
||||
|
@ -61,8 +63,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
private String accessRequestMessage;
|
||||
private URI actionUri;
|
||||
|
||||
public static enum MessageType {SUCCESS, WARNING, ERROR}
|
||||
|
||||
private String message;
|
||||
private MessageType messageType = MessageType.ERROR;
|
||||
|
||||
private MultivaluedMap<String, String> formData;
|
||||
|
@ -253,6 +254,10 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return createResponse(LoginFormsPages.REGISTER);
|
||||
}
|
||||
|
||||
public Response createInfoPage() {
|
||||
return createResponse(LoginFormsPages.INFO);
|
||||
}
|
||||
|
||||
public Response createErrorPage() {
|
||||
if (status == null) {
|
||||
status = Response.Status.INTERNAL_SERVER_ERROR;
|
||||
|
|
|
@ -25,6 +25,8 @@ public class Templates {
|
|||
return "login-update-password.ftl";
|
||||
case REGISTER:
|
||||
return "register.ftl";
|
||||
case INFO:
|
||||
return "info.ftl";
|
||||
case ERROR:
|
||||
return "error.ftl";
|
||||
case LOGIN_UPDATE_PROFILE:
|
||||
|
|
|
@ -26,4 +26,12 @@ public class ClientBean {
|
|||
public String getClientId() {
|
||||
return client.getClientId();
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
if (client instanceof ApplicationModel) {
|
||||
return ((ApplicationModel) client).getBaseUrl();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -99,6 +99,10 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void setAccessCodeLifespanUserAction(int seconds);
|
||||
|
||||
int getAccessCodeLifespanLogin();
|
||||
|
||||
void setAccessCodeLifespanLogin(int seconds);
|
||||
|
||||
String getPublicKeyPem();
|
||||
|
||||
void setPublicKeyPem(String publicKeyPem);
|
||||
|
|
|
@ -34,6 +34,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
private int accessTokenLifespan;
|
||||
private int accessCodeLifespan;
|
||||
private int accessCodeLifespanUserAction;
|
||||
private int accessCodeLifespanLogin;
|
||||
private int notBefore;
|
||||
|
||||
private String publicKeyPem;
|
||||
|
@ -230,6 +231,13 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
|
||||
this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
|
||||
}
|
||||
public int getAccessCodeLifespanLogin() {
|
||||
return accessCodeLifespanLogin;
|
||||
}
|
||||
|
||||
public void setAccessCodeLifespanLogin(int accessCodeLifespanLogin) {
|
||||
this.accessCodeLifespanLogin = accessCodeLifespanLogin;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
|
|
|
@ -114,6 +114,7 @@ public class ModelToRepresentation {
|
|||
rep.setSsoSessionMaxLifespan(realm.getSsoSessionMaxLifespan());
|
||||
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
|
||||
rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
|
||||
rep.setAccessCodeLifespanLogin(realm.getAccessCodeLifespanLogin());
|
||||
rep.setSmtpServer(realm.getSmtpConfig());
|
||||
rep.setBrowserSecurityHeaders(realm.getBrowserSecurityHeaders());
|
||||
rep.setAccountTheme(realm.getAccountTheme());
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package org.keycloak.models.utils;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class RealmInfoUtil {
|
||||
|
||||
public static int getDettachedClientSessionLifespan(RealmModel realm) {
|
||||
int lifespan = realm.getAccessCodeLifespanLogin();
|
||||
if (realm.getAccessCodeLifespanUserAction() > lifespan) {
|
||||
lifespan = realm.getAccessCodeLifespanUserAction();
|
||||
}
|
||||
if (realm.getAccessCodeLifespan() > lifespan) {
|
||||
lifespan = realm.getAccessCodeLifespan();
|
||||
}
|
||||
return lifespan;
|
||||
}
|
||||
|
||||
}
|
|
@ -78,6 +78,10 @@ public class RepresentationToModel {
|
|||
newRealm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
|
||||
else newRealm.setAccessCodeLifespanUserAction(300);
|
||||
|
||||
if (rep.getAccessCodeLifespanLogin() != null)
|
||||
newRealm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin());
|
||||
else newRealm.setAccessCodeLifespanLogin(1800);
|
||||
|
||||
if (rep.getSslRequired() != null) newRealm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
|
||||
if (rep.isPasswordCredentialGrantAllowed() != null) newRealm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed());
|
||||
if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed());
|
||||
|
@ -258,8 +262,8 @@ public class RepresentationToModel {
|
|||
if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
|
||||
if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
|
||||
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
|
||||
if (rep.getAccessCodeLifespanUserAction() != null)
|
||||
realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
|
||||
if (rep.getAccessCodeLifespanUserAction() != null) realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
|
||||
if (rep.getAccessCodeLifespanLogin() != null) realm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin());
|
||||
if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore());
|
||||
if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
|
||||
if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
|
||||
|
|
|
@ -300,6 +300,18 @@ public class RealmAdapter implements RealmModel {
|
|||
updated.setAccessCodeLifespanUserAction(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessCodeLifespanLogin() {
|
||||
if (updated != null) return updated.getAccessCodeLifespanLogin();
|
||||
return cached.getAccessCodeLifespanLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessCodeLifespanLogin(int seconds) {
|
||||
getDelegateForUpdate();
|
||||
updated.setAccessCodeLifespanLogin(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPublicKeyPem() {
|
||||
if (updated != null) return updated.getPublicKeyPem();
|
||||
|
|
|
@ -53,6 +53,7 @@ public class CachedRealm {
|
|||
private int accessTokenLifespan;
|
||||
private int accessCodeLifespan;
|
||||
private int accessCodeLifespanUserAction;
|
||||
private int accessCodeLifespanLogin;
|
||||
private int notBefore;
|
||||
private PasswordPolicy passwordPolicy;
|
||||
|
||||
|
@ -111,6 +112,7 @@ public class CachedRealm {
|
|||
accessTokenLifespan = model.getAccessTokenLifespan();
|
||||
accessCodeLifespan = model.getAccessCodeLifespan();
|
||||
accessCodeLifespanUserAction = model.getAccessCodeLifespanUserAction();
|
||||
accessCodeLifespanLogin = model.getAccessCodeLifespanLogin();
|
||||
notBefore = model.getNotBefore();
|
||||
passwordPolicy = model.getPasswordPolicy();
|
||||
|
||||
|
@ -266,6 +268,9 @@ public class CachedRealm {
|
|||
public int getAccessCodeLifespanUserAction() {
|
||||
return accessCodeLifespanUserAction;
|
||||
}
|
||||
public int getAccessCodeLifespanLogin() {
|
||||
return accessCodeLifespanLogin;
|
||||
}
|
||||
|
||||
public String getPublicKeyPem() {
|
||||
return publicKeyPem;
|
||||
|
|
|
@ -362,6 +362,17 @@ public class RealmAdapter implements RealmModel {
|
|||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessCodeLifespanLogin() {
|
||||
return realm.getAccessCodeLifespanLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessCodeLifespanLogin(int accessCodeLifespanLogin) {
|
||||
realm.setAccessCodeLifespanLogin(accessCodeLifespanLogin);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPublicKeyPem() {
|
||||
return realm.getPublicKeyPem();
|
||||
|
|
|
@ -68,6 +68,8 @@ public class RealmEntity {
|
|||
protected int accessCodeLifespan;
|
||||
@Column(name="USER_ACTION_LIFESPAN")
|
||||
protected int accessCodeLifespanUserAction;
|
||||
@Column(name="LOGIN_LIFESPAN")
|
||||
protected int accessCodeLifespanLogin;
|
||||
@Column(name="NOT_BEFORE")
|
||||
protected int notBefore;
|
||||
|
||||
|
@ -244,6 +246,13 @@ public class RealmEntity {
|
|||
public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
|
||||
this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
|
||||
}
|
||||
public int getAccessCodeLifespanLogin() {
|
||||
return accessCodeLifespanLogin;
|
||||
}
|
||||
|
||||
public void setAccessCodeLifespanLogin(int accessCodeLifespanLogin) {
|
||||
this.accessCodeLifespanLogin = accessCodeLifespanLogin;
|
||||
}
|
||||
|
||||
public String getPublicKeyPem() {
|
||||
return publicKeyPem;
|
||||
|
|
|
@ -295,8 +295,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
updateRealm();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getAccessCodeLifespan() {
|
||||
return realm.getAccessCodeLifespan();
|
||||
|
@ -319,6 +317,17 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessCodeLifespanLogin(int accessCodeLifespanLogin) {
|
||||
realm.setAccessCodeLifespanLogin(accessCodeLifespanLogin);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessCodeLifespanLogin() {
|
||||
return realm.getAccessCodeLifespanLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPublicKeyPem() {
|
||||
return realm.getPublicKeyPem();
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.models.sessions.infinispan.mapreduce.LargestResultReducer;
|
|||
import org.keycloak.models.sessions.infinispan.mapreduce.SessionMapper;
|
||||
import org.keycloak.models.sessions.infinispan.mapreduce.UserSessionMapper;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RealmInfoUtil;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.Collection;
|
||||
|
@ -201,6 +202,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
public void removeExpiredUserSessions(RealmModel realm) {
|
||||
int expired = Time.currentTime() - realm.getSsoSessionMaxLifespan();
|
||||
int expiredRefresh = Time.currentTime() - realm.getSsoSessionIdleTimeout();
|
||||
int expiredDettachedClientSession = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
|
||||
|
||||
Map<String, String> map = new MapReduceTask(sessionCache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).expired(expired, expiredRefresh).emitKey())
|
||||
|
@ -212,7 +214,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
map = new MapReduceTask(sessionCache)
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredRefresh).requireNullUserSession(true).emitKey())
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession(true).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.keycloak.models.sessions.jpa.entities.ClientSessionEntity;
|
|||
import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
|
||||
import org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RealmInfoUtil;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
@ -190,18 +191,19 @@ public class JpaUserSessionProvider implements UserSessionProvider {
|
|||
public void removeExpiredUserSessions(RealmModel realm) {
|
||||
int maxTime = Time.currentTime() - realm.getSsoSessionMaxLifespan();
|
||||
int idleTime = Time.currentTime() - realm.getSsoSessionIdleTimeout();
|
||||
int dettachedClientSessionExpired = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
|
||||
|
||||
em.createNamedQuery("removeDetachedClientSessionRoleByExpired")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("maxTime", idleTime)
|
||||
.setParameter("maxTime", dettachedClientSessionExpired)
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("removeDetachedClientSessionNoteByExpired")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("maxTime", idleTime)
|
||||
.setParameter("maxTime", dettachedClientSessionExpired)
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("removeDetachedClientSessionByExpired")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("maxTime", idleTime)
|
||||
.setParameter("maxTime", dettachedClientSessionExpired)
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("removeClientSessionRoleByExpired")
|
||||
.setParameter("realmId", realm.getId())
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
|
|||
import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureEntity;
|
||||
import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureKey;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RealmInfoUtil;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -191,10 +192,11 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
int expired = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
|
||||
Iterator<ClientSessionEntity> citr = clientSessions.values().iterator();
|
||||
while (citr.hasNext()) {
|
||||
ClientSessionEntity c = citr.next();
|
||||
if (c.getSession() == null && c.getRealmId().equals(realm.getId()) && c.getTimestamp() < Time.currentTime() - realm.getSsoSessionIdleTimeout()) {
|
||||
if (c.getSession() == null && c.getRealmId().equals(realm.getId()) && c.getTimestamp() < expired) {
|
||||
citr.remove();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity;
|
|||
import org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity;
|
||||
import org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RealmInfoUtil;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
@ -190,7 +191,7 @@ public class MongoUserSessionProvider implements UserSessionProvider {
|
|||
query = new QueryBuilder()
|
||||
.and("sessionId").is(null)
|
||||
.and("realmId").is(realm.getId())
|
||||
.and("timestamp").lessThan(currentTime - realm.getSsoSessionIdleTimeout())
|
||||
.and("timestamp").lessThan(currentTime - RealmInfoUtil.getDettachedClientSessionLifespan(realm))
|
||||
.get();
|
||||
|
||||
mongoStore.removeEntities(MongoClientSessionEntity.class, query, invocationContext);
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.keycloak.protocol.LoginProtocol;
|
|||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.services.util.CookieHelper;
|
||||
|
@ -366,6 +367,7 @@ public class AuthenticationManager {
|
|||
LoginFormsProvider loginFormsProvider = Flows.forms(session, realm, client, uriInfo).setClientSessionCode(accessCode.getCode()).setUser(user);
|
||||
if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL)) {
|
||||
event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
|
||||
LoginActionsService.createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
|
||||
}
|
||||
|
||||
return loginFormsProvider
|
||||
|
|
|
@ -91,7 +91,19 @@ public class ClientSessionCode {
|
|||
return false;
|
||||
}
|
||||
|
||||
int lifespan = action.equals(ClientSessionModel.Action.CODE_TO_TOKEN) ? realm.getAccessCodeLifespan() : realm.getAccessCodeLifespanUserAction();
|
||||
int lifespan;
|
||||
switch (action) {
|
||||
case CODE_TO_TOKEN:
|
||||
lifespan = realm.getAccessCodeLifespan();
|
||||
break;
|
||||
case AUTHENTICATE:
|
||||
lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction();
|
||||
break;
|
||||
default:
|
||||
lifespan = realm.getAccessCodeLifespanUserAction();
|
||||
break;
|
||||
}
|
||||
|
||||
return timestamp + lifespan > Time.currentTime();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.keycloak.services.managers.ClientSessionCode;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.util.CookieHelper;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -61,6 +62,7 @@ import javax.ws.rs.POST;
|
|||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Cookie;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
|
@ -79,6 +81,8 @@ public class LoginActionsService {
|
|||
|
||||
protected static final Logger logger = Logger.getLogger(LoginActionsService.class);
|
||||
|
||||
public static final String ACTION_COOKIE = "KEYCLOAK_ACTION";
|
||||
|
||||
private RealmModel realm;
|
||||
|
||||
@Context
|
||||
|
@ -163,6 +167,18 @@ public class LoginActionsService {
|
|||
}
|
||||
}
|
||||
|
||||
boolean check(String code, ClientSessionModel.Action requiredAction, ClientSessionModel.Action alternativeRequiredAction) {
|
||||
if (!check(code)) {
|
||||
return false;
|
||||
} else if (!(clientCode.isValid(requiredAction) || clientCode.isValid(alternativeRequiredAction))) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean check(String code) {
|
||||
if (!checkSsl()) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
|
@ -272,7 +288,10 @@ public class LoginActionsService {
|
|||
event.error(Errors.INVALID_CODE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
|
||||
}
|
||||
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
|
||||
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
|
||||
|
@ -689,7 +708,7 @@ public class LoginActionsService {
|
|||
final MultivaluedMap<String, String> formData) {
|
||||
event.event(EventType.UPDATE_PASSWORD);
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD)) {
|
||||
if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD, ClientSessionModel.Action.RECOVER_PASSWORD)) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
|
@ -723,7 +742,17 @@ public class LoginActionsService {
|
|||
|
||||
user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
||||
|
||||
event.clone().event(EventType.UPDATE_PASSWORD).success();
|
||||
event.event(EventType.UPDATE_PASSWORD).success();
|
||||
|
||||
if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD)) {
|
||||
String actionCookieValue = getActionCookie();
|
||||
if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
|
||||
return Flows.forms(session, realm, clientSession.getClient(), uriInfo).setSuccess("passwordUpdated").createInfoPage();
|
||||
}
|
||||
}
|
||||
|
||||
event = event.clone().event(EventType.LOGIN);
|
||||
|
||||
return redirectOauth(user, accessCode, clientSession, userSession);
|
||||
}
|
||||
|
||||
|
@ -746,7 +775,14 @@ public class LoginActionsService {
|
|||
|
||||
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
||||
|
||||
event.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
|
||||
event.event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
|
||||
|
||||
String actionCookieValue = getActionCookie();
|
||||
if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
|
||||
return Flows.forms(session, realm, clientSession.getClient(), uriInfo).setSuccess("emailVerified").createInfoPage();
|
||||
}
|
||||
|
||||
event = event.clone().removeDetail(Details.EMAIL).event(EventType.LOGIN);
|
||||
|
||||
return redirectOauth(user, accessCode, clientSession, userSession);
|
||||
} else {
|
||||
|
@ -759,6 +795,8 @@ public class LoginActionsService {
|
|||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
initEvent(clientSession);
|
||||
|
||||
createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
|
||||
|
||||
return Flows.forms(session, realm, null, uriInfo)
|
||||
.setClientSessionCode(accessCode.getCode())
|
||||
.setUser(userSession.getUser())
|
||||
|
@ -776,7 +814,6 @@ public class LoginActionsService {
|
|||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
accessCode.setRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
||||
return Flows.forms(session, realm, null, uriInfo)
|
||||
.setClientSessionCode(accessCode.getCode())
|
||||
.createResponse(RequiredAction.UPDATE_PASSWORD);
|
||||
|
@ -863,11 +900,23 @@ public class LoginActionsService {
|
|||
.setClientSessionCode(accessCode.getCode())
|
||||
.createErrorPage();
|
||||
}
|
||||
|
||||
createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
|
||||
}
|
||||
|
||||
return Flows.forms(session, realm, client, uriInfo).setSuccess("emailSent").setClientSessionCode(accessCode.getCode()).createPasswordReset();
|
||||
}
|
||||
|
||||
private String getActionCookie() {
|
||||
Cookie cookie = headers.getCookies().get(ACTION_COOKIE);
|
||||
AuthenticationManager.expireCookie(realm, ACTION_COOKIE, AuthenticationManager.getRealmCookiePath(realm, uriInfo), realm.getSslRequired().isRequired(clientConnection), clientConnection);
|
||||
return cookie != null ? cookie.getValue() : null;
|
||||
}
|
||||
|
||||
public static void createActionCookie(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, String sessionId) {
|
||||
CookieHelper.addCookie(ACTION_COOKIE, sessionId, AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, null, -1, realm.getSslRequired().isRequired(clientConnection), true);
|
||||
}
|
||||
|
||||
private Response redirectOauth(UserModel user, ClientSessionCode accessCode, ClientSessionModel clientSession, UserSessionModel userSession) {
|
||||
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.keycloak.testsuite.MailUtil;
|
|||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.InfoPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.RegisterPage;
|
||||
import org.keycloak.testsuite.pages.VerifyEmailPage;
|
||||
|
@ -51,6 +52,9 @@ import javax.mail.MessagingException;
|
|||
import javax.mail.internet.MimeMessage;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -86,6 +90,9 @@ public class RequiredActionEmailVerificationTest {
|
|||
@WebResource
|
||||
protected RegisterPage registerPage;
|
||||
|
||||
@WebResource
|
||||
protected InfoPage infoPage;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
oauth.state("mystate"); // have to set this as keycloak validates that state is sent
|
||||
|
@ -200,4 +207,41 @@ public class RequiredActionEmailVerificationTest {
|
|||
events.expectLogin().session(sessionId).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyEmailNewBrowserSession() throws IOException, MessagingException {
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
Assert.assertTrue(verifyEmailPage.isCurrent());
|
||||
|
||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
|
||||
String body = (String) message.getContent();
|
||||
String verificationUrl = MailUtil.getLink(body);
|
||||
|
||||
AssertEvents.ExpectedEvent emailEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).detail("email", "test-user@localhost");
|
||||
Event sendEvent = emailEvent.assertEvent();
|
||||
String sessionId = sendEvent.getSessionId();
|
||||
|
||||
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
||||
|
||||
Assert.assertEquals(mailCodeId, verificationUrl.split("key=")[1].split("\\.")[1]);
|
||||
|
||||
driver.manage().deleteAllCookies();
|
||||
|
||||
driver.navigate().to(verificationUrl.trim());
|
||||
|
||||
events.expectRequiredAction(EventType.VERIFY_EMAIL).session(sessionId).detail("email", "test-user@localhost").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
||||
|
||||
assertTrue(infoPage.isCurrent());
|
||||
assertEquals("Email verified", infoPage.getInfo());
|
||||
|
||||
loginPage.open();
|
||||
|
||||
assertTrue(loginPage.isCurrent());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ public class RealmTest extends AbstractClientTest {
|
|||
RealmRepresentation rep = realm.toRepresentation();
|
||||
rep.setSsoSessionIdleTimeout(123);
|
||||
rep.setSsoSessionMaxLifespan(12);
|
||||
rep.setAccessCodeLifespanLogin(1234);
|
||||
|
||||
realm.update(rep);
|
||||
|
||||
|
@ -70,6 +71,7 @@ public class RealmTest extends AbstractClientTest {
|
|||
|
||||
assertEquals(123, rep.getSsoSessionIdleTimeout().intValue());
|
||||
assertEquals(12, rep.getSsoSessionMaxLifespan().intValue());
|
||||
assertEquals(1234, rep.getAccessCodeLifespanLogin().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -116,7 +116,7 @@ public class LoginTest {
|
|||
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).assertEvent();
|
||||
events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -136,7 +136,7 @@ public class LoginTest {
|
|||
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).assertEvent();
|
||||
events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").assertEvent();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
|
@ -164,7 +164,7 @@ public class LoginTest {
|
|||
|
||||
Assert.assertEquals("Account is disabled, contact admin", loginPage.getError());
|
||||
|
||||
events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).assertEvent();
|
||||
events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").assertEvent();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
|
@ -184,7 +184,7 @@ public class LoginTest {
|
|||
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
events.expectLogin().user((String) null).session((String) null).error("user_not_found").detail(Details.USERNAME, "invalid").removeDetail(Details.CODE_ID).assertEvent();
|
||||
events.expectLogin().user((String) null).session((String) null).error("user_not_found").detail(Details.USERNAME, "invalid").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -198,6 +198,36 @@ public class LoginTest {
|
|||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginNoTimeoutWithLongWait() {
|
||||
try {
|
||||
loginPage.open();
|
||||
|
||||
Time.setOffset(1700);
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent().getSessionId();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginTimeout() {
|
||||
try {
|
||||
loginPage.open();
|
||||
|
||||
Time.setOffset(1850);
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
events.expectLogin().clearDetails().detail(Details.CODE_ID, AssertEvents.isCodeId()).user((String) null).session((String) null).error("expired_code").assertEvent().getSessionId();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginLoginHint() {
|
||||
String loginFormUrl = oauth.getLoginFormUrl() + "&login_hint=login-test";
|
||||
|
@ -274,7 +304,7 @@ public class LoginTest {
|
|||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertEquals("access_denied", oauth.getCurrentQuery().get(OAuth2Constants.ERROR));
|
||||
|
||||
events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).assertEvent();
|
||||
events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).assertEvent();
|
||||
}
|
||||
|
||||
// KEYCLOAK-1037
|
||||
|
@ -288,7 +318,7 @@ public class LoginTest {
|
|||
loginPage.assertCurrent();
|
||||
Assert.assertEquals("Login timeout. Please login again", loginPage.getError());
|
||||
|
||||
events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails().assertEvent();
|
||||
events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails().detail(Details.CODE_ID, AssertEvents.isCodeId()).assertEvent();
|
||||
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.keycloak.testsuite.rule.KeycloakRule;
|
|||
import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.keycloak.util.Time;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
@ -113,7 +114,7 @@ public class LoginTotpTest {
|
|||
loginPage.assertCurrent();
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).session((String) null).assertEvent();
|
||||
events.expectLogin().error("invalid_user_credentials").session((String) null).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -139,45 +140,29 @@ public class LoginTotpTest {
|
|||
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).session((String) null).assertEvent();
|
||||
events.expectLogin().error("invalid_user_credentials").session((String) null).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithTotpExpiredPasswordToken() throws Exception {
|
||||
try {
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
keycloakRule.configure(new KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
lifespan = appRealm.getAccessCodeLifespanUserAction();
|
||||
appRealm.setAccessCodeLifespanUserAction(1);
|
||||
}
|
||||
});
|
||||
|
||||
loginTotpPage.assertCurrent();
|
||||
|
||||
Thread.sleep(2000);
|
||||
Time.setOffset(350);
|
||||
|
||||
loginTotpPage.login(totp.generate("totpSecret"));
|
||||
|
||||
loginPage.assertCurrent();
|
||||
Assert.assertEquals("Login timeout. Please login again", loginPage.getError());
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
AssertEvents.ExpectedEvent expectedEvent = events.expectLogin().error("expired_code")
|
||||
.user((String)null)
|
||||
.clearDetails()
|
||||
AssertEvents.ExpectedEvent expectedEvent = events.expectLogin().error("invalid_user_credentials")
|
||||
.session((String) null);
|
||||
expectedEvent.assertEvent();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setAccessCodeLifespanUserAction(lifespan);
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
@ -41,6 +40,7 @@ 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.InfoPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
|
@ -56,6 +56,9 @@ import javax.mail.internet.MimeMessage;
|
|||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -103,6 +106,9 @@ public class ResetPasswordTest {
|
|||
@WebResource
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@WebResource
|
||||
protected InfoPage infoPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPasswordResetPage resetPasswordPage;
|
||||
|
||||
|
@ -132,13 +138,13 @@ public class ResetPasswordTest {
|
|||
|
||||
resetPasswordPage.backToLogin();
|
||||
|
||||
Assert.assertTrue(loginPage.isCurrent());
|
||||
assertTrue(loginPage.isCurrent());
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
|
||||
|
@ -149,8 +155,8 @@ public class ResetPasswordTest {
|
|||
|
||||
events.expect(EventType.RESET_PASSWORD_ERROR).client((String) null).user((String) null).error("invalid_code").clearDetails().assertEvent();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Unknown code, please login again through your application.", errorPage.getError());
|
||||
assertTrue(errorPage.isCurrent());
|
||||
assertEquals("Unknown code, please login again through your application.", errorPage.getError());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -168,7 +174,7 @@ public class ResetPasswordTest {
|
|||
|
||||
resetPasswordPage.backToLogin();
|
||||
|
||||
Assert.assertTrue(loginPage.isCurrent());
|
||||
assertTrue(loginPage.isCurrent());
|
||||
|
||||
loginPage.login("login@test.com", "password");
|
||||
|
||||
|
@ -177,8 +183,8 @@ public class ResetPasswordTest {
|
|||
String code = oauth.getCurrentQuery().get("code");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
|
||||
Assert.assertEquals(200, tokenResponse.getStatusCode());
|
||||
Assert.assertEquals(userId, oauth.verifyToken(tokenResponse.getAccessToken()).getSubject());
|
||||
assertEquals(200, tokenResponse.getStatusCode());
|
||||
assertEquals(userId, oauth.verifyToken(tokenResponse.getAccessToken()).getSubject());
|
||||
|
||||
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId()).user(userId).assertEvent();
|
||||
}
|
||||
|
@ -200,9 +206,9 @@ public class ResetPasswordTest {
|
|||
|
||||
String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
|
||||
|
||||
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
|
||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
|
||||
|
@ -217,7 +223,7 @@ public class ResetPasswordTest {
|
|||
|
||||
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, username).session(sessionId).assertEvent();
|
||||
|
||||
|
@ -231,7 +237,7 @@ public class ResetPasswordTest {
|
|||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -245,11 +251,11 @@ public class ResetPasswordTest {
|
|||
|
||||
resetPasswordPage.assertCurrent();
|
||||
|
||||
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
Assert.assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
|
||||
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user((String) null).session((String) null).detail(Details.USERNAME, "invalid").removeDetail(Details.EMAIL).removeDetail(Details.CODE_ID).error("user_not_found").assertEvent();
|
||||
}
|
||||
|
@ -268,9 +274,9 @@ public class ResetPasswordTest {
|
|||
|
||||
String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
|
||||
|
||||
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
|
||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
|
||||
|
@ -283,7 +289,7 @@ public class ResetPasswordTest {
|
|||
|
||||
errorPage.assertCurrent();
|
||||
|
||||
Assert.assertEquals("Invalid code, please login again through your application.", errorPage.getError());
|
||||
assertEquals("Invalid code, please login again through your application.", errorPage.getError());
|
||||
|
||||
events.expectRequiredAction(EventType.RESET_PASSWORD).error("invalid_code").client((String) null).user((String) null).session((String) null).clearDetails().assertEvent();
|
||||
} finally {
|
||||
|
@ -310,11 +316,11 @@ public class ResetPasswordTest {
|
|||
|
||||
resetPasswordPage.assertCurrent();
|
||||
|
||||
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
Assert.assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
|
||||
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("user_disabled").assertEvent();
|
||||
} finally {
|
||||
|
@ -350,11 +356,11 @@ public class ResetPasswordTest {
|
|||
|
||||
resetPasswordPage.assertCurrent();
|
||||
|
||||
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
Assert.assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
|
||||
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD_ERROR).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("invalid_email").assertEvent();
|
||||
} finally {
|
||||
|
@ -388,11 +394,11 @@ public class ResetPasswordTest {
|
|||
|
||||
errorPage.assertCurrent();
|
||||
|
||||
Assert.assertEquals("Failed to send email, please try again later", errorPage.getError());
|
||||
assertEquals("Failed to send email, please try again later", errorPage.getError());
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
Assert.assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
assertEquals(0, greenMail.getReceivedMessages().length);
|
||||
|
||||
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD_ERROR).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error(Errors.EMAIL_SEND_FAILED).assertEvent();
|
||||
} finally {
|
||||
|
@ -423,9 +429,9 @@ public class ResetPasswordTest {
|
|||
|
||||
resetPasswordPage.assertCurrent();
|
||||
|
||||
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
|
||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
|
||||
|
@ -440,13 +446,13 @@ public class ResetPasswordTest {
|
|||
|
||||
updatePasswordPage.changePassword("invalid", "invalid");
|
||||
|
||||
Assert.assertEquals("Invalid password: minimum length 8", resetPasswordPage.getErrorMessage());
|
||||
assertEquals("Invalid password: minimum length 8", resetPasswordPage.getErrorMessage());
|
||||
|
||||
updatePasswordPage.changePassword("resetPasswordWithPasswordPolicy", "resetPasswordWithPasswordPolicy");
|
||||
|
||||
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").session(sessionId).assertEvent();
|
||||
|
||||
|
@ -458,9 +464,51 @@ public class ResetPasswordTest {
|
|||
|
||||
loginPage.login("login-test", "resetPasswordWithPasswordPolicy");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetPasswordNewBrowserSession() throws IOException, MessagingException {
|
||||
String username = "login-test";
|
||||
|
||||
loginPage.open();
|
||||
loginPage.resetPassword();
|
||||
|
||||
resetPasswordPage.assertCurrent();
|
||||
|
||||
resetPasswordPage.changePassword(username);
|
||||
|
||||
resetPasswordPage.assertCurrent();
|
||||
|
||||
String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
|
||||
|
||||
assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||
|
||||
assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
|
||||
String body = (String) message.getContent();
|
||||
String changePasswordUrl = MailUtil.getLink(body);
|
||||
|
||||
driver.manage().deleteAllCookies();
|
||||
|
||||
driver.navigate().to(changePasswordUrl.trim());
|
||||
|
||||
updatePasswordPage.assertCurrent();
|
||||
|
||||
updatePasswordPage.changePassword("resetPassword", "resetPassword");
|
||||
|
||||
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
|
||||
|
||||
assertTrue(infoPage.isCurrent());
|
||||
assertEquals("Password updated", infoPage.getInfo());
|
||||
|
||||
loginPage.open();
|
||||
|
||||
assertTrue(loginPage.isCurrent());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -293,6 +293,61 @@ public class UserSessionProviderTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpireDetachedClientSessions() {
|
||||
try {
|
||||
realm.setAccessCodeLifespan(10);
|
||||
realm.setAccessCodeLifespanUserAction(10);
|
||||
realm.setAccessCodeLifespanLogin(30);
|
||||
|
||||
// Login lifespan is largest
|
||||
String clientSessionId = session.sessions().createClientSession(realm, realm.findClient("test-app")).getId();
|
||||
|
||||
Time.setOffset(25);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(35);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
// User action is largest
|
||||
realm.setAccessCodeLifespanUserAction(40);
|
||||
|
||||
Time.setOffset(0);
|
||||
clientSessionId = session.sessions().createClientSession(realm, realm.findClient("test-app")).getId();
|
||||
|
||||
Time.setOffset(35);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(45);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
// Access code is largest
|
||||
realm.setAccessCodeLifespan(50);
|
||||
|
||||
Time.setOffset(0);
|
||||
clientSessionId = session.sessions().createClientSession(realm, realm.findClient("test-app")).getId();
|
||||
|
||||
Time.setOffset(45);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(55);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
||||
realm.setAccessCodeLifespan(60);
|
||||
realm.setAccessCodeLifespanUserAction(300);
|
||||
realm.setAccessCodeLifespanLogin(1800);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByClient() {
|
||||
UserSessionModel[] sessions = createSessions();
|
||||
|
|
|
@ -140,7 +140,7 @@ public class AuthorizationCodeTest {
|
|||
assertEquals("access_denied", error);
|
||||
|
||||
events.expectLogin().error("rejected_by_user").user((String) null).session((String) null)
|
||||
.removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID)
|
||||
.removeDetail(Details.USERNAME)
|
||||
.detail(Details.REDIRECT_URI, "http://localhost:8081/auth/realms/test/protocol/openid-connect/oauth/oob")
|
||||
.assertEvent().getDetails().get(Details.CODE_ID);
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.pages;
|
||||
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfoPage extends AbstractPage {
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@FindBy(className = "instruction")
|
||||
private WebElement infoMessage;
|
||||
|
||||
public String getInfo() {
|
||||
return infoMessage.getText();
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return driver.getPageSource().contains("kc-info-message");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue