From accb56be7fbcd92f9280528bd8fad85752acb24a Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Wed, 12 Mar 2014 10:17:09 -0400 Subject: [PATCH 1/3] user import fix --- distribution/modules/lib.xml | 4 ++-- examples/demo-template/testrealm.json | 6 +++--- .../java/org/keycloak/services/managers/RealmManager.java | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/distribution/modules/lib.xml b/distribution/modules/lib.xml index 1883476f9d..c28dcf883c 100755 --- a/distribution/modules/lib.xml +++ b/distribution/modules/lib.xml @@ -104,7 +104,7 @@ @@ -114,7 +114,7 @@ diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json index 5de75975e3..2d95f9d878 100755 --- a/examples/demo-template/testrealm.json +++ b/examples/demo-template/testrealm.json @@ -15,9 +15,9 @@ { "username" : "bburke@redhat.com", "enabled": true, - "attributes" : { - "email" : "bburke@redhat.com" - }, + "email" : "bburke@redhat.com", + "firstName": "Bill", + "lastName": "Burke", "credentials" : [ { "type" : "password", "value" : "password" } 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 dae9ae3946..ad4b1cba4c 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -423,6 +423,8 @@ public class RealmManager { UserModel user = newRealm.addUser(userRep.getUsername()); user.setEnabled(userRep.isEnabled()); user.setEmail(userRep.getEmail()); + user.setFirstName(userRep.getFirstName()); + user.setLastName(userRep.getLastName()); if (userRep.getAttributes() != null) { for (Map.Entry entry : userRep.getAttributes().entrySet()) { user.setAttribute(entry.getKey(), entry.getValue()); From fcc95ef99bdffdbdeee4ee74a961a69591b9adaa Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Wed, 12 Mar 2014 15:00:31 -0400 Subject: [PATCH 2/3] fix delete realm --- .../models/jpa/JpaKeycloakSession.java | 4 +- .../org/keycloak/model/test/ImportTest.java | 17 +++ .../tests/src/test/resources/testrealm2.json | 100 ++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100755 model/tests/src/test/resources/testrealm2.json diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java index 78123153e0..057679e15a 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java @@ -83,7 +83,9 @@ public class JpaKeycloakSession implements KeycloakSession { adapter.removeApplication(a.getId()); } - em.createQuery("delete from " + OAuthClientEntity.class.getSimpleName() + " where realm = :realm").setParameter("realm", realm).executeUpdate(); + for (OAuthClientModel oauth : adapter.getOAuthClients()) { + adapter.removeOAuthClient(oauth.getId()); + } for (UserEntity u : em.createQuery("from UserEntity", UserEntity.class).getResultList()) { adapter.removeUser(u.getLoginName()); diff --git a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java index ae83661dfb..c48e134451 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java @@ -28,6 +28,16 @@ import java.util.Set; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ImportTest extends AbstractModelTest { + @Test + public void demoDelete() throws Exception { + // was having trouble deleting this realm from admin console + RealmRepresentation rep = AbstractModelTest.loadJson("testrealm2.json"); + RealmModel realm = realmManager.importRealm(rep); + commit(); + realm = realmManager.getRealmByName("demo-delete"); + realmManager.removeRealm(realm); + } + @Test public void install() throws Exception { RealmRepresentation rep = AbstractModelTest.loadJson("testrealm.json"); @@ -160,6 +170,13 @@ public class ImportTest extends AbstractModelTest { Assert.assertTrue(realm.removeSocialLink(socialUser, "facebook")); Assert.assertNull(realm.getSocialLink(socialUser, "facebook")); Assert.assertFalse(realm.removeSocialLink(socialUser, "facebook")); + + commit(); + + realm = realmManager.getRealm("demo"); + realmManager.removeRealm(realm); + + } @Test diff --git a/model/tests/src/test/resources/testrealm2.json b/model/tests/src/test/resources/testrealm2.json new file mode 100755 index 0000000000..ff9e3bcc67 --- /dev/null +++ b/model/tests/src/test/resources/testrealm2.json @@ -0,0 +1,100 @@ +{ + "realm": "demo-delete", + "enabled": true, + "accessTokenLifespan": 3000, + "accessCodeLifespan": 10, + "accessCodeLifespanUserAction": 6000, + "sslNotRequired": true, + "registrationAllowed": false, + "social": false, + "updateProfileOnInitialSocialLogin": false, + "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", + "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "requiredCredentials": [ "password" ], + "users" : [ + { + "username" : "bburke@redhat.com", + "enabled": true, + "email" : "bburke@redhat.com", + "firstName": "Bill", + "lastName": "Burke", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ] + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "User privileges" + }, + { + "name": "admin", + "description": "Administrator privileges" + } + ] + }, + "roleMappings": [ + { + "username": "bburke@redhat.com", + "roles": ["user"] + } + ], + "scopeMappings": [ + { + "client": "third-party", + "roles": ["user"] + }, + { + "client": "customer-portal", + "roles": ["user"] + }, + { + "client": "product-portal", + "roles": ["user"] + } + + ], + "applications": [ + { + "name": "customer-portal", + "enabled": true, + "adminUrl": "http://localhost:8080/customer-portal", + "redirectUris": [ + "http://localhost:8080/customer-portal/*" + ], + "secret": "password" + }, + { + "name": "product-portal", + "enabled": true, + "adminUrl": "http://localhost:8080/product-portal", + "redirectUris": [ + "http://localhost:8080/product-portal/*" + ], + "secret": "password" + } + ], + "oauthClients": [ + { + "name": "third-party", + "enabled": true, + "redirectUris": [ + "http://localhost:8080/oauth-client/*", + "http://localhost:8080/oauth-client-cdi/*" + ], + "secret": "password" + } + ], + "applicationRoleMappings": { + "account": [ + { + "username": "bburke@redhat.com", + "roles": ["manage-account"] + } + ] + } + +} From 52018b1f8129c92be18117ab286b0b6235a479fc Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Wed, 12 Mar 2014 15:57:43 -0400 Subject: [PATCH 3/3] revoke on logoutAll --- .../admin/partials/application-sessions.html | 4 ++-- .../resources/admin/partials/user-sessions.html | 2 +- .../adapters/action/LogoutAction.java | 11 ++++++++++- .../keycloak/adapters/PreAuthActionsHandler.java | 3 +++ .../main/java/org/keycloak/models/UserModel.java | 3 +++ .../java/org/keycloak/models/jpa/UserAdapter.java | 10 ++++++++++ .../keycloak/models/jpa/entities/UserEntity.java | 9 +++++++++ .../mongo/keycloak/adapters/UserAdapter.java | 10 ++++++++++ .../mongo/keycloak/entities/UserEntity.java | 10 ++++++++++ .../services/managers/AuthenticationManager.java | 9 ++++++++- .../services/managers/ResourceAdminManager.java | 15 +++++++++------ .../keycloak/services/managers/TokenManager.java | 2 +- .../keycloak/services/resources/TokenService.java | 2 +- .../services/resources/admin/UsersResource.java | 4 +++- 14 files changed, 80 insertions(+), 14 deletions(-) diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html index 4a3ecf75af..a7473b0f1a 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html @@ -37,7 +37,7 @@ @@ -52,7 +52,7 @@ {{user}} {{data.whenLoggedIn | date:'medium'}} - logout + invalidate session diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html index 0011ad356f..b702f2505f 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html @@ -33,7 +33,7 @@ {{application}} {{data.whenLoggedIn | date:'medium'}} - logout + invalidate session diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java index ef94ebed86..35576bdc16 100755 --- a/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java +++ b/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java @@ -7,11 +7,12 @@ package org.keycloak.representations.adapters.action; public class LogoutAction extends AdminAction { public static final String LOGOUT = "LOGOUT"; protected String user; + protected int notBefore; public LogoutAction() { } - public LogoutAction(String id, int expiration, String resource, String user) { + public LogoutAction(String id, int expiration, String resource, String user, int notBefore) { super(id, expiration, resource, LOGOUT); this.user = user; } @@ -24,6 +25,14 @@ public class LogoutAction extends AdminAction { this.user = user; } + public int getNotBefore() { + return notBefore; + } + + public void setNotBefore(int notBefore) { + this.notBefore = notBefore; + } + @Override public boolean validate() { return LOGOUT.equals(action); diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java index 6cf53a6d32..370b313927 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java @@ -105,6 +105,9 @@ public class PreAuthActionsHandler { userSessionManagement.logout(user); } else { log.info("logout of all sessions"); + if (action.getNotBefore() > deployment.getNotBefore()) { + deployment.setNotBefore(action.getNotBefore()); + } userSessionManagement.logoutAll(); } } catch (Exception e) { diff --git a/model/api/src/main/java/org/keycloak/models/UserModel.java b/model/api/src/main/java/org/keycloak/models/UserModel.java index 2b2a0cf3f8..95d6613a05 100755 --- a/model/api/src/main/java/org/keycloak/models/UserModel.java +++ b/model/api/src/main/java/org/keycloak/models/UserModel.java @@ -55,6 +55,9 @@ public interface UserModel { void setTotp(boolean totp); + int getNotBefore(); + void setNotBefore(int notBefore); + public static enum RequiredAction { VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java index cff1659f9d..4eec0cab3e 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -144,4 +144,14 @@ public class UserAdapter implements UserModel { public void setTotp(boolean totp) { user.setTotp(totp); } + + @Override + public int getNotBefore() { + return user.getNotBefore(); + } + + @Override + public void setNotBefore(int notBefore) { + user.setNotBefore(notBefore); + } } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java index 51daf7b12d..ab25a647db 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java @@ -46,6 +46,7 @@ public class UserEntity { protected boolean enabled; protected boolean totp; protected boolean emailVerified; + protected int notBefore; @ManyToOne protected RealmEntity realm; @@ -158,4 +159,12 @@ public class UserEntity { public void setCredentials(Collection credentials) { this.credentials = credentials; } + + public int getNotBefore() { + return notBefore; + } + + public void setNotBefore(int notBefore) { + this.notBefore = notBefore; + } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java index abcdffcd02..5d3ed09068 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java @@ -45,6 +45,16 @@ public class UserAdapter extends AbstractMongoAdapter implements Use updateUser(); } + @Override + public int getNotBefore() { + return user.getNotBefore(); + } + + @Override + public void setNotBefore(int notBefore) { + user.setNotBefore(notBefore); + } + @Override public String getFirstName() { return user.getFirstName(); diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java index 753ab8b408..a2f869239d 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java @@ -23,6 +23,7 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo private boolean emailVerified; private boolean totp; private boolean enabled; + private int notBefore; private String realmId; @@ -96,6 +97,15 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo this.totp = totp; } + @MongoField + public int getNotBefore() { + return notBefore; + } + + public void setNotBefore(int notBefore) { + this.notBefore = notBefore; + } + @MongoField public String getRealmId() { return realmId; 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 4d006d9542..b1a473a08c 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -156,12 +156,19 @@ public class AuthenticationManager { } UserModel user = realm.getUserById(token.getSubject()); - if (user == null || !user.isEnabled()) { + if (user == null || !user.isEnabled() ) { logger.info("Unknown user in identity cookie"); expireIdentityCookie(realm, uriInfo); return null; } + if (token.getIssuedAt() < user.getNotBefore()) { + logger.info("Stale cookie"); + expireIdentityCookie(realm, uriInfo); + return null; + + } + return user; } catch (VerificationException e) { logger.info("Failed to verify identity cookie", e); diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java index 96a49f0f01..3c0e74121e 100755 --- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java @@ -108,16 +108,17 @@ public class ResourceAdminManager { } - public void logoutUser(RealmModel realm, String user) { + public void logoutUser(RealmModel realm, UserModel user) { ResteasyClient client = new ResteasyClientBuilder() .disableTrustManager() // todo fix this, should have a trust manager or a good default .build(); try { + // don't set user notBefore as we don't want a database hit on a user driven logout List resources = realm.getApplications(); logger.debug("logging out {0} resources ", resources.size()); for (ApplicationModel resource : resources) { - logoutApplication(realm, resource, user, client); + logoutApplication(realm, resource, user.getId(), client, 0); } } finally { client.close(); @@ -129,10 +130,11 @@ public class ResourceAdminManager { .build(); try { + realm.setNotBefore((int)(System.currentTimeMillis()/1000)); List resources = realm.getApplications(); logger.debug("logging out {0} resources ", resources.size()); for (ApplicationModel resource : resources) { - logoutApplication(realm, resource, null, client); + logoutApplication(realm, resource, null, client, realm.getNotBefore()); } } finally { client.close(); @@ -145,7 +147,8 @@ public class ResourceAdminManager { .build(); try { - logoutApplication(realm, resource, user, client); + resource.setNotBefore((int)(System.currentTimeMillis()/1000)); + logoutApplication(realm, resource, user, client, resource.getNotBefore()); } finally { client.close(); } @@ -153,10 +156,10 @@ public class ResourceAdminManager { } - protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client) { + protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client, int notBefore) { String managementUrl = resource.getManagementUrl(); if (managementUrl != null) { - LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), user); + LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), user, notBefore); String token = new TokenManager().encodeToken(realm, adminAction); logger.info("logout user: {0} resource: {1} url: {2}", user, resource.getName(), managementUrl); Response response = client.target(managementUrl).path(AdapterConstants.K_LOGOUT).request().post(Entity.text(token)); diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java index e1ecc0bade..beb916efe6 100755 --- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java +++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java @@ -131,7 +131,7 @@ public class TokenManager { } - if (refreshToken.getIssuedAt() < client.getNotBefore()) { + if (refreshToken.getIssuedAt() < client.getNotBefore() || refreshToken.getIssuedAt() < user.getNotBefore()) { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token"); } diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index 1f1d8827e7..b9b16739c5 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -580,7 +580,7 @@ public class TokenService { logger.info("Logging out: {0}", user.getLoginName()); authManager.expireIdentityCookie(realm, uriInfo); authManager.expireRememberMeCookie(realm, uriInfo); - resourceAdminManager.logoutUser(realm, user.getId()); + resourceAdminManager.logoutUser(realm, user); } else { logger.info("No user logged in for logout"); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index e3f9bbe845..8aac6918cb 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -176,7 +176,9 @@ public class UsersResource { if (user == null) { throw new NotFoundException(); } - new ResourceAdminManager().logoutUser(realm, user.getId()); + // set notBefore so that user will be forced to log in. + user.setNotBefore((int)(System.currentTimeMillis()/1000)); + new ResourceAdminManager().logoutUser(realm, user); }