diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js index 44b024bc7f..9c5e974392 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js +++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js @@ -549,6 +549,12 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $scope.realm.accessTokenLifespan = TimeUnit.convert($scope.realm.accessTokenLifespan, from, to); }); + $scope.realm.centralLoginLifespanUnit = TimeUnit.autoUnit(realm.accessTokenLifespan); + $scope.realm.centralLoginLifespan = TimeUnit.toUnit(realm.centralLoginLifespan, $scope.realm.centralLoginLifespanUnit); + $scope.$watch('realm.centralLoginLifespanUnit', function(to, from) { + $scope.realm.centralLoginLifespan = TimeUnit.convert($scope.realm.centralLoginLifespan, from, to); + }); + $scope.realm.refreshTokenLifespanUnit = TimeUnit.autoUnit(realm.refreshTokenLifespan); $scope.realm.refreshTokenLifespan = TimeUnit.toUnit(realm.refreshTokenLifespan, $scope.realm.refreshTokenLifespanUnit); $scope.$watch('realm.refreshTokenLifespanUnit', function(to, from) { @@ -582,6 +588,7 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, delete realmCopy["accessTokenLifespanUnit"]; delete realmCopy["refreshTokenLifespanUnit"]; delete realmCopy["accessCodeLifespanUnit"]; + delete realmCopy["centralLoginLifespanUnit"]; delete realmCopy["accessCodeLifespanUserActionUnit"]; realmCopy.accessTokenLifespan = TimeUnit.toSeconds($scope.realm.accessTokenLifespan, $scope.realm.accessTokenLifespanUnit) diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-tokens.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-tokens.html index 4e8ada46fb..ac83d0982b 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-tokens.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-tokens.html @@ -10,6 +10,32 @@

{{realm.realm}} Token Settings

+
+ +
+ +
+
+
+ +
+
+
+ +
+
+ +
+
+
+
@@ -67,7 +93,7 @@
- +
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java index acdca83f20..dbca8743e7 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java @@ -15,11 +15,13 @@ public class RealmRepresentation { protected String realm; protected Integer accessTokenLifespan; protected Integer refreshTokenLifespan; + protected Integer centralLoginLifespan; protected Integer accessCodeLifespan; protected Integer accessCodeLifespanUserAction; protected Boolean enabled; protected Boolean sslNotRequired; protected Boolean registrationAllowed; + protected Boolean rememberMe; protected Boolean verifyEmail; protected Boolean resetPasswordAllowed; protected Boolean social; @@ -127,6 +129,14 @@ public class RealmRepresentation { return refreshTokenLifespan; } + public Integer getCentralLoginLifespan() { + return centralLoginLifespan; + } + + public void setCentralLoginLifespan(Integer centralLoginLifespan) { + this.centralLoginLifespan = centralLoginLifespan; + } + public void setRefreshTokenLifespan(Integer refreshTokenLifespan) { this.refreshTokenLifespan = refreshTokenLifespan; } @@ -231,6 +241,14 @@ public class RealmRepresentation { this.registrationAllowed = registrationAllowed; } + public Boolean isRememberMe() { + return rememberMe; + } + + public void setRememberMe(Boolean rememberMe) { + this.rememberMe = rememberMe; + } + public Boolean isVerifyEmail() { return verifyEmail; } diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java index b5682eef90..a49e269d86 100755 --- a/model/api/src/main/java/org/keycloak/models/RealmModel.java +++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java @@ -29,6 +29,9 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa boolean isRegistrationAllowed(); void setRegistrationAllowed(boolean registrationAllowed); + boolean isRememberMe(); + + void setRememberMe(boolean rememberMe); boolean isVerifyEmail(); @@ -38,6 +41,10 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa void setResetPasswordAllowed(boolean resetPasswordAllowed); + int getCentralLoginLifespan(); + + void setCentralLoginLifespan(int lifespan); + int getAccessTokenLifespan(); void setAccessTokenLifespan(int tokenLifespan); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index dfa38006be..a84b5d7327 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -105,6 +105,17 @@ public class RealmAdapter implements RealmModel { em.flush(); } + @Override + public boolean isRememberMe() { + return realm.isRememberMe(); + } + + @Override + public void setRememberMe(boolean rememberMe) { + realm.setRememberMe(rememberMe); + em.flush(); + } + @Override public boolean isVerifyEmail() { return realm.isVerifyEmail(); @@ -138,6 +149,17 @@ public class RealmAdapter implements RealmModel { em.flush(); } + @Override + public int getCentralLoginLifespan() { + return realm.getCentralLoginLifespan(); + } + + @Override + public void setCentralLoginLifespan(int lifespan) { + realm.setCentralLoginLifespan(lifespan); + em.flush(); + } + @Override public int getRefreshTokenLifespan() { return realm.getRefreshTokenLifespan(); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java index e29b59df6c..d2e83703db 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java @@ -38,11 +38,13 @@ public class RealmEntity { protected boolean verifyEmail; protected boolean resetPasswordAllowed; protected boolean social; + protected boolean rememberMe; @Column(name="updateProfileOnInitSocLogin") protected boolean updateProfileOnInitialSocialLogin; protected String passwordPolicy; + protected int centralLoginLifespan; protected int accessTokenLifespan; protected int accessCodeLifespan; protected int accessCodeLifespanUserAction; @@ -130,6 +132,14 @@ public class RealmEntity { this.registrationAllowed = registrationAllowed; } + public boolean isRememberMe() { + return rememberMe; + } + + public void setRememberMe(boolean rememberMe) { + this.rememberMe = rememberMe; + } + public boolean isVerifyEmail() { return verifyEmail; } @@ -162,6 +172,14 @@ public class RealmEntity { this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin; } + public int getCentralLoginLifespan() { + return centralLoginLifespan; + } + + public void setCentralLoginLifespan(int centralLoginLifespan) { + this.centralLoginLifespan = centralLoginLifespan; + } + public int getRefreshTokenLifespan() { return refreshTokenLifespan; } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index 6e8cd8c2d7..7192a6f6a5 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -107,6 +107,18 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel { updateRealm(); } + @Override + public boolean isRememberMe() { + return realm.isRememberMe(); + } + + @Override + public void setRememberMe(boolean rememberMe) { + realm.setRememberMe(rememberMe); + updateRealm(); + } + + @Override public boolean isVerifyEmail() { return realm.isVerifyEmail(); @@ -177,6 +189,18 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel { updateRealm(); } + @Override + public int getCentralLoginLifespan() { + return realm.getCentralLoginLifespan(); + } + + @Override + public void setCentralLoginLifespan(int lifespan) { + realm.setCentralLoginLifespan(lifespan); + updateRealm(); + } + + @Override public int getRefreshTokenLifespan() { return realm.getRefreshTokenLifespan(); diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java index 57fe31eecb..9147249fa9 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java @@ -23,12 +23,14 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong private boolean enabled; private boolean sslNotRequired; private boolean registrationAllowed; + private boolean rememberMe; private boolean verifyEmail; private boolean resetPasswordAllowed; private boolean social; private boolean updateProfileOnInitialSocialLogin; private String passwordPolicy; + private int centralLoginLifespan; private int accessTokenLifespan; private int accessCodeLifespan; private int accessCodeLifespanUserAction; @@ -86,6 +88,15 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong this.registrationAllowed = registrationAllowed; } + @MongoField + public boolean isRememberMe() { + return rememberMe; + } + + public void setRememberMe(boolean rememberMe) { + this.rememberMe = rememberMe; + } + @MongoField public boolean isVerifyEmail() { return verifyEmail; @@ -131,6 +142,15 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong this.passwordPolicy = passwordPolicy; } + @MongoField + public int getCentralLoginLifespan() { + return centralLoginLifespan; + } + + public void setCentralLoginLifespan(int centralLoginLifespan) { + this.centralLoginLifespan = centralLoginLifespan; + } + @MongoField public int getAccessTokenLifespan() { return accessTokenLifespan; diff --git a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java index ac95546e46..49ea96a07e 100755 --- a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java +++ b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java @@ -46,6 +46,7 @@ public class ApplianceBootstrap { realm.addRequiredCredential(CredentialRepresentation.PASSWORD); realm.addRequiredOAuthClientCredential(CredentialRepresentation.PASSWORD); realm.addRequiredResourceCredential(CredentialRepresentation.PASSWORD); + realm.setCentralLoginLifespan(3000); realm.setAccessTokenLifespan(60); realm.setRefreshTokenLifespan(3600); realm.setAccessCodeLifespan(60); diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index d8f491110d..237b7dccff 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -46,13 +46,12 @@ public class AuthenticationManager { token.issuedNow(); token.subject(user.getId()); token.audience(realm.getName()); - if (realm.getAccessTokenLifespan() > 0) { - token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan()); + if (realm.getCentralLoginLifespan() > 0) { + token.expiration((System.currentTimeMillis() / 1000) + realm.getCentralLoginLifespan()); } return token; } - public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo) { String cookieName = KEYCLOAK_IDENTITY_COOKIE; String cookiePath = getIdentityCookiePath(realm, uriInfo); @@ -80,7 +79,13 @@ public class AuthenticationManager { String encoded = encodeToken(realm, identityToken); boolean secureOnly = !realm.isSslNotRequired(); logger.debug("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath); - NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, NewCookie.DEFAULT_MAX_AGE, secureOnly, true); + int maxAge = NewCookie.DEFAULT_MAX_AGE; + /* + if (realm.isRememberMe()) { + maxAge = realm.getCentralLoginLifespan(); + } + */ + NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly, true); return cookie; } diff --git a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java index afceb259a3..9bf9142624 100755 --- a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java +++ b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java @@ -69,9 +69,11 @@ public class ModelToRepresentation { rep.setPublicKey(realm.getPublicKeyPem()); rep.setPrivateKey(realm.getPrivateKeyPem()); rep.setRegistrationAllowed(realm.isRegistrationAllowed()); + rep.setRememberMe(realm.isRememberMe()); rep.setVerifyEmail(realm.isVerifyEmail()); rep.setResetPasswordAllowed(realm.isResetPasswordAllowed()); rep.setAccessTokenLifespan(realm.getAccessTokenLifespan()); + rep.setCentralLoginLifespan(realm.getCentralLoginLifespan()); rep.setRefreshTokenLifespan(realm.getRefreshTokenLifespan()); rep.setAccessCodeLifespan(realm.getAccessCodeLifespan()); rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction()); diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index d364d5b4aa..4828002486 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -97,6 +97,7 @@ public class RealmManager { if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled()); if (rep.isSocial() != null) realm.setSocial(rep.isSocial()); if (rep.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed()); + if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe()); if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail()); if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); if (rep.isUpdateProfileOnInitialSocialLogin() != null) @@ -107,6 +108,7 @@ public class RealmManager { realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); if (rep.getRefreshTokenLifespan() != null) realm.setRefreshTokenLifespan(rep.getRefreshTokenLifespan()); + if (rep.getCentralLoginLifespan() != null) realm.setCentralLoginLifespan(rep.getCentralLoginLifespan()); if (rep.getRequiredCredentials() != null) { realm.updateRequiredCredentials(rep.getRequiredCredentials()); } @@ -163,7 +165,9 @@ public class RealmManager { else newRealm.setAccessTokenLifespan(300); if (rep.getRefreshTokenLifespan() != null) newRealm.setRefreshTokenLifespan(rep.getRefreshTokenLifespan()); - else newRealm.setRefreshTokenLifespan(3600); + else newRealm.setRefreshTokenLifespan(36000); + if (rep.getCentralLoginLifespan() != null) newRealm.setCentralLoginLifespan(rep.getCentralLoginLifespan()); + else newRealm.setCentralLoginLifespan(300); if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); else newRealm.setAccessCodeLifespan(60); @@ -174,6 +178,7 @@ public class RealmManager { if (rep.isSslNotRequired() != null) newRealm.setSslNotRequired(rep.isSslNotRequired()); if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); + if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe()); if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail()); if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); if (rep.isUpdateProfileOnInitialSocialLogin() != null) diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java index 335bef23a4..648382305d 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java @@ -57,6 +57,7 @@ public class CompositeRoleTest { RealmModel realm = manager.createRealm("Test"); manager.generateRealmKeys(realm); realmPublicKey = realm.getPublicKey(); + realm.setCentralLoginLifespan(3000); realm.setAccessTokenLifespan(10000); realm.setRefreshTokenLifespan(10000); realm.setAccessCodeLifespanUserAction(1000);