keycloak-memoizeit
Changes
audit/jpa/pom.xml 15(+2 -13)
audit/mongo/pom.xml 91(+3 -88)
audit/pom.xml 1(+0 -1)
authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java 18(+9 -9)
authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java 19(+13 -6)
authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProvider.java 13(+4 -9)
authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java 2(+1 -1)
authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProvider.java 5(+5 -0)
authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java 2(+1 -1)
authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java 2(+1 -1)
connections/jpa/pom.xml 39(+39 -0)
connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProvider.java 26(+26 -0)
connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java 102(+102 -0)
connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java 9(+9 -0)
connections/jpa/src/main/java/org/keycloak/connections/jpa/PersistenceExceptionConverter.java 48(+48 -0)
connections/jpa/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory 1(+1 -0)
connections/mongo/pom.xml 37(+37 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/api/context/MongoStoreInvocationContext.java 6(+3 -3)
connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIdentifiableEntity.java 4(+2 -2)
connections/mongo/src/main/java/org/keycloak/connections/mongo/api/types/MapperContext.java 2(+1 -1)
connections/mongo/src/main/java/org/keycloak/connections/mongo/api/types/MapperRegistry.java 2(+1 -1)
connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java 112(+112 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionProvider.java 41(+41 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/SimpleMongoStoreInvocationContext.java 11(+5 -6)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/TransactionMongoStoreInvocationContext.java 12(+6 -6)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBListMapper.java 8(+4 -4)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectMapper.java 25(+12 -13)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectToMapMapper.java 10(+5 -5)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/EnumToStringMapper.java 6(+3 -3)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/MongoEntityMapper.java 15(+8 -7)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/SimpleMapper.java 6(+3 -3)
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/StringToEnumMapper.java 6(+3 -3)
connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProvider.java 19(+19 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java 9(+9 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoKeycloakTransaction.java 6(+3 -3)
connections/mongo/src/main/resources/META-INF/services/org.keycloak.connections.mongo.MongoConnectionProviderFactory 1(+1 -0)
connections/pom.xml 31(+31 -0)
core/src/main/java/org/keycloak/representations/idm/AuthenticationProviderRepresentation.java 19(+19 -0)
core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java 55(+55 -0)
dependencies/pom.xml 32(+32 -0)
dependencies/server-all/pom.xml 159(+159 -0)
dependencies/server-min/pom.xml 157(+157 -0)
distribution/war-zip/pom.xml 2(+1 -1)
docbook/reference/en/en-US/modules/cache.xml 68(+68 -0)
examples/cordova/example-realm.json 8(+6 -2)
examples/cors/cors-realm.json 22(+6 -16)
examples/demo-template/testrealm.json 36(+10 -26)
examples/js-console/example-realm.json 22(+6 -16)
examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java 2(+1 -1)
export-import/export-import-api/pom.xml 26(+26 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java 121(+121 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java 63(+63 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java 18(+18 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java 7(+2 -5)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java 17(+17 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java 10(+3 -7)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java 11(+11 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java 15(+15 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java 47(+47 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java 367(+367 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java 222(+222 -0)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java 114(+114 -0)
export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi 2(+2 -0)
export-import/export-import-dir/pom.xml 66(+66 -0)
export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProvider.java 54(+23 -31)
export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProviderFactory.java 30(+15 -15)
export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java 123(+123 -0)
export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java 34(+34 -0)
export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory 1(+1 -0)
export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory 1(+1 -0)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java 58(+0 -58)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportPropertiesManager.java 152(+0 -152)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java 83(+0 -83)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirImportReader.java 67(+0 -67)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ImportReader.java 13(+0 -13)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPImportReader.java 70(+0 -70)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java 48(+0 -48)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelExporter.java 334(+0 -334)
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java 329(+0 -329)
export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportImportProvider 1(+0 -1)
export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.io.ExportImportIOProvider 2(+0 -2)
export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java 115(+0 -115)
export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java 36(+0 -36)
export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java 69(+0 -69)
export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java 85(+85 -0)
export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java 36(+36 -0)
export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java 59(+59 -0)
export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java 34(+34 -0)
export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory 1(+1 -0)
export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory 1(+1 -0)
export-import/export-import-zip/pom.xml 70(+70 -0)
export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProvider.java 62(+35 -27)
export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java 44(+44 -0)
export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java 124(+124 -0)
export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java 41(+41 -0)
export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory 1(+1 -0)
export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory 1(+1 -0)
export-import/pom.xml 4(+3 -1)
federation/ldap/pom.xml 88(+88 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/KeycloakLDAPIdentityStore.java 89(+89 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java 43(+43 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPKeycloakCredentialHandler.java 168(+168 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/PartitionManagerRegistry.java 158(+158 -0)
federation/ldap/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory 1(+1 -0)
federation/pom.xml 22(+22 -0)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java 2(+1 -1)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java 2(+1 -1)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java 7(+5 -2)
forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js 36(+32 -4)
forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js 38(+31 -7)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-sessions.html 17(+14 -3)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html 17(+16 -1)
integration/js/pom.xml 35(+26 -9)
integration/js/src/main/resources/keycloak.js 53(+41 -12)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java 4(+3 -1)
model/api/pom.xml 5(+5 -0)
model/hybrid/pom.xml 30(+30 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ApplicationAdapter.java 16(+15 -1)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java 9(+6 -3)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheRealmProviderFactory.java 2(+1 -1)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheRealmProviderSpi.java 8(+4 -4)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProvider.java 15(+15 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderFactory.java 11(+11 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java 27(+27 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java 16(+3 -13)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java 258(+60 -198)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java 265(+137 -128)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplication.java 14(+4 -10)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplicationRole.java 5(+3 -2)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java 14(+9 -5)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedOAuthClient.java 7(+3 -4)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java 15(+10 -5)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealmRole.java 8(+3 -5)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java 9(+8 -1)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java 34(+10 -24)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryCacheRealmProviderFactory.java 10(+5 -5)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryCacheUserProviderFactory.java 35(+35 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryRealmCache.java 145(+23 -122)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryUserCache.java 156(+156 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java 112(+112 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProviderFactory.java 6(+3 -3)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java 161(+161 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProviderFactory.java 30(+30 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/OAuthClientAdapter.java 2(+1 -1)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java 211(+18 -193)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmCache.java 17(+3 -14)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java 6(+3 -3)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java 57(+32 -25)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserCache.java 29(+29 -0)
model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheModelProviderFactory 2(+0 -2)
model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory 2(+2 -0)
model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheUserProviderFactory 2(+2 -0)
model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.provider.Spi 3(+2 -1)
model/jpa/pom.xml 63(+12 -51)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java 85(+67 -18)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java 84(+0 -84)
model/mongo/pom.xml 121(+2 -119)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractMongoAdapter.java 6(+3 -3)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java 20(+17 -3)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java 31(+10 -21)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoModelProviderFactory.java 101(+0 -101)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java 131(+131 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java 38(+38 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java 312(+101 -211)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProviderFactory.java 38(+38 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java 3(+1 -2)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java 265(+39 -226)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java 20(+4 -16)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java 14(+4 -10)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java 11(+4 -7)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java 16(+8 -8)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java 19(+6 -13)
model/pom.xml 5(+3 -2)
model/realms-jpa/pom.xml 45(+45 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ApplicationEntity.java 77(+77 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/AuthenticationProviderEntity.java 79(+79 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ClientEntity.java 126(+126 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/OAuthClientEntity.java 27(+27 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RealmEntity.java 479(+479 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RequiredCredentialEntity.java 64(+64 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ScopeMappingEntity.java 32(+20 -12)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaKeycloakTransaction.java 53(+53 -0)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaRealmProviderFactory.java 15(+8 -7)
model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/PersistenceExceptionConverter.java 48(+48 -0)
model/realms-jpa/src/main/resources/META-INF/services/org.keycloak.models.realms.RealmProviderFactory 1(+1 -0)
model/sessions-jpa/pom.xml 47(+47 -0)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientUserSessionAssociationEntity.java 97(+97 -0)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UsernameLoginFailureEntity.java 94(+72 -22)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionEntity.java 85(+58 -27)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaKeycloakTransaction.java 53(+53 -0)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java 198(+198 -0)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProviderFactory.java 37(+37 -0)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/PersistenceExceptionConverter.java 48(+48 -0)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/UsernameLoginFailureAdapter.java 7(+3 -4)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/UserSessionAdapter.java 65(+46 -19)
model/sessions-jpa/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory 1(+1 -0)
model/sessions-mem/pom.xml 31(+31 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureEntity.java 65(+65 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureKey.java 36(+36 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java 102(+102 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionKey.java 36(+36 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java 227(+227 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java 45(+45 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UsernameLoginFailureAdapter.java 71(+71 -0)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java 135(+135 -0)
model/sessions-mem/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory 1(+1 -0)
model/sessions-mongo/pom.xml 29(+9 -20)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/AbstractMongoAdapter.java 44(+44 -0)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUsernameLoginFailureEntity.java 9(+5 -4)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java 42(+36 -6)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java 206(+206 -0)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProviderFactory.java 35(+35 -0)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UsernameLoginFailureAdapter.java 6(+3 -3)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java 54(+42 -12)
model/sessions-mongo/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory 1(+1 -0)
model/users-jpa/pom.xml 79(+79 -0)
model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserAttributeEntity.java 79(+79 -0)
model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserCredentialEntity.java 106(+106 -0)
model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserRoleMappingEntity.java 68(+68 -0)
model/users-jpa/src/main/java/org/keycloak/models/users/jpa/PersistenceExceptionConverter.java 48(+48 -0)
model/users-jpa/src/main/resources/META-INF/services/org.keycloak.models.users.UserProviderFactory 1(+1 -0)
picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java 2(+1 -1)
pom.xml 51(+14 -37)
project-integrations/aerogear-ups/auth-server/pom.xml 174(+14 -160)
project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java 4(+2 -2)
project-integrations/aerogear-ups/auth-server/src/main/resources/META-INF/keycloak-server.json 42(+39 -3)
project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml 9(+9 -0)
project-integrations/README.md 1(+0 -1)
server/pom.xml 261(+2 -259)
testsuite/integration/pom.xml 322(+101 -221)
testsuite/integration/README.md 7(+4 -3)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java 9(+5 -4)
testsuite/integration/src/test/java/org/keycloak/testsuite/audit/AuditProviderTest.java 134(+134 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java 3(+2 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java 33(+24 -9)
testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java 385(+385 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java 11(+6 -5)
testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java 179(+179 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/ApplicationModelTest.java 11(+7 -4)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthenticationManagerTest.java 50(+22 -28)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthProvidersConfigTest.java 16(+8 -8)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthProvidersExternalModelTest.java 53(+26 -27)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthProvidersLDAPTest.java 58(+27 -31)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java 18(+9 -9)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java 47(+24 -23)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java 218(+218 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java 63(+48 -15)
testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java 2(+1 -1)
testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java 7(+4 -3)
testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java 7(+3 -4)
testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java 12(+6 -6)
testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java 2(+1 -1)
testsuite/performance-web/pom.xml 4(+2 -2)
testsuite/performance-web/README.md 14(+12 -2)
testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java 1(+1 -0)
testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java 9(+6 -3)
testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java 5(+4 -1)
testsuite/tools/pom.xml 275(+16 -259)
Details
diff --git a/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListener.java b/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListener.java
old mode 100644
new mode 100755
index 53a8fcd..6f50c4e
--- a/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListener.java
+++ b/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListener.java
@@ -7,6 +7,7 @@ import org.keycloak.audit.EventType;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
@@ -20,11 +21,13 @@ public class EmailAuditListener implements AuditListener {
private static final Logger log = Logger.getLogger(EmailAuditListener.class);
private KeycloakSession session;
+ private RealmProvider model;
private EmailProvider emailProvider;
private Set<EventType> includedEvents;
public EmailAuditListener(KeycloakSession session, EmailProvider emailProvider, Set<EventType> includedEvents) {
this.session = session;
+ this.model = session.realms();
this.emailProvider = emailProvider;
this.includedEvents = includedEvents;
}
@@ -33,8 +36,8 @@ public class EmailAuditListener implements AuditListener {
public void onEvent(Event event) {
if (includedEvents.contains(event.getEvent())) {
if (event.getRealmId() != null && event.getUserId() != null) {
- RealmModel realm = session.getRealm(event.getRealmId());
- UserModel user = realm.getUserById(event.getUserId());
+ RealmModel realm = model.getRealm(event.getRealmId());
+ UserModel user = session.users().getUserById(event.getUserId(), realm);
if (user != null && user.getEmail() != null && user.isEmailVerified()) {
try {
emailProvider.setRealm(realm).setUser(user).sendEvent(event);
audit/jpa/pom.xml 15(+2 -13)
diff --git a/audit/jpa/pom.xml b/audit/jpa/pom.xml
index db0a350..dd725b8 100755
--- a/audit/jpa/pom.xml
+++ b/audit/jpa/pom.xml
@@ -27,13 +27,13 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-api</artifactId>
+ <artifactId>keycloak-connections-jpa</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-tests</artifactId>
+ <artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
@@ -58,16 +58,5 @@
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
-
</project>
diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/EventEntity.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/EventEntity.java
index f191813..b22b576 100644
--- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/EventEntity.java
+++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/EventEntity.java
@@ -3,33 +3,44 @@ package org.keycloak.audit.jpa;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
+import javax.persistence.Table;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Entity
+@Table(name="EVENT_ENTITY")
public class EventEntity {
@Id
+ @Column(name="ID", length = 36)
private String id;
+ @Column(name="TIME")
private long time;
+ @Column(name="EVENT")
private String event;
+ @Column(name="REALM_ID")
private String realmId;
+ @Column(name="CLIENT_ID")
private String clientId;
+ @Column(name="USER_ID")
private String userId;
+ @Column(name="SESSION_ID")
private String sessionId;
+ @Column(name="IP_ADDRESS")
private String ipAddress;
+ @Column(name="ERROR")
private String error;
- @Column(length = 2550)
+ @Column(name="DETAILS_JSON", length = 2550)
private String detailsJson;
public String getId() {
diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java
index d6366b3..ee2f7e9 100644
--- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java
+++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java
@@ -41,44 +41,28 @@ public class JpaAuditProvider implements AuditProvider {
@Override
public void clear() {
- beginTx();
em.createQuery("delete from EventEntity").executeUpdate();
}
@Override
public void clear(String realmId) {
- beginTx();
em.createQuery("delete from EventEntity where realmId = :realmId").setParameter("realmId", realmId).executeUpdate();
}
@Override
public void clear(String realmId, long olderThan) {
- beginTx();
em.createQuery("delete from EventEntity where realmId = :realmId and time < :time").setParameter("realmId", realmId).setParameter("time", olderThan).executeUpdate();
}
@Override
public void onEvent(Event event) {
if (includedEvents.contains(event.getEvent())) {
- beginTx();
em.persist(convert(event));
}
}
@Override
public void close() {
- if (tx != null) {
- tx.commit();
- }
-
- em.close();
- }
-
- private void beginTx() {
- if (tx == null) {
- tx = em.getTransaction();
- tx.begin();
- }
}
static EventEntity convert(Event o) {
diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java
index 2e01171..c471a7d 100644
--- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java
+++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java
@@ -4,11 +4,9 @@ import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.audit.EventType;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.util.JpaUtils;
-import javax.persistence.EntityManagerFactory;
-import javax.persistence.Persistence;
import java.util.HashSet;
import java.util.Set;
@@ -18,19 +16,17 @@ import java.util.Set;
public class JpaAuditProviderFactory implements AuditProviderFactory {
public static final String ID = "jpa";
- private EntityManagerFactory emf;
private Set<EventType> includedEvents = new HashSet<EventType>();
@Override
public AuditProvider create(KeycloakSession session) {
- return new JpaAuditProvider(emf.createEntityManager(), includedEvents);
+ JpaConnectionProvider connection = session.getProvider(JpaConnectionProvider.class);
+ return new JpaAuditProvider(connection.getEntityManager(), includedEvents);
}
@Override
public void init(Config.Scope config) {
- emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store", JpaUtils.getHibernateProperties());
-
String[] include = config.getArray("include-events");
if (include != null) {
for (String i : include) {
@@ -52,7 +48,6 @@ public class JpaAuditProviderFactory implements AuditProviderFactory {
@Override
public void close() {
- emf.close();
}
@Override
audit/mongo/pom.xml 91(+3 -88)
diff --git a/audit/mongo/pom.xml b/audit/mongo/pom.xml
index db80dda..e928784 100755
--- a/audit/mongo/pom.xml
+++ b/audit/mongo/pom.xml
@@ -27,15 +27,15 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-api</artifactId>
+ <artifactId>keycloak-connections-mongo</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-tests</artifactId>
+ <artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
- <scope>test</scope>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
@@ -47,90 +47,5 @@
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
-
- <properties>
- <keycloak.audit.mongo.host>localhost</keycloak.audit.mongo.host>
- <keycloak.audit.mongo.port>27018</keycloak.audit.mongo.port>
- <keycloak.audit.mongo.db>keycloak</keycloak.audit.mongo.db>
- <keycloak.audit.mongo.clearOnStartup>true</keycloak.audit.mongo.clearOnStartup>
- <keycloak.audit.mongo.bindIp>127.0.0.1</keycloak.audit.mongo.bindIp>
- </properties>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- </configuration>
- </plugin>
-
- <!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <executions>
- <execution>
- <id>test</id>
- <phase>integration-test</phase>
- <goals>
- <goal>test</goal>
- </goals>
- <configuration>
- <systemPropertyVariables>
- <keycloak.audit.mongo.host>${keycloak.audit.mongo.host}</keycloak.audit.mongo.host>
- <keycloak.audit.mongo.port>${keycloak.audit.mongo.port}</keycloak.audit.mongo.port>
- <keycloak.audit.mongo.db>${keycloak.audit.mongo.db}</keycloak.audit.mongo.db>
- <keycloak.audit.mongo.clearOnStartup>${keycloak.audit.mongo.clearOnStartup}</keycloak.audit.mongo.clearOnStartup>
- <keycloak.audit.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.audit.mongo.bindIp>
- </systemPropertyVariables>
- </configuration>
- </execution>
- <execution>
- <id>default-test</id>
- <configuration>
- <skip>true</skip>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <!-- Embedded mongo -->
- <plugin>
- <groupId>com.github.joelittlejohn.embedmongo</groupId>
- <artifactId>embedmongo-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>start-mongodb</id>
- <phase>pre-integration-test</phase>
- <goals>
- <goal>start</goal>
- </goals>
- <configuration>
- <port>${keycloak.audit.mongo.port}</port>
- <logging>file</logging>
- <logFile>${project.build.directory}/mongodb.log</logFile>
- <bindIp>${keycloak.audit.mongo.bindIp}</bindIp>
- </configuration>
- </execution>
- <execution>
- <id>stop-mongodb</id>
- <phase>post-integration-test</phase>
- <goals>
- <goal>stop</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- </plugins>
- </build>
</project>
diff --git a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java
index d27c40e..79f4f5d 100644
--- a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java
+++ b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java
@@ -1,19 +1,15 @@
package org.keycloak.audit.mongo;
-import com.mongodb.DB;
-import com.mongodb.MongoClient;
-import com.mongodb.MongoCredential;
-import com.mongodb.ServerAddress;
+import com.mongodb.DBCollection;
import com.mongodb.WriteConcern;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.audit.EventType;
+import org.keycloak.connections.mongo.MongoConnectionProvider;
import org.keycloak.models.KeycloakSession;
-import java.net.UnknownHostException;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -25,45 +21,21 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
protected static final Logger logger = Logger.getLogger(MongoAuditProviderFactory.class);
public static final String ID = "mongo";
- private MongoClient client;
- private DB db;
private Set<EventType> includedEvents = new HashSet<EventType>();
@Override
public AuditProvider create(KeycloakSession session) {
- return new MongoAuditProvider(db.getCollection("audit"), includedEvents);
+ MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
+
+ DBCollection collection = connection.getDB().getCollection("audit");
+ collection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
+
+ return new MongoAuditProvider(collection, includedEvents);
}
@Override
public void init(Config.Scope config) {
- try {
- String host = config.get("host", ServerAddress.defaultHost());
- int port = config.getInt("port", ServerAddress.defaultPort());
- String dbName = config.get("db", "keycloak-audit");
- boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
-
- String user = config.get("user");
- String password = config.get("password");
- if (user != null && password != null) {
- MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
- client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
- } else {
- client = new MongoClient(host, port);
- }
-
- client.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
- db = client.getDB(dbName);
-
- if (clearOnStartup) {
- db.getCollection("audit").drop();
- }
-
- logger.infof("Initialized mongo audit. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
-
String[] include = config.getArray("include-events");
if (include != null) {
for (String i : include) {
@@ -85,7 +57,6 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
@Override
public void close() {
- client.close();
}
@Override
audit/pom.xml 1(+0 -1)
diff --git a/audit/pom.xml b/audit/pom.xml
index b6ae9e3..0f210d3 100755
--- a/audit/pom.xml
+++ b/audit/pom.xml
@@ -21,6 +21,5 @@
<module>jpa</module>
<module>jboss-logging</module>
<module>mongo</module>
- <module>tests</module>
</modules>
</project>
diff --git a/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java
index 20d08bb..9c81665 100755
--- a/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java
+++ b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java
@@ -70,7 +70,7 @@ public class AuthenticationProviderManager {
AuthenticationLinkModel authLink = user.getAuthenticationLink();
if (authLink == null) {
// User not yet linked with any authenticationProvider. Find provider with biggest priority where he is and link
- AuthUser authUser = getUser(user.getLoginName());
+ AuthUser authUser = getUser(user.getUsername());
authLink = new AuthenticationLinkModel(authUser.getProviderName(), authUser.getId());
user.setAuthenticationLink(authLink);
logger.infof("User '%s' linked with provider '%s'", authUser.getUsername(), authUser.getProviderName());
@@ -85,10 +85,10 @@ public class AuthenticationProviderManager {
}
try {
- checkCorrectAuthLink(delegate, providerModel, authLink, user.getLoginName());
+ checkCorrectAuthLink(delegate, providerModel, authLink, user.getUsername());
- AuthProviderStatus currentResult = delegate.validatePassword(realm, providerModel.getConfig(), user.getLoginName(), password);
- logger.debugf("Authentication provider '%s' finished with '%s' for authentication of '%s'", delegate.getName(), currentResult.toString(), user.getLoginName());
+ AuthProviderStatus currentResult = delegate.validatePassword(realm, providerModel.getConfig(), user.getUsername(), password);
+ logger.debugf("Authentication provider '%s' finished with '%s' for authentication of '%s'", delegate.getName(), currentResult.toString(), user.getUsername());
return currentResult;
} catch (AuthenticationProviderException ape) {
logger.warn(ape.getMessage(), ape);
@@ -105,7 +105,7 @@ public class AuthenticationProviderManager {
if (providerModel.isPasswordUpdateSupported()) {
AuthenticationProvider delegate = getProvider(providerModel.getProviderName());
if (delegate != null) {
- AuthUser authUser = delegate.getUser(realm, providerModel.getConfig(), user.getLoginName());
+ AuthUser authUser = delegate.getUser(realm, providerModel.getConfig(), user.getUsername());
if (authUser != null) {
// Linking existing user supported just for "model" provider. In other cases throw exception
if (providerModel.getProviderName().equals(AuthenticationProviderModel.DEFAULT_PROVIDER.getProviderName())) {
@@ -120,7 +120,7 @@ public class AuthenticationProviderManager {
String userIdInProvider = delegate.registerUser(realm, providerModel.getConfig(), user);
authLink = new AuthenticationLinkModel(providerModel.getProviderName(), userIdInProvider);
user.setAuthenticationLink(authLink);
- logger.infof("User '%s' registered in provider '%s' and linked", user.getLoginName(), providerModel.getProviderName());
+ logger.infof("User '%s' registered in provider '%s' and linked", user.getUsername(), providerModel.getProviderName());
}
break;
}
@@ -128,7 +128,7 @@ public class AuthenticationProviderManager {
}
if (authLink == null) {
- logger.warnf("No providers found where password update is supported for user '%s'", user.getLoginName());
+ logger.warnf("No providers found where password update is supported for user '%s'", user.getUsername());
return false;
}
}
@@ -140,7 +140,7 @@ public class AuthenticationProviderManager {
return false;
}
- String username = user.getLoginName();
+ String username = user.getUsername();
// Update just if password update is supported
if (providerModel.isPasswordUpdateSupported()) {
@@ -152,7 +152,7 @@ public class AuthenticationProviderManager {
checkCorrectAuthLink(delegate, providerModel, authLink, username);
- if (delegate.updateCredential(realm,providerModel.getConfig(), user.getLoginName(), password)) {
+ if (delegate.updateCredential(realm,providerModel.getConfig(), user.getUsername(), password)) {
logger.debugf("Updated password in authentication provider '%s' for user '%s'", providerName, username);
return true;
} else {
diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java
index ea20abc..63c0ec5 100755
--- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java
+++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java
@@ -3,6 +3,7 @@ package org.keycloak.authentication.model;
import java.util.Map;
import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
@@ -22,17 +23,23 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
private static final Logger logger = Logger.getLogger(AbstractModelAuthenticationProvider.class);
+ protected KeycloakSession keycloakSession;
+
+ protected AbstractModelAuthenticationProvider(KeycloakSession keycloakSession) {
+ this.keycloakSession = keycloakSession;
+ }
+
@Override
public AuthUser getUser(RealmModel currentRealm, Map<String, String> config, String username) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
- UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
+ UserModel user = KeycloakModelUtils.findUserByNameOrEmail(keycloakSession, realm, username);
return user == null ? null : createAuthenticatedUserInstance(user);
}
@Override
public String registerUser(RealmModel currentRealm, Map<String, String> config, UserModel user) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
- UserModel newUser = realm.addUser(user.getLoginName());
+ UserModel newUser = keycloakSession.users().addUser(realm, user.getUsername());
newUser.setFirstName(user.getFirstName());
newUser.setLastName(user.getLastName());
newUser.setEmail(user.getEmail());
@@ -43,9 +50,9 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
@Override
public AuthProviderStatus validatePassword(RealmModel currentRealm, Map<String, String> config, String username, String password) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
- UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
+ UserModel user = KeycloakModelUtils.findUserByNameOrEmail(keycloakSession, realm, username);
- boolean result = realm.validatePassword(user, password);
+ boolean result = keycloakSession.users().validCredentials(realm, user, UserCredentialModel.password(password));
return result ? AuthProviderStatus.SUCCESS : AuthProviderStatus.INVALID_CREDENTIALS;
}
@@ -59,7 +66,7 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
throw new AuthenticationProviderException(error);
}
- UserModel user = realm.getUser(username);
+ UserModel user = keycloakSession.users().getUserByUsername(username, realm);
if (user == null) {
logger.warnf("User '%s' doesn't exists. Skip password update", username);
return false;
@@ -80,7 +87,7 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
protected abstract RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) throws AuthenticationProviderException;
protected AuthUser createAuthenticatedUserInstance(UserModel user) {
- return new AuthUser(user.getId(), user.getLoginName(), getName())
+ return new AuthUser(user.getId(), user.getUsername(), getName())
.setName(user.getFirstName(), user.getLastName())
.setEmail(user.getEmail());
}
diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProvider.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProvider.java
old mode 100644
new mode 100755
index 9381bb9..e109360
--- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProvider.java
+++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProvider.java
@@ -1,6 +1,5 @@
package org.keycloak.authentication.model;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProviderException;
import org.keycloak.models.KeycloakSession;
@@ -17,7 +16,9 @@ import java.util.Map;
*/
public class ExternalModelAuthenticationProvider extends AbstractModelAuthenticationProvider {
- public ExternalModelAuthenticationProvider() {
+
+ public ExternalModelAuthenticationProvider(KeycloakSession session) {
+ super(session);
}
@Override
@@ -37,13 +38,7 @@ public class ExternalModelAuthenticationProvider extends AbstractModelAuthentica
throw new AuthenticationProviderException("Option '" + AuthProviderConstants.EXTERNAL_REALM_ID + "' not specified in configuration");
}
- // TODO: This won't be needed when KeycloakSession is available from ProviderSession
- KeycloakSession session = ResteasyProviderFactory.getContextData(KeycloakSession.class);
- if (session == null) {
- throw new AuthenticationProviderException("KeycloakSession not available");
- }
-
- RealmModel realm = session.getRealm(realmId);
+ RealmModel realm = keycloakSession.realms().getRealm(realmId);
if (realm == null) {
throw new AuthenticationProviderException("Realm with id '" + realmId + "' doesn't exists");
}
diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java
index 7f19623..7ef0a5d 100644
--- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java
+++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java
@@ -13,7 +13,7 @@ public class ExternalModelAuthenticationProviderFactory implements Authenticatio
@Override
public AuthenticationProvider create(KeycloakSession session) {
- return new ExternalModelAuthenticationProvider();
+ return new ExternalModelAuthenticationProvider(session);
}
@Override
diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProvider.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProvider.java
old mode 100644
new mode 100755
index e46514d..210d56d
--- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProvider.java
+++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProvider.java
@@ -4,6 +4,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.authentication.AuthProviderConstants;
@@ -14,6 +15,10 @@ import org.keycloak.authentication.AuthProviderConstants;
*/
public class ModelAuthenticationProvider extends AbstractModelAuthenticationProvider {
+ public ModelAuthenticationProvider(KeycloakSession keycloakSession) {
+ super(keycloakSession);
+ }
+
@Override
public String getName() {
return AuthProviderConstants.PROVIDER_NAME_MODEL;
diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java
old mode 100644
new mode 100755
index ac8c393..bf699e3
--- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java
+++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java
@@ -13,7 +13,7 @@ public class ModelAuthenticationProviderFactory implements AuthenticationProvide
@Override
public AuthenticationProvider create(KeycloakSession session) {
- return new ModelAuthenticationProvider();
+ return new ModelAuthenticationProvider(session);
}
@Override
diff --git a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
index 772f6da..6cbcaad 100755
--- a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
+++ b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
@@ -72,7 +72,7 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
IdentityManager identityManager = getIdentityManager(realm);
try {
- User picketlinkUser = new User(user.getLoginName());
+ User picketlinkUser = new User(user.getUsername());
picketlinkUser.setFirstName(user.getFirstName());
picketlinkUser.setLastName(user.getLastName());
picketlinkUser.setEmail(user.getEmail());
connections/jpa/pom.xml 39(+39 -0)
diff --git a/connections/jpa/pom.xml b/connections/jpa/pom.xml
new file mode 100755
index 0000000..7301e3b
--- /dev/null
+++ b/connections/jpa/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-connections-jpa</artifactId>
+ <name>Keycloak Connections JPA</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.javax.persistence</groupId>
+ <artifactId>hibernate-jpa-2.0-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>${hibernate.entitymanager.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProvider.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProvider.java
new file mode 100644
index 0000000..793e60e
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProvider.java
@@ -0,0 +1,26 @@
+package org.keycloak.connections.jpa;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultJpaConnectionProvider implements JpaConnectionProvider {
+
+ private final EntityManager em;
+
+ public DefaultJpaConnectionProvider(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public EntityManager getEntityManager() {
+ return em;
+ }
+
+ @Override
+ public void close() {
+ em.close();
+ }
+
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
new file mode 100644
index 0000000..e39877b
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -0,0 +1,102 @@
+package org.keycloak.connections.jpa;
+
+import org.hibernate.ejb.AvailableSettings;
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
+
+ private volatile EntityManagerFactory emf;
+
+ private Config.Scope config;
+
+ @Override
+ public JpaConnectionProvider create(KeycloakSession session) {
+ lazyInit();
+
+ EntityManager em = emf.createEntityManager();
+ em = PersistenceExceptionConverter.create(em);
+ session.getTransaction().enlist(new JpaKeycloakTransaction(em));
+ return new DefaultJpaConnectionProvider(em);
+ }
+
+ @Override
+ public void close() {
+ if (emf != null) {
+ emf.close();
+ }
+ }
+
+ @Override
+ public String getId() {
+ return "default";
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ this.config = config;
+ }
+
+ private void lazyInit() {
+ if (emf == null) {
+ synchronized (this) {
+ if (emf == null) {
+ String unitName = config.get("unitName");
+ Map<String, Object> properties = new HashMap<String, Object>();
+
+ // Only load config from keycloak-server.json if unitName is not specified
+ if (unitName == null) {
+ unitName = "keycloak-default";
+
+ String dataSource = config.get("dataSource");
+ if (dataSource != null) {
+ if (config.getBoolean("jta", false)) {
+ properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
+ } else {
+ properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
+ }
+ } else {
+ properties.put(AvailableSettings.JDBC_URL, config.get("url"));
+ properties.put(AvailableSettings.JDBC_DRIVER, config.get("driver"));
+
+ String driverDialect = config.get("driverDialect");
+ if (driverDialect != null && driverDialect.length() > 0) {
+ properties.put("hibernate.dialect", driverDialect);
+ }
+
+ String user = config.get("user");
+ if (user != null) {
+ properties.put(AvailableSettings.JDBC_USER, user);
+ }
+ String password = config.get("password");
+ if (password != null) {
+ properties.put(AvailableSettings.JDBC_PASSWORD, password);
+ }
+ }
+
+ String databaseSchema = config.get("databaseSchema", "validate");
+ if (databaseSchema != null) {
+ properties.put("hibernate.hbm2ddl.auto", databaseSchema);
+ }
+
+ properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
+ properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
+ }
+
+ emf = Persistence.createEntityManagerFactory(unitName, properties);
+ }
+ }
+ }
+ }
+
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProvider.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProvider.java
new file mode 100644
index 0000000..7c4ced4
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProvider.java
@@ -0,0 +1,14 @@
+package org.keycloak.connections.jpa;
+
+import org.keycloak.provider.Provider;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface JpaConnectionProvider extends Provider {
+
+ EntityManager getEntityManager();
+
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java
new file mode 100644
index 0000000..1cf4a5f
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.connections.jpa;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionSpi.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionSpi.java
new file mode 100644
index 0000000..83859ab
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.connections.jpa;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class JpaConnectionSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "connectionsJpa";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return JpaConnectionProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return JpaConnectionProviderFactory.class;
+ }
+
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaKeycloakTransaction.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaKeycloakTransaction.java
new file mode 100755
index 0000000..4f42fe7
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaKeycloakTransaction.java
@@ -0,0 +1,53 @@
+package org.keycloak.connections.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaKeycloakTransaction implements KeycloakTransaction {
+
+ protected EntityManager em;
+
+ public JpaKeycloakTransaction(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public void begin() {
+ em.getTransaction().begin();
+ }
+
+ @Override
+ public void commit() {
+ try {
+ em.getTransaction().commit();
+ } catch (PersistenceException e) {
+ throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
+ }
+ }
+
+ @Override
+ public void rollback() {
+ em.getTransaction().rollback();
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ em.getTransaction().setRollbackOnly();
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ return em.getTransaction().getRollbackOnly();
+ }
+
+ @Override
+ public boolean isActive() {
+ return em.getTransaction().isActive();
+ }
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/PersistenceExceptionConverter.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/PersistenceExceptionConverter.java
new file mode 100644
index 0000000..443d7bf
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/PersistenceExceptionConverter.java
@@ -0,0 +1,48 @@
+package org.keycloak.connections.jpa;
+
+import org.hibernate.exception.ConstraintViolationException;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class PersistenceExceptionConverter implements InvocationHandler {
+
+ private EntityManager em;
+
+ public static EntityManager create(EntityManager em) {
+ return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
+ }
+
+ private PersistenceExceptionConverter(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ return method.invoke(em, args);
+ } catch (InvocationTargetException e) {
+ throw convert(e.getCause());
+ }
+ }
+
+ public static ModelException convert(Throwable t) {
+ if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
+ throw new ModelDuplicateException(t);
+ } if (t instanceof EntityExistsException) {
+ throw new ModelDuplicateException(t);
+ } else {
+ throw new ModelException(t);
+ }
+ }
+
+}
diff --git a/connections/jpa/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory
new file mode 100644
index 0000000..68bc9ae
--- /dev/null
+++ b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory
@@ -0,0 +1 @@
+org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory
\ No newline at end of file
diff --git a/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000..d7b47a8
--- /dev/null
+++ b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1 @@
+org.keycloak.connections.jpa.JpaConnectionSpi
\ No newline at end of file
connections/mongo/pom.xml 37(+37 -0)
diff --git a/connections/mongo/pom.xml b/connections/mongo/pom.xml
new file mode 100755
index 0000000..59643ab
--- /dev/null
+++ b/connections/mongo/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-connections-mongo</artifactId>
+ <name>Keycloak Connections Mongo</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mongodb</groupId>
+ <artifactId>mongo-java-driver</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
new file mode 100644
index 0000000..cea49ff
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -0,0 +1,112 @@
+package org.keycloak.connections.mongo;
+
+import com.mongodb.DB;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.impl.MongoStoreImpl;
+import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocationContext;
+import org.keycloak.models.KeycloakSession;
+
+import java.util.Collections;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory {
+
+ // TODO Make configurable
+ private String[] entities = new String[]{
+ "org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity",
+ "org.keycloak.models.mongo.keycloak.entities.MongoUserEntity",
+ "org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity",
+ "org.keycloak.models.entities.RequiredCredentialEntity",
+ "org.keycloak.models.entities.AuthenticationProviderEntity",
+ "org.keycloak.models.entities.CredentialEntity",
+ "org.keycloak.models.entities.SocialLinkEntity",
+ "org.keycloak.models.entities.AuthenticationLinkEntity",
+ "org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity",
+ "org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity",
+ "org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
+ "org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
+ "org.keycloak.models.entities.FederationProviderEntity"
+ };
+
+ private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
+
+ private volatile MongoClient client;
+
+ private MongoStore mongoStore;
+ private DB db;
+ private Config.Scope config;
+
+ @Override
+ public MongoConnectionProvider create(KeycloakSession session) {
+ lazyInit();
+
+ TransactionMongoStoreInvocationContext invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
+ session.getTransaction().enlist(new MongoKeycloakTransaction(invocationContext));
+ return new DefaultMongoConnectionProvider(db, mongoStore, invocationContext);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ this.config = config;
+ }
+
+ private void lazyInit() {
+ if (client == null) {
+ synchronized (this) {
+ if (client == null) {
+ try {
+ String host = config.get("host", ServerAddress.defaultHost());
+ int port = config.getInt("port", ServerAddress.defaultPort());
+ String dbName = config.get("db", "keycloak");
+ boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
+
+ String user = config.get("user");
+ String password = config.get("password");
+ if (user != null && password != null) {
+ MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
+ client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
+ } else {
+ client = new MongoClient(host, port);
+ }
+
+ this.db = client.getDB(dbName);
+
+ this.mongoStore = new MongoStoreImpl(db, clearOnStartup, getManagedEntities());
+
+ logger.infof("Initialized mongo model. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+ private Class[] getManagedEntities() throws ClassNotFoundException {
+ Class[] entityClasses = new Class[entities.length];
+ for (int i = 0; i < entities.length; i++) {
+ entityClasses[i] = Thread.currentThread().getContextClassLoader().loadClass(entities[i]);
+ }
+ return entityClasses;
+ }
+
+ @Override
+ public void close() {
+ if (client != null) {
+ client.close();
+ }
+ }
+
+ @Override
+ public String getId() {
+ return "default";
+ }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionProvider.java
new file mode 100644
index 0000000..4b08686
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionProvider.java
@@ -0,0 +1,41 @@
+package org.keycloak.connections.mongo;
+
+import com.mongodb.DB;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultMongoConnectionProvider implements MongoConnectionProvider {
+
+ private DB db;
+ private MongoStore mongoStore;
+ private MongoStoreInvocationContext invocationContext;
+
+ public DefaultMongoConnectionProvider(DB db, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
+ this.db = db;
+ this.mongoStore = mongoStore;
+ this.invocationContext = invocationContext;
+ }
+
+ @Override
+ public DB getDB() {
+ return db;
+ }
+
+ @Override
+ public MongoStore getMongoStore() {
+ return mongoStore;
+ }
+
+ @Override
+ public MongoStoreInvocationContext getInvocationContext() {
+ return invocationContext;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProvider.java
new file mode 100644
index 0000000..976c9c8
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProvider.java
@@ -0,0 +1,19 @@
+package org.keycloak.connections.mongo;
+
+import com.mongodb.DB;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface MongoConnectionProvider extends Provider {
+
+ DB getDB();
+
+ MongoStore getMongoStore();
+
+ MongoStoreInvocationContext getInvocationContext();
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java
new file mode 100644
index 0000000..e787ce6
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.connections.mongo;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionSpi.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionSpi.java
new file mode 100644
index 0000000..031076d
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.connections.mongo;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MongoConnectionSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "connectionsMongo";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return MongoConnectionProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return MongoConnectionProviderFactory.class;
+ }
+
+}
diff --git a/connections/mongo/src/main/resources/META-INF/services/org.keycloak.connections.mongo.MongoConnectionProviderFactory b/connections/mongo/src/main/resources/META-INF/services/org.keycloak.connections.mongo.MongoConnectionProviderFactory
new file mode 100644
index 0000000..d9927c1
--- /dev/null
+++ b/connections/mongo/src/main/resources/META-INF/services/org.keycloak.connections.mongo.MongoConnectionProviderFactory
@@ -0,0 +1 @@
+org.keycloak.connections.mongo.DefaultMongoConnectionFactoryProvider
\ No newline at end of file
diff --git a/connections/mongo/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/connections/mongo/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000..90eedc3
--- /dev/null
+++ b/connections/mongo/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1 @@
+org.keycloak.connections.mongo.MongoConnectionSpi
\ No newline at end of file
connections/pom.xml 31(+31 -0)
diff --git a/connections/pom.xml b/connections/pom.xml
new file mode 100755
index 0000000..9887b35
--- /dev/null
+++ b/connections/pom.xml
@@ -0,0 +1,31 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ </parent>
+ <name>Connections Parent</name>
+ <description/>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-connections-pom</artifactId>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>jpa</module>
+ <module>mongo</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/core/src/main/java/org/keycloak/representations/AccessCode.java b/core/src/main/java/org/keycloak/representations/AccessCode.java
index 1ecebb2..ef61202 100755
--- a/core/src/main/java/org/keycloak/representations/AccessCode.java
+++ b/core/src/main/java/org/keycloak/representations/AccessCode.java
@@ -1,6 +1,5 @@
package org.keycloak.representations;
-import java.util.HashSet;
import java.util.Set;
/**
@@ -10,15 +9,14 @@ import java.util.Set;
*/
public class AccessCode {
protected String id;
- protected String usernameUsed;
+ protected String clientId;
+ protected String userId;
protected String state;
+ protected String sessionState;
protected String redirectUri;
- protected boolean rememberMe;
- protected String authMethod;
protected int timestamp;
- protected int expiration;
- protected AccessToken accessToken;
- protected Set<String> requiredActions;
+ protected Action action;
+ protected Set<String> requestedRoles;
public String getId() {
return id;
@@ -28,52 +26,44 @@ public class AccessCode {
this.id = id;
}
- public String getState() {
- return state;
- }
-
- public void setState(String state) {
- this.state = state;
+ public String getClientId() {
+ return clientId;
}
- public String getRedirectUri() {
- return redirectUri;
- }
-
- public void setRedirectUri(String redirectUri) {
- this.redirectUri = redirectUri;
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
}
- public boolean isRememberMe() {
- return rememberMe;
+ public String getUserId() {
+ return userId;
}
- public void setRememberMe(boolean rememberMe) {
- this.rememberMe = rememberMe;
+ public void setUserId(String userId) {
+ this.userId = userId;
}
- public String getAuthMethod() {
- return authMethod;
+ public String getState() {
+ return state;
}
- public void setAuthMethod(String authMethod) {
- this.authMethod = authMethod;
+ public void setState(String state) {
+ this.state = state;
}
- public int getExpiration() {
- return expiration;
+ public String getSessionState() {
+ return sessionState;
}
- public void setExpiration(int expiration) {
- this.expiration = expiration;
+ public void setSessionState(String sessionState) {
+ this.sessionState = sessionState;
}
- public AccessToken getAccessToken() {
- return accessToken;
+ public String getRedirectUri() {
+ return redirectUri;
}
- public void setAccessToken(AccessToken accessToken) {
- this.accessToken = accessToken;
+ public void setRedirectUri(String redirectUri) {
+ this.redirectUri = redirectUri;
}
public int getTimestamp() {
@@ -84,19 +74,28 @@ public class AccessCode {
this.timestamp = timestamp;
}
- public Set<String> getRequiredActions() {
- return requiredActions;
+ public Action getAction() {
+ return action;
}
- public void setRequiredActions(Set<String> requiredActions) {
- this.requiredActions = requiredActions;
+ public void setAction(Action action) {
+ this.action = action;
}
- public String getUsernameUsed() {
- return usernameUsed;
+ public Set<String> getRequestedRoles() {
+ return requestedRoles;
}
- public void setUsernameUsed(String usernameUsed) {
- this.usernameUsed = usernameUsed;
+ public void setRequestedRoles(Set<String> requestedRoles) {
+ this.requestedRoles = requestedRoles;
}
+
+ public static enum Action {
+ OAUTH_GRANT,
+ VERIFY_EMAIL,
+ UPDATE_PROFILE,
+ CONFIGURE_TOTP,
+ UPDATE_PASSWORD
+ }
+
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/AuthenticationProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/AuthenticationProviderRepresentation.java
old mode 100644
new mode 100755
index eeb71a0..f3a0ed2
--- a/core/src/main/java/org/keycloak/representations/idm/AuthenticationProviderRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/AuthenticationProviderRepresentation.java
@@ -34,4 +34,23 @@ public class AuthenticationProviderRepresentation {
public void setConfig(Map<String, String> config) {
this.config = config;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AuthenticationProviderRepresentation that = (AuthenticationProviderRepresentation) o;
+
+ if (passwordUpdateSupported != that.passwordUpdateSupported) return false;
+ if (config != null ? !config.equals(that.config) : that.config != null) return false;
+ if (!providerName.equals(that.providerName)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return providerName.hashCode();
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClaimRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClaimRepresentation.java
index 1783d89..9d3aed1 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClaimRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClaimRepresentation.java
@@ -95,4 +95,40 @@ public class ClaimRepresentation {
public void setPhone(boolean phone) {
this.phone = phone;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ClaimRepresentation that = (ClaimRepresentation) o;
+
+ if (address != that.address) return false;
+ if (email != that.email) return false;
+ if (gender != that.gender) return false;
+ if (locale != that.locale) return false;
+ if (name != that.name) return false;
+ if (phone != that.phone) return false;
+ if (picture != that.picture) return false;
+ if (profile != that.profile) return false;
+ if (username != that.username) return false;
+ if (website != that.website) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (name ? 1 : 0);
+ result = 31 * result + (username ? 1 : 0);
+ result = 31 * result + (profile ? 1 : 0);
+ result = 31 * result + (picture ? 1 : 0);
+ result = 31 * result + (website ? 1 : 0);
+ result = 31 * result + (email ? 1 : 0);
+ result = 31 * result + (gender ? 1 : 0);
+ result = 31 * result + (locale ? 1 : 0);
+ result = 31 * result + (address ? 1 : 0);
+ result = 31 * result + (phone ? 1 : 0);
+ return result;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
index b0c37ac..99237ee 100755
--- a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
@@ -11,9 +11,16 @@ public class CredentialRepresentation {
public static final String CLIENT_CERT = "cert";
protected String type;
- protected String value;
protected String device;
+ // Plain-text value of credential (used for example during import from manually created JSON file)
+ protected String value;
+
+ // Value stored in DB (used for example during export/import)
+ protected String hashedSaltedValue;
+ protected String salt;
+ protected Integer hashIterations;
+
public String getType() {
return type;
}
@@ -37,4 +44,28 @@ public class CredentialRepresentation {
public void setDevice(String device) {
this.device = device;
}
+
+ public String getHashedSaltedValue() {
+ return hashedSaltedValue;
+ }
+
+ public void setHashedSaltedValue(String hashedSaltedValue) {
+ this.hashedSaltedValue = hashedSaltedValue;
+ }
+
+ public String getSalt() {
+ return salt;
+ }
+
+ public void setSalt(String salt) {
+ this.salt = salt;
+ }
+
+ public Integer getHashIterations() {
+ return hashIterations;
+ }
+
+ public void setHashIterations(Integer hashIterations) {
+ this.hashIterations = hashIterations;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
index 8751a38..7511de6 100755
--- a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
@@ -9,7 +9,6 @@ import java.util.List;
public class OAuthClientRepresentation {
protected String id;
protected String name;
- protected String baseUrl;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected Boolean enabled;
@@ -44,14 +43,6 @@ public class OAuthClientRepresentation {
this.enabled = enabled;
}
- public String getBaseUrl() {
- return baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
public List<String> getRedirectUris() {
return redirectUris;
}
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 68ba134..5f87fee 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -27,6 +27,10 @@ public class RealmRepresentation {
protected Boolean resetPasswordAllowed;
protected Boolean social;
protected Boolean updateProfileOnInitialSocialLogin;
+
+ protected Boolean userCacheEnabled;
+ protected Boolean realmCacheEnabled;
+
//--- brute force settings
protected Boolean bruteForceProtected;
protected Integer maxFailureWaitSeconds;
@@ -44,17 +48,15 @@ public class RealmRepresentation {
protected Set<String> requiredCredentials;
protected String passwordPolicy;
protected List<UserRepresentation> users;
- protected List<UserRoleMappingRepresentation> roleMappings;
protected List<ScopeMappingRepresentation> scopeMappings;
- protected Map<String, List<UserRoleMappingRepresentation>> applicationRoleMappings;
protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
- protected List<SocialMappingRepresentation> socialMappings;
protected List<ApplicationRepresentation> applications;
protected List<OAuthClientRepresentation> oauthClients;
protected Map<String, String> socialProviders;
protected Map<String, String> smtpServer;
protected Map<String, String> ldapServer;
protected List<AuthenticationProviderRepresentation> authenticationProviders;
+ protected List<UserFederationProviderRepresentation> userFederationProviders;
protected String loginTheme;
protected String accountTheme;
protected String adminTheme;
@@ -151,18 +153,6 @@ public class RealmRepresentation {
this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
}
- public List<UserRoleMappingRepresentation> getRoleMappings() {
- return roleMappings;
- }
-
- public UserRoleMappingRepresentation roleMapping(String username) {
- UserRoleMappingRepresentation mapping = new UserRoleMappingRepresentation();
- mapping.setUsername(username);
- if (roleMappings == null) roleMappings = new ArrayList<UserRoleMappingRepresentation>();
- roleMappings.add(mapping);
- return mapping;
- }
-
public List<ScopeMappingRepresentation> getScopeMappings() {
return scopeMappings;
}
@@ -175,18 +165,6 @@ public class RealmRepresentation {
return mapping;
}
- public List<SocialMappingRepresentation> getSocialMappings() {
- return socialMappings;
- }
-
- public SocialMappingRepresentation socialMapping(String username) {
- SocialMappingRepresentation mapping = new SocialMappingRepresentation();
- mapping.setUsername(username);
- if (socialMappings == null) socialMappings = new ArrayList<SocialMappingRepresentation>();
- socialMappings.add(mapping);
- return mapping;
- }
-
public Set<String> getRequiredCredentials() {
return requiredCredentials;
}
@@ -267,6 +245,22 @@ public class RealmRepresentation {
this.rememberMe = rememberMe;
}
+ public Boolean isRealmCacheEnabled() {
+ return realmCacheEnabled;
+ }
+
+ public void setRealmCacheEnabled(Boolean realmCacheEnabled) {
+ this.realmCacheEnabled = realmCacheEnabled;
+ }
+
+ public Boolean isUserCacheEnabled() {
+ return userCacheEnabled;
+ }
+
+ public void setUserCacheEnabled(Boolean userCacheEnabled) {
+ this.userCacheEnabled = userCacheEnabled;
+ }
+
public Boolean isVerifyEmail() {
return verifyEmail;
}
@@ -339,14 +333,6 @@ public class RealmRepresentation {
this.oauthClients = oauthClients;
}
- public Map<String, List<UserRoleMappingRepresentation>> getApplicationRoleMappings() {
- return applicationRoleMappings;
- }
-
- public void setApplicationRoleMappings(Map<String, List<UserRoleMappingRepresentation>> applicationRoleMappings) {
- this.applicationRoleMappings = applicationRoleMappings;
- }
-
public Map<String, List<ScopeMappingRepresentation>> getApplicationScopeMappings() {
return applicationScopeMappings;
}
@@ -458,4 +444,36 @@ public class RealmRepresentation {
public void setFailureFactor(Integer failureFactor) {
this.failureFactor = failureFactor;
}
+
+ public boolean isAuditEnabled() {
+ return auditEnabled;
+ }
+
+ public void setAuditEnabled(boolean auditEnabled) {
+ this.auditEnabled = auditEnabled;
+ }
+
+ public long getAuditExpiration() {
+ return auditExpiration;
+ }
+
+ public void setAuditExpiration(long auditExpiration) {
+ this.auditExpiration = auditExpiration;
+ }
+
+ public List<String> getAuditListeners() {
+ return auditListeners;
+ }
+
+ public void setAuditListeners(List<String> auditListeners) {
+ this.auditListeners = auditListeners;
+ }
+
+ public List<UserFederationProviderRepresentation> getUserFederationProviders() {
+ return userFederationProviders;
+ }
+
+ public void setUserFederationProviders(List<UserFederationProviderRepresentation> userFederationProviders) {
+ this.userFederationProviders = userFederationProviders;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java
new file mode 100755
index 0000000..90efd17
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java
@@ -0,0 +1,55 @@
+package org.keycloak.representations.idm;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserFederationProviderRepresentation {
+
+ private String id;
+ private String providerName;
+ private Map<String, String> config;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+
+ public Map<String, String> getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ UserFederationProviderRepresentation that = (UserFederationProviderRepresentation) o;
+
+ if (!id.equals(that.id)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index 43aa368..2578983 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -21,9 +21,13 @@ public class UserRepresentation {
protected String lastName;
protected String email;
protected AuthenticationLinkRepresentation authenticationLink;
+ protected String federationLink;
protected Map<String, String> attributes;
protected List<CredentialRepresentation> credentials;
protected List<String> requiredActions;
+ protected List<SocialLinkRepresentation> socialLinks;
+ protected List<String> realmRoles;
+ protected Map<String, List<String>> applicationRoles;
public String getSelf() {
return self;
@@ -143,4 +147,36 @@ public class UserRepresentation {
public void setRequiredActions(List<String> requiredActions) {
this.requiredActions = requiredActions;
}
+
+ public List<SocialLinkRepresentation> getSocialLinks() {
+ return socialLinks;
+ }
+
+ public void setSocialLinks(List<SocialLinkRepresentation> socialLinks) {
+ this.socialLinks = socialLinks;
+ }
+
+ public List<String> getRealmRoles() {
+ return realmRoles;
+ }
+
+ public void setRealmRoles(List<String> realmRoles) {
+ this.realmRoles = realmRoles;
+ }
+
+ public Map<String, List<String>> getApplicationRoles() {
+ return applicationRoles;
+ }
+
+ public void setApplicationRoles(Map<String, List<String>> applicationRoles) {
+ this.applicationRoles = applicationRoles;
+ }
+
+ public String getFederationLink() {
+ return federationLink;
+ }
+
+ public void setFederationLink(String federationLink) {
+ this.federationLink = federationLink;
+ }
}
dependencies/pom.xml 32(+32 -0)
diff --git a/dependencies/pom.xml b/dependencies/pom.xml
new file mode 100755
index 0000000..896eaa4
--- /dev/null
+++ b/dependencies/pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>keycloak-dependencies-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak Dependencies Parent</name>
+ <description/>
+
+ <modules>
+ <module>server-min</module>
+ <module>server-all</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
dependencies/server-all/pom.xml 159(+159 -0)
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
new file mode 100755
index 0000000..6316ed9
--- /dev/null
+++ b/dependencies/server-all/pom.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-dependencies-server-all</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak Dependencies Server All</name>
+ <description />
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-dependencies-server-min</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-sessions-mem</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-sessions-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-sessions-mongo</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-jboss-logging</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-email</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- social -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-github</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-google</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-twitter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.twitter4j</groupId>
+ <artifactId>twitter4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-facebook</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- authentication api -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-authentication-picketlink</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-simple-schema</artifactId>
+ </dependency>
+
+ <!-- picketlink -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-picketlink-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-picketlink-realm</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- mongo -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-mongo</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-mongo</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-mongo</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mongodb</groupId>
+ <artifactId>mongo-java-driver</artifactId>
+ </dependency>
+
+ <!-- export/import -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-zip</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>de.idyl</groupId>
+ <artifactId>winzipaes</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
dependencies/server-min/pom.xml 157(+157 -0)
diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml
new file mode 100755
index 0000000..5ae92c6
--- /dev/null
+++ b/dependencies/server-min/pom.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-dependencies-server-min</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak Dependencies Server Min</name>
+ <description />
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.iharder</groupId>
+ <artifactId>base64</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core-jaxrs</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-services</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.zxing</groupId>
+ <artifactId>javase</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-invalidation-cache-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-js-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- social -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- forms -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-forms-common-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-forms-common-themes</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-account-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-account-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-email-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-email-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-login-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-login-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- authentication api -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-authentication-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-authentication-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- timer -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-timer-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-timer-basic</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- export/import -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-dir</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-single-file</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
distribution/war-zip/pom.xml 2(+1 -1)
diff --git a/distribution/war-zip/pom.xml b/distribution/war-zip/pom.xml
index d0ddb28..86f55c6 100755
--- a/distribution/war-zip/pom.xml
+++ b/distribution/war-zip/pom.xml
@@ -62,7 +62,7 @@
</execution>
</executions>
<configuration>
- <file>${project.build.directory}/unpacked/deployments/auth-server.war/WEB-INF/classes/META-INF/persistence.xml</file>
+ <file>${project.build.directory}/unpacked/deployments/auth-server.war/WEB-INF/classes/META-INF/keycloak-server.json</file>
<replacements>
<replacement>
<token>java:jboss/datasources/ExampleDS</token>
diff --git a/docbook/reference/en/en-US/master.xml b/docbook/reference/en/en-US/master.xml
index 0fe428f..b8785df 100755
--- a/docbook/reference/en/en-US/master.xml
+++ b/docbook/reference/en/en-US/master.xml
@@ -30,6 +30,7 @@
<!ENTITY Authentication SYSTEM "modules/authentication-spi.xml">
<!ENTITY Ldap SYSTEM "modules/ldap.xml">
<!ENTITY ExportImport SYSTEM "modules/export-import.xml">
+ <!ENTITY ServerCache SYSTEM "modules/cache.xml">
]>
<book>
@@ -117,6 +118,7 @@ This one is short
&Authentication;
&Ldap;
&ExportImport;
+ &ServerCache;
&Migration;
</book>
docbook/reference/en/en-US/modules/cache.xml 68(+68 -0)
diff --git a/docbook/reference/en/en-US/modules/cache.xml b/docbook/reference/en/en-US/modules/cache.xml
new file mode 100755
index 0000000..6632e3a
--- /dev/null
+++ b/docbook/reference/en/en-US/modules/cache.xml
@@ -0,0 +1,68 @@
+<chapter id="server_cache">
+ <title>Server Cache</title>
+ <para>
+ By default, Keycloak caches realm metadata and users. There are two separate caches, one for realm metadata
+ (realm, application, client, roles, etc...) and one for users. These caches greatly improves the performance of the server.
+ </para>
+
+ <section>
+ <title>Disabling Caches</title>
+ <para>
+ The realm and user caches can be disabled through configuration or through the management console. To
+ manally disable the realm or user cache, you must edit the <literal>keycloak-server.json</literal> file
+ in your distribution. Here's what the config looks like initially.
+ </para>
+ <para>
+ <programlisting><![CDATA[
+ "realmCache": {
+ "provider": "${keycloak.realm.cache.provider:mem}"
+ },
+
+ "userCache": {
+ "provider": "${keycloak.user.cache.provider:mem}",
+ "mem": {
+ "maxSize": 20000
+ }
+ },
+]]></programlisting>
+ </para>
+ <para>You must then change it to:
+ <programlisting><![CDATA[
+ "realmCache": {
+ "provider": "${keycloak.realm.cache.provider:none}"
+ },
+
+ "userCache": {
+ "provider": "${keycloak.user.cache.provider:none}"
+ },
+]]></programlisting>
+ </para>
+ <para>
+ You can also disable either of the caches at runtime through the Keycloak admin console Realm Settings page.
+ This will not permanently disable the cache. If you reboot the server, the cache will be re-enabled unless
+ you manualy disable the cache in the <literal>keycloak-server.json</literal> file.
+ </para>
+ </section>
+ <section>
+ <title>Clear Caches</title>
+ <para>
+ To clear the realm or user cache, go to the Keycloak admin console Realm Settings page. Disable the cache
+ you want. Save the settings. Then re-enable the cache. This will cause the cache to be cleared.
+ </para>
+ </section>
+ <section>
+ <title>Cache Config</title>
+ <para>
+ Cache configuration is done within <literal>keycloak-server.json</literal>. Changes to this file will not
+ be seen by the server until you reboot. Currently you can only configure the max size of the user cache.
+ <programlisting><![CDATA[
+ "userCache": {
+ "provider": "${keycloak.user.cache.provider:mem}",
+ "mem": {
+ "maxSize": 20000
+ }
+ },
+]]></programlisting>
+ </para>
+ </section>
+</chapter>
\ No newline at end of file
diff --git a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
index cc713f5..c881b61 100755
--- a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
+++ b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
@@ -1,6 +1,14 @@
<chapter id="Migration_from_older_versions">
<title>Migration from older versions</title>
<sect1>
+ <title>Migrating from 1.0 Beta 1 to Beta 4</title>
+ <itemizedlist>
+ <listitem>
+ DB Schema has changed again.
+ </listitem>
+ </itemizedlist>
+ </sect1>
+ <sect1>
<title>Migrating from 1.0 Alpha 4 to Beta 1</title>
<itemizedlist>
<listitem>
examples/cordova/example-realm.json 8(+6 -2)
diff --git a/examples/cordova/example-realm.json b/examples/cordova/example-realm.json
index 37e899e..05ad905 100755
--- a/examples/cordova/example-realm.json
+++ b/examples/cordova/example-realm.json
@@ -15,8 +15,12 @@
"lastName": "User",
"credentials" : [
{ "type" : "password",
- "value" : "password" }
- ]
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "account": ["view-profile", "manage-account"]
+ }
}
],
"roles" : {
examples/cors/cors-realm.json 22(+6 -16)
diff --git a/examples/cors/cors-realm.json b/examples/cors/cors-realm.json
index 9b3da42..1dd5503 100755
--- a/examples/cors/cors-realm.json
+++ b/examples/cors/cors-realm.json
@@ -20,8 +20,12 @@
"lastName": "Burke",
"credentials" : [
{ "type" : "password",
- "value" : "password" }
- ]
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "realm-management": [ "realm-admin" ]
+ }
}
],
"roles" : {
@@ -32,12 +36,6 @@
}
]
},
- "roleMappings": [
- {
- "username": "bburke@redhat.com",
- "roles": ["user"]
- }
- ],
"scopeMappings": [
{
"client": "angular-product",
@@ -58,14 +56,6 @@
]
}
],
- "applicationRoleMappings": {
- "realm-management": [
- {
- "username": "bburke@redhat.com",
- "roles": ["realm-admin"]
- }
- ]
- },
"applicationScopeMappings": {
"realm-management": [
{
examples/demo-template/testrealm.json 36(+10 -26)
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index f4ff956..f321333 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -24,7 +24,11 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "account": [ "manage-account" ]
+ }
},
{
"username" : "admin",
@@ -35,7 +39,11 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": [ "user","admin" ],
+ "applicationRoles": {
+ "realm-management": [ "realm-admin" ]
+ }
}
],
"roles" : {
@@ -50,16 +58,6 @@
}
]
},
- "roleMappings": [
- {
- "username": "bburke@redhat.com",
- "roles": ["user"]
- },
- {
- "username": "admin",
- "roles": ["user","admin"]
- }
- ],
"scopeMappings": [
{
"client": "third-party",
@@ -154,20 +152,6 @@
}
],
- "applicationRoleMappings": {
- "account": [
- {
- "username": "bburke@redhat.com",
- "roles": ["manage-account"]
- }
- ],
- "realm-management": [
- {
- "username": "admin",
- "roles": ["realm-admin"]
- }
- ]
- },
"applicationScopeMappings": {
"realm-management": [
{
examples/js-console/example-realm.json 22(+6 -16)
diff --git a/examples/js-console/example-realm.json b/examples/js-console/example-realm.json
index 42d291e..ab43028 100755
--- a/examples/js-console/example-realm.json
+++ b/examples/js-console/example-realm.json
@@ -15,8 +15,12 @@
"lastName": "User",
"credentials" : [
{ "type" : "password",
- "value" : "password" }
- ]
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "account": ["view-profile", "manage-account"]
+ }
}
],
"roles" : {
@@ -31,12 +35,6 @@
}
]
},
- "roleMappings": [
- {
- "username": "user",
- "roles": ["user"]
- }
- ],
"scopeMappings": [
{
"client": "js-console",
@@ -57,14 +55,6 @@
]
}
],
- "applicationRoleMappings": {
- "account": [
- {
- "username": "user",
- "roles": ["view-profile", "manage-account"]
- }
- ]
- },
"applicationScopeMappings": {
"account": [
{
diff --git a/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java
index 9b83ec0..8c5ff1a 100644
--- a/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java
+++ b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java
@@ -48,7 +48,7 @@ public class PropertiesAuthenticationProvider implements AuthenticationProvider
@Override
public String registerUser(RealmModel realm, Map<String, String> configuration, UserModel user) throws AuthenticationProviderException {
// Registration ignored
- return user.getLoginName();
+ return user.getUsername();
}
@Override
export-import/export-import-api/pom.xml 26(+26 -0)
diff --git a/export-import/export-import-api/pom.xml b/export-import/export-import-api/pom.xml
index 19db6e5..1b4dc0a 100755
--- a/export-import/export-import-api/pom.xml
+++ b/export-import/export-import-api/pom.xml
@@ -26,6 +26,32 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-invalidation-cache-model</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.iharder</groupId>
+ <artifactId>base64</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java
new file mode 100644
index 0000000..3ce6336
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java
@@ -0,0 +1,121 @@
+package org.keycloak.exportimport;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ExportImportConfig {
+
+ public static final String PREFIX = "keycloak.migration.";
+ public static final String ACTION = PREFIX + "action";
+ public static final String ACTION_EXPORT = "export";
+ public static final String ACTION_IMPORT = "import";
+
+ public static final String PROVIDER = PREFIX + "provider";
+ public static final String PROVIDER_DEFAULT = "zip";
+
+ // Name of the realm to export. If null, then full export will be triggered
+ public static final String REALM_NAME = PREFIX + "realmName";
+
+ // used for "dir" provider
+ public static final String DIR = PREFIX + "dir";
+ // used for "zip" provider
+ public static final String ZIP_FILE = PREFIX + "zipFile";
+ public static final String ZIP_PASSWORD = PREFIX + "zipPassword";
+ // used for "singleFile" provider
+ public static final String FILE = PREFIX + "file";
+
+ // How to export users when realm export is requested for "dir" and "zip" provider
+ public static final String USERS_EXPORT_STRATEGY = PREFIX + "usersExportStrategy";
+ public static final UsersExportStrategy DEFAULT_USERS_EXPORT_STRATEGY = UsersExportStrategy.DIFFERENT_FILES;
+
+ // Number of users per file used in "dir" and "zip" providers. Used if usersExportStrategy is DIFFERENT_FILES
+ public static final String USERS_PER_FILE = PREFIX + "usersPerFile";
+ public static final Integer DEFAULT_USERS_PER_FILE = 5000;
+
+ // Strategy used during import data
+ public static final String STRATEGY = PREFIX + "strategy";
+ public static final Strategy DEFAULT_STRATEGY = Strategy.OVERWRITE_EXISTING;
+
+ public static String getAction() {
+ return System.getProperty(ACTION);
+ }
+
+ public static void setAction(String exportImportAction) {
+ System.setProperty(ACTION, exportImportAction);
+ }
+
+ public static String getProvider() {
+ return System.getProperty(PROVIDER, PROVIDER_DEFAULT);
+ }
+
+ public static void setProvider(String exportImportProvider) {
+ System.setProperty(PROVIDER, exportImportProvider);
+ }
+
+ public static String getRealmName() {
+ return System.getProperty(REALM_NAME);
+ }
+
+ public static void setRealmName(String realmName) {
+ if (realmName != null) {
+ System.setProperty(REALM_NAME, realmName);
+ } else {
+ System.getProperties().remove(REALM_NAME);
+ }
+ }
+
+ public static String getDir() {
+ return System.getProperty(DIR);
+ }
+
+ public static String setDir(String dir) {
+ return System.setProperty(DIR, dir);
+ }
+
+ public static String getZipFile() {
+ return System.getProperty(ZIP_FILE);
+ }
+
+ public static void setZipFile(String exportImportZipFile) {
+ System.setProperty(ZIP_FILE, exportImportZipFile);
+ }
+
+ public static String getZipPassword() {
+ return System.getProperty(ZIP_PASSWORD);
+ }
+
+ public static void setZipPassword(String exportImportZipPassword) {
+ System.setProperty(ZIP_PASSWORD, exportImportZipPassword);
+ }
+
+ public static String getFile() {
+ return System.getProperty(FILE);
+ }
+
+ public static void setFile(String file) {
+ System.setProperty(FILE, file);
+ }
+
+ public static UsersExportStrategy getUsersExportStrategy() {
+ String usersExportStrategy = System.getProperty(USERS_EXPORT_STRATEGY, DEFAULT_USERS_EXPORT_STRATEGY.toString());
+ return Enum.valueOf(UsersExportStrategy.class, usersExportStrategy);
+ }
+
+ public static void setUsersExportStrategy(UsersExportStrategy usersExportStrategy) {
+ System.setProperty(USERS_EXPORT_STRATEGY, usersExportStrategy.toString());
+ }
+
+ public static Integer getUsersPerFile() {
+ String usersPerFile = System.getProperty(USERS_PER_FILE, String.valueOf(DEFAULT_USERS_PER_FILE));
+ return Integer.parseInt(usersPerFile.trim());
+ }
+
+ public static void setUsersPerFile(Integer usersPerFile) {
+ System.setProperty(USERS_PER_FILE, String.valueOf(usersPerFile));
+ }
+
+ public static Strategy getStrategy() {
+ String strategy = System.getProperty(STRATEGY, DEFAULT_STRATEGY.toString());
+ return Enum.valueOf(Strategy.class, strategy);
+ }
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java
new file mode 100644
index 0000000..caedb36
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java
@@ -0,0 +1,63 @@
+package org.keycloak.exportimport;
+
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ExportImportManager {
+
+ private static final Logger logger = Logger.getLogger(ExportImportManager.class);
+
+ public void checkExportImport(KeycloakSessionFactory sessionFactory) {
+ String exportImportAction = ExportImportConfig.getAction();
+ String realmName = ExportImportConfig.getRealmName();
+
+ boolean export = false;
+ boolean importt = false;
+ if (ExportImportConfig.ACTION_EXPORT.equals(exportImportAction)) {
+ export = true;
+ } else if (ExportImportConfig.ACTION_IMPORT.equals(exportImportAction)) {
+ importt = true;
+ }
+
+ if (export || importt) {
+ String exportImportProviderId = ExportImportConfig.getProvider();
+ logger.debug("Will use provider: " + exportImportProviderId);
+ KeycloakSession session = sessionFactory.create();
+
+ try {
+ if (export) {
+ ExportProvider exportProvider = session.getProvider(ExportProvider.class, exportImportProviderId);
+
+ if (realmName == null) {
+ logger.info("Full model export requested");
+ exportProvider.exportModel(sessionFactory);
+ } else {
+ logger.infof("Export of realm '%s' requested", realmName);
+ exportProvider.exportRealm(sessionFactory, realmName);
+ }
+ logger.info("Export finished successfully");
+ } else {
+ ImportProvider importProvider = session.getProvider(ImportProvider.class, exportImportProviderId);
+ Strategy strategy = ExportImportConfig.getStrategy();
+ if (realmName == null) {
+ logger.infof("Full model import requested. Strategy: %s", strategy.toString());
+ importProvider.importModel(sessionFactory, strategy);
+ } else {
+ logger.infof("Import of realm '%s' requested. Strategy: %s", realmName, strategy.toString());
+ importProvider.importRealm(sessionFactory, realmName, strategy);
+ }
+ logger.info("Import finished successfully");
+ }
+ } catch (Throwable ioe) {
+ logger.error("Error during export/import", ioe);
+ } finally {
+ session.close();
+ }
+ }
+ }
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java
new file mode 100644
index 0000000..1a0d6a0
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java
@@ -0,0 +1,18 @@
+package org.keycloak.exportimport;
+
+import java.io.IOException;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface ExportProvider extends Provider {
+
+ void exportModel(KeycloakSessionFactory factory) throws IOException;
+
+ void exportRealm(KeycloakSessionFactory factory, String realmName) throws IOException;
+
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java
new file mode 100644
index 0000000..6ec2960
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java
@@ -0,0 +1,26 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ExportSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "export";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return ExportProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return ExportProviderFactory.class;
+ }
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java
new file mode 100644
index 0000000..9134516
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java
@@ -0,0 +1,17 @@
+package org.keycloak.exportimport;
+
+import java.io.IOException;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface ImportProvider extends Provider {
+
+ void importModel(KeycloakSessionFactory factory, Strategy strategy) throws IOException;
+
+ void importRealm(KeycloakSessionFactory factory, String realmName, Strategy strategy) throws IOException;
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java
new file mode 100644
index 0000000..b562f3f
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java
@@ -0,0 +1,26 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ImportSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "import";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return ImportProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return ImportProviderFactory.class;
+ }
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/Strategy.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/Strategy.java
new file mode 100644
index 0000000..6bbd70d
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/Strategy.java
@@ -0,0 +1,10 @@
+package org.keycloak.exportimport;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public enum Strategy {
+
+ IGNORE_EXISTING, // Ignore existing user entries
+ OVERWRITE_EXISTING // Overwrite existing user entries
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java
new file mode 100644
index 0000000..c1df989
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java
@@ -0,0 +1,11 @@
+package org.keycloak.exportimport;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public enum UsersExportStrategy {
+ SKIP, // Exporting of users will be skipped completely
+ REALM_FILE, // All users will be exported to same file with realm (So file like "foo-realm.json" with both realm data and users)
+ SAME_FILE, // All users will be exported to same file but different than realm (So file like "foo-realm.json" with realm data and "foo-users.json" with users)
+ DIFFERENT_FILES // Users will be exported into more different files according to maximum number of users per file
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java
new file mode 100644
index 0000000..748fe1f
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java
@@ -0,0 +1,15 @@
+package org.keycloak.exportimport.util;
+
+import java.io.IOException;
+
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * Task to be executed inside transaction
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface ExportImportJob {
+
+ public void run(KeycloakSession session) throws IOException;
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java
new file mode 100644
index 0000000..b73a4c0
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java
@@ -0,0 +1,47 @@
+package org.keycloak.exportimport.util;
+
+import java.io.IOException;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ExportImportUtils {
+
+ /**
+ * Wrap given runnable job into KeycloakTransaction.
+ *
+ * @param factory
+ * @param job
+ */
+ public static void runJobInTransaction(KeycloakSessionFactory factory, ExportImportJob job) throws IOException {
+ KeycloakSession session = factory.create();
+ KeycloakTransaction tx = session.getTransaction();
+ try {
+ tx.begin();
+ job.run(session);
+
+ if (tx.isActive()) {
+ if (tx.getRollbackOnly()) {
+ tx.rollback();
+ } else {
+ tx.commit();
+ }
+ }
+ } finally {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ session.close();
+ }
+ }
+
+ public static String getMasterRealmAdminApplicationName(RealmModel realm) {
+ return realm.getName() + "-realm";
+ }
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
new file mode 100755
index 0000000..4c1cc5b
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -0,0 +1,367 @@
+package org.keycloak.exportimport.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.iharder.Base64;
+import org.codehaus.jackson.JsonEncoding;
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.keycloak.exportimport.Strategy;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.ApplicationRepresentation;
+import org.keycloak.representations.idm.AuthenticationLinkRepresentation;
+import org.keycloak.representations.idm.ClaimRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.OAuthClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.RolesRepresentation;
+import org.keycloak.representations.idm.ScopeMappingRepresentation;
+import org.keycloak.representations.idm.SocialLinkRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ExportUtils {
+
+ public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, boolean includeUsers) {
+ RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm);
+
+ // Audit
+ rep.setAuditEnabled(realm.isAuditEnabled());
+ if (realm.getAuditExpiration() != 0) {
+ rep.setAuditExpiration(realm.getAuditExpiration());
+ }
+
+ if (realm.getAuditListeners() != null) {
+ rep.setAuditListeners(new LinkedList<String>(realm.getAuditListeners()));
+ }
+
+ // Applications
+ List<ApplicationModel> applications = realm.getApplications();
+ List<ApplicationRepresentation> appReps = new ArrayList<ApplicationRepresentation>();
+ for (ApplicationModel app : applications) {
+ ApplicationRepresentation appRep = exportApplication(app);
+ appReps.add(appRep);
+ }
+ rep.setApplications(appReps);
+
+ // OAuth clients
+ List<OAuthClientModel> oauthClients = realm.getOAuthClients();
+ List<OAuthClientRepresentation> oauthClientReps = new ArrayList<OAuthClientRepresentation>();
+ for (OAuthClientModel oauthClient : oauthClients) {
+ OAuthClientRepresentation clientRep = ModelToRepresentation.toRepresentation(oauthClient);
+ oauthClientReps.add(clientRep);
+ }
+ rep.setOauthClients(oauthClientReps);
+
+ // Roles
+ List<RoleRepresentation> realmRoleReps = null;
+ Map<String, List<RoleRepresentation>> appRolesReps = new HashMap<String, List<RoleRepresentation>>();
+
+ Set<RoleModel> realmRoles = realm.getRoles();
+ if (realmRoles != null && realmRoles.size() > 0) {
+ realmRoleReps = exportRoles(realmRoles);
+ }
+ for (ApplicationModel app : applications) {
+ Set<RoleModel> currentAppRoles = app.getRoles();
+ List<RoleRepresentation> currentAppRoleReps = exportRoles(currentAppRoles);
+ appRolesReps.put(app.getName(), currentAppRoleReps);
+ }
+
+ RolesRepresentation rolesRep = new RolesRepresentation();
+ if (realmRoleReps != null) {
+ rolesRep.setRealm(realmRoleReps);
+ }
+ if (appRolesReps.size() > 0) {
+ rolesRep.setApplication(appRolesReps);
+ }
+ rep.setRoles(rolesRep);
+
+ // Scopes
+ List<ClientModel> allClients = new ArrayList<ClientModel>(applications);
+ allClients.addAll(realm.getOAuthClients());
+ Map<String, List<ScopeMappingRepresentation>> appScopeReps = new HashMap<String, List<ScopeMappingRepresentation>>();
+
+ for (ClientModel client : allClients) {
+ Set<RoleModel> clientScopes = client.getScopeMappings();
+ ScopeMappingRepresentation scopeMappingRep = null;
+ for (RoleModel scope : clientScopes) {
+ if (scope.getContainer() instanceof RealmModel) {
+ if (scopeMappingRep == null) {
+ scopeMappingRep = rep.scopeMapping(client.getClientId());
+ }
+ scopeMappingRep.role(scope.getName());
+ } else {
+ ApplicationModel app = (ApplicationModel)scope.getContainer();
+ String appName = app.getName();
+ List<ScopeMappingRepresentation> currentAppScopes = appScopeReps.get(appName);
+ if (currentAppScopes == null) {
+ currentAppScopes = new ArrayList<ScopeMappingRepresentation>();
+ appScopeReps.put(appName, currentAppScopes);
+ }
+
+ ScopeMappingRepresentation currentClientScope = null;
+ for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
+ if (scopeMapping.getClient().equals(client.getClientId())) {
+ currentClientScope = scopeMapping;
+ break;
+ }
+ }
+ if (currentClientScope == null) {
+ currentClientScope = new ScopeMappingRepresentation();
+ currentClientScope.setClient(client.getClientId());
+ currentAppScopes.add(currentClientScope);
+ }
+ currentClientScope.role(scope.getName());
+ }
+ }
+ }
+
+ if (appScopeReps.size() > 0) {
+ rep.setApplicationScopeMappings(appScopeReps);
+ }
+
+ // Finally users if needed
+ if (includeUsers) {
+ List<UserModel> allUsers = session.users().getUsers(realm);
+ List<UserRepresentation> users = new ArrayList<UserRepresentation>();
+ for (UserModel user : allUsers) {
+ UserRepresentation userRep = exportUser(session, realm, user);
+ users.add(userRep);
+ }
+
+ if (users.size() > 0) {
+ rep.setUsers(users);
+ }
+ }
+
+ return rep;
+ }
+
+ /**
+ * Full export of application including claims and secret
+ * @param app
+ * @return full ApplicationRepresentation
+ */
+ public static ApplicationRepresentation exportApplication(ApplicationModel app) {
+ ApplicationRepresentation appRep = ModelToRepresentation.toRepresentation(app);
+
+ appRep.setSecret(app.getSecret());
+ ClaimRepresentation claimRep = ModelToRepresentation.toRepresentation((ClientModel)app);
+ appRep.setClaims(claimRep);
+ return appRep;
+ }
+
+ public static List<RoleRepresentation> exportRoles(Collection<RoleModel> roles) {
+ List<RoleRepresentation> roleReps = new ArrayList<RoleRepresentation>();
+
+ for (RoleModel role : roles) {
+ RoleRepresentation roleRep = exportRole(role);
+ roleReps.add(roleRep);
+ }
+ return roleReps;
+ }
+
+ public static List<String> getRoleNames(Collection<RoleModel> roles) {
+ List<String> roleNames = new ArrayList<String>();
+ for (RoleModel role : roles) {
+ roleNames.add(role.getName());
+ }
+ return roleNames;
+ }
+
+ /**
+ * Full export of role including composite roles
+ * @param role
+ * @return RoleRepresentation with all stuff filled (including composite roles)
+ */
+ public static RoleRepresentation exportRole(RoleModel role) {
+ RoleRepresentation roleRep = ModelToRepresentation.toRepresentation(role);
+
+ Set<RoleModel> composites = role.getComposites();
+ if (composites != null && composites.size() > 0) {
+ Set<String> compositeRealmRoles = null;
+ Map<String, List<String>> compositeAppRoles = null;
+
+ for (RoleModel composite : composites) {
+ RoleContainerModel crContainer = composite.getContainer();
+ if (crContainer instanceof RealmModel) {
+
+ if (compositeRealmRoles == null) {
+ compositeRealmRoles = new HashSet<String>();
+ }
+ compositeRealmRoles.add(composite.getName());
+ } else {
+ if (compositeAppRoles == null) {
+ compositeAppRoles = new HashMap<String, List<String>>();
+ }
+
+ ApplicationModel app = (ApplicationModel)crContainer;
+ String appName = app.getName();
+ List<String> currentAppComposites = compositeAppRoles.get(appName);
+ if (currentAppComposites == null) {
+ currentAppComposites = new ArrayList<String>();
+ compositeAppRoles.put(appName, currentAppComposites);
+ }
+ currentAppComposites.add(composite.getName());
+ }
+ }
+
+ RoleRepresentation.Composites compRep = new RoleRepresentation.Composites();
+ if (compositeRealmRoles != null) {
+ compRep.setRealm(compositeRealmRoles);
+ }
+ if (compositeAppRoles != null) {
+ compRep.setApplication(compositeAppRoles);
+ }
+
+ roleRep.setComposites(compRep);
+ }
+
+ return roleRep;
+ }
+
+ /**
+ * Full export of user (including role mappings and credentials)
+ *
+ * @param user
+ * @return fully exported user representation
+ */
+ public static UserRepresentation exportUser(KeycloakSession session, RealmModel realm, UserModel user) {
+ UserRepresentation userRep = ModelToRepresentation.toRepresentation(user);
+
+ // AuthenticationLink
+ AuthenticationLinkModel authLink = user.getAuthenticationLink();
+ if (authLink != null) {
+ AuthenticationLinkRepresentation authLinkRepresentation = exportAuthLink(authLink);
+ userRep.setAuthenticationLink(authLinkRepresentation);
+ }
+
+ // Social links
+ Set<SocialLinkModel> socialLinks = session.users().getSocialLinks(user, realm);
+ List<SocialLinkRepresentation> socialLinkReps = new ArrayList<SocialLinkRepresentation>();
+ for (SocialLinkModel socialLink : socialLinks) {
+ SocialLinkRepresentation socialLinkRep = exportSocialLink(socialLink);
+ socialLinkReps.add(socialLinkRep);
+ }
+ if (socialLinkReps.size() > 0) {
+ userRep.setSocialLinks(socialLinkReps);
+ }
+
+ // Role mappings
+ Set<RoleModel> roles = user.getRoleMappings();
+ List<String> realmRoleNames = new ArrayList<String>();
+ Map<String, List<String>> appRoleNames = new HashMap<String, List<String>>();
+ for (RoleModel role : roles) {
+ if (role.getContainer() instanceof RealmModel) {
+ realmRoleNames.add(role.getName());
+ } else {
+ ApplicationModel app = (ApplicationModel)role.getContainer();
+ String appName = app.getName();
+ List<String> currentAppRoles = appRoleNames.get(appName);
+ if (currentAppRoles == null) {
+ currentAppRoles = new ArrayList<String>();
+ appRoleNames.put(appName, currentAppRoles);
+ }
+
+ currentAppRoles.add(role.getName());
+ }
+ }
+
+ if (realmRoleNames.size() > 0) {
+ userRep.setRealmRoles(realmRoleNames);
+ }
+ if (appRoleNames.size() > 0) {
+ userRep.setApplicationRoles(appRoleNames);
+ }
+
+ // Credentials
+ List<UserCredentialValueModel> creds = user.getCredentialsDirectly();
+ List<CredentialRepresentation> credReps = new ArrayList<CredentialRepresentation>();
+ for (UserCredentialValueModel cred : creds) {
+ CredentialRepresentation credRep = exportCredential(cred);
+ credReps.add(credRep);
+ }
+ userRep.setCredentials(credReps);
+ userRep.setFederationLink(user.getFederationLink());
+
+ return userRep;
+ }
+
+ public static AuthenticationLinkRepresentation exportAuthLink(AuthenticationLinkModel authLinkModel) {
+ AuthenticationLinkRepresentation authLinkRep = new AuthenticationLinkRepresentation();
+ authLinkRep.setAuthProvider(authLinkModel.getAuthProvider());
+ authLinkRep.setAuthUserId(authLinkModel.getAuthUserId());
+ return authLinkRep;
+ }
+
+ public static SocialLinkRepresentation exportSocialLink(SocialLinkModel socialLink) {
+ SocialLinkRepresentation socialLinkRep = new SocialLinkRepresentation();
+ socialLinkRep.setSocialProvider(socialLink.getSocialProvider());
+ socialLinkRep.setSocialUserId(socialLink.getSocialUserId());
+ socialLinkRep.setSocialUsername(socialLink.getSocialUsername());
+ return socialLinkRep;
+ }
+
+ public static CredentialRepresentation exportCredential(UserCredentialValueModel userCred) {
+ CredentialRepresentation credRep = new CredentialRepresentation();
+ credRep.setType(userCred.getType());
+ credRep.setDevice(userCred.getDevice());
+ credRep.setHashedSaltedValue(userCred.getValue());
+ credRep.setSalt(Base64.encodeBytes(userCred.getSalt()));
+ credRep.setHashIterations(userCred.getHashIterations());
+ return credRep;
+ }
+
+ // Streaming API
+
+ public static void exportUsersToStream(KeycloakSession session, RealmModel realm, List<UserModel> usersToExport, ObjectMapper mapper, OutputStream os) throws IOException {
+ JsonFactory factory = mapper.getJsonFactory();
+ JsonGenerator generator = factory.createJsonGenerator(os, JsonEncoding.UTF8);
+ try {
+ if (mapper.isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) {
+ generator.useDefaultPrettyPrinter();
+ }
+ generator.writeStartObject();
+ generator.writeStringField("realm", realm.getName());
+ // generator.writeStringField("strategy", strategy.toString());
+ generator.writeFieldName("users");
+ generator.writeStartArray();
+
+ for (UserModel user : usersToExport) {
+ UserRepresentation userRep = ExportUtils.exportUser(session, realm, user);
+ generator.writeObject(userRep);
+ }
+
+ generator.writeEndArray();
+ generator.writeEndObject();
+ } finally {
+ generator.close();
+ }
+ }
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
new file mode 100755
index 0000000..1425b36
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
@@ -0,0 +1,222 @@
+package org.keycloak.exportimport.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.exportimport.Strategy;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ImportUtils {
+
+ private static final Logger logger = Logger.getLogger(ImportUtils.class);
+
+ /**
+ * Fully import realm from representation, save it to model and return model of newly created realm
+ *
+ * @param session
+ * @param rep
+ * @param strategy specifies whether to overwrite or ignore existing realm or user entries
+ * @return newly imported realm (or existing realm if ignoreExisting is true and realm of this name already exists)
+ */
+ public static RealmModel importRealm(KeycloakSession session, RealmRepresentation rep, Strategy strategy) {
+ String realmName = rep.getRealm();
+ RealmProvider model = session.realms();
+ RealmModel realm = model.getRealmByName(realmName);
+
+ if (realm != null) {
+ if (strategy == Strategy.IGNORE_EXISTING) {
+ logger.infof("Realm '%s' already exists. Import skipped", realmName);
+ return realm;
+ } else {
+ logger.infof("Realm '%s' already exists. Removing it before import", realmName);
+ if (Config.getAdminRealm().equals(realm.getId())) {
+ // Delete all masterAdmin apps due to foreign key constraints
+ for (RealmModel currRealm : model.getRealms()) {
+ currRealm.setMasterAdminApp(null);
+ }
+ }
+ // TODO: For migration between versions, it should be possible to delete just realm but keep it's users
+ model.removeRealm(realm.getId());
+ }
+ }
+
+ realm = rep.getId() != null ? model.createRealm(rep.getId(), realmName) : model.createRealm(realmName);
+
+ RepresentationToModel.importRealm(session, rep, realm);
+
+ refreshMasterAdminApps(model, realm);
+
+ logger.infof("Realm '%s' imported", realmName);
+ return realm;
+ }
+
+ private static void refreshMasterAdminApps(RealmProvider model, RealmModel realm) {
+ String adminRealmId = Config.getAdminRealm();
+ if (adminRealmId.equals(realm.getId())) {
+ // We just imported master realm. All 'masterAdminApps' need to be refreshed
+ RealmModel adminRealm = realm;
+ for (RealmModel currentRealm : model.getRealms()) {
+ ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(currentRealm));
+ if (masterApp != null) {
+ currentRealm.setMasterAdminApp(masterApp);
+ } else {
+ setupMasterAdminManagement(model, currentRealm);
+ }
+ }
+ } else {
+ // Need to refresh masterApp for current realm
+ RealmModel adminRealm = model.getRealm(adminRealmId);
+ ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(realm));
+ if (masterApp != null) {
+ realm.setMasterAdminApp(masterApp);
+ } else {
+ setupMasterAdminManagement(model, realm);
+ }
+ }
+ }
+
+ // TODO: We need method here, so we are able to refresh masterAdmin applications after import. Should be RealmManager moved to model/api instead?
+ public static void setupMasterAdminManagement(RealmProvider model, RealmModel realm) {
+ RealmModel adminRealm;
+ RoleModel adminRole;
+
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ adminRealm = realm;
+
+ adminRole = realm.addRole(AdminRoles.ADMIN);
+
+ RoleModel createRealmRole = realm.addRole(AdminRoles.CREATE_REALM);
+ adminRole.addCompositeRole(createRealmRole);
+ } else {
+ adminRealm = model.getRealmByName(Config.getAdminRealm());
+ adminRole = adminRealm.getRole(AdminRoles.ADMIN);
+ }
+
+ ApplicationModel realmAdminApp = KeycloakModelUtils.createApplication(adminRealm, ExportImportUtils.getMasterRealmAdminApplicationName(realm));
+ realmAdminApp.setBearerOnly(true);
+ realm.setMasterAdminApp(realmAdminApp);
+
+ for (String r : AdminRoles.ALL_REALM_ROLES) {
+ RoleModel role = realmAdminApp.addRole(r);
+ adminRole.addCompositeRole(role);
+ }
+ }
+
+
+ /**
+ * Fully import realm (or more realms from particular stream)
+ *
+ * @param session
+ * @param mapper
+ * @param is
+ * @param strategy
+ * @throws IOException
+ */
+ public static void importFromStream(KeycloakSession session, ObjectMapper mapper, InputStream is, Strategy strategy) throws IOException {
+ JsonFactory factory = mapper.getJsonFactory();
+ JsonParser parser = factory.createJsonParser(is);
+ try {
+ parser.nextToken();
+
+ if (parser.getCurrentToken() == JsonToken.START_ARRAY) {
+ // Case with more realms in stream
+ parser.nextToken();
+
+ List<RealmRepresentation> realmReps = new ArrayList<RealmRepresentation>();
+ while (parser.getCurrentToken() == JsonToken.START_OBJECT) {
+ RealmRepresentation realmRep = parser.readValueAs(RealmRepresentation.class);
+ parser.nextToken();
+
+ // Ensure that master realm is imported first
+ if (Config.getAdminRealm().equals(realmRep.getRealm())) {
+ realmReps.add(0, realmRep);
+ } else {
+ realmReps.add(realmRep);
+ }
+ }
+
+ for (RealmRepresentation realmRep : realmReps) {
+ importRealm(session, realmRep, strategy);
+ }
+ } else if (parser.getCurrentToken() == JsonToken.START_OBJECT) {
+ // Case with single realm in stream
+ RealmRepresentation realmRep = parser.readValueAs(RealmRepresentation.class);
+ importRealm(session, realmRep, strategy);
+ }
+ } finally {
+ parser.close();
+ }
+ }
+
+ // Assuming that it's invoked inside transaction
+ public static void importUsersFromStream(KeycloakSession session, String realmName, ObjectMapper mapper, InputStream is) throws IOException {
+ RealmProvider model = session.realms();
+ JsonFactory factory = mapper.getJsonFactory();
+ JsonParser parser = factory.createJsonParser(is);
+ try {
+ parser.nextToken();
+
+ while (parser.nextToken() == JsonToken.FIELD_NAME) {
+ if ("realm".equals(parser.getText())) {
+ parser.nextToken();
+ String currRealmName = parser.getText();
+ if (!currRealmName.equals(realmName)) {
+ throw new IllegalStateException("Trying to import users into invalid realm. Realm name: " + realmName + ", Expected realm name: " + realmName);
+ }
+ } else if ("users".equals(parser.getText())) {
+ parser.nextToken();
+
+ if (parser.getCurrentToken() == JsonToken.START_ARRAY) {
+ parser.nextToken();
+ }
+
+ // TODO: support for more transactions per single users file (if needed)
+ List<UserRepresentation> userReps = new ArrayList<UserRepresentation>();
+ while (parser.getCurrentToken() == JsonToken.START_OBJECT) {
+ UserRepresentation user = parser.readValueAs(UserRepresentation.class);
+ userReps.add(user);
+ parser.nextToken();
+ }
+
+ importUsers(session, model, realmName, userReps);
+
+ if (parser.getCurrentToken() == JsonToken.END_ARRAY) {
+ parser.nextToken();
+ }
+ }
+ }
+ } finally {
+ parser.close();
+ }
+ }
+
+ private static void importUsers(KeycloakSession session, RealmProvider model, String realmName, List<UserRepresentation> userReps) {
+ RealmModel realm = model.getRealmByName(realmName);
+ Map<String, ApplicationModel> apps = realm.getApplicationNameMap();
+ for (UserRepresentation user : userReps) {
+ RepresentationToModel.createUser(session, realm, user, apps);
+ }
+ }
+
+}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java
new file mode 100755
index 0000000..9395ceb
--- /dev/null
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java
@@ -0,0 +1,114 @@
+package org.keycloak.exportimport.util;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.jboss.logging.Logger;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ExportProvider;
+import org.keycloak.exportimport.UsersExportStrategy;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class MultipleStepsExportProvider implements ExportProvider {
+
+ protected final Logger logger = Logger.getLogger(getClass());
+
+ @Override
+ public void exportModel(KeycloakSessionFactory factory) throws IOException {
+ final RealmsHolder holder = new RealmsHolder();
+
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) {
+ List<RealmModel> realms = session.realms().getRealms();
+ holder.realms = realms;
+ }
+
+ });
+
+ for (RealmModel realm : holder.realms) {
+ exportRealm(factory, realm.getName());
+ }
+ }
+
+ @Override
+ public void exportRealm(KeycloakSessionFactory factory, final String realmName) throws IOException {
+ final UsersExportStrategy usersExportStrategy = ExportImportConfig.getUsersExportStrategy();
+ final int usersPerFile = ExportImportConfig.getUsersPerFile();
+ final UsersHolder usersHolder = new UsersHolder();
+ final boolean exportUsersIntoRealmFile = usersExportStrategy == UsersExportStrategy.REALM_FILE;
+
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ RealmModel realm = session.realms().getRealmByName(realmName);
+ RealmRepresentation rep = ExportUtils.exportRealm(session, realm, exportUsersIntoRealmFile);
+ writeRealm(realmName + "-realm.json", rep);
+ logger.info("Realm '" + realmName + "' - data exported");
+
+ // Count total number of users
+ if (!exportUsersIntoRealmFile) {
+ usersHolder.totalCount = session.users().getUsersCount(realm);
+ }
+ }
+
+ });
+
+ if (usersExportStrategy != UsersExportStrategy.SKIP && !exportUsersIntoRealmFile) {
+ // We need to export users now
+ usersHolder.currentPageStart = 0;
+
+ // usersExportStrategy==SAME_FILE means exporting all users into single file (but separate to realm)
+ final int countPerPage = (usersExportStrategy == UsersExportStrategy.SAME_FILE) ? usersHolder.totalCount : usersPerFile;
+
+ while (usersHolder.currentPageStart < usersHolder.totalCount) {
+ if (usersHolder.currentPageStart + countPerPage < usersHolder.totalCount) {
+ usersHolder.currentPageEnd = usersHolder.currentPageStart + countPerPage;
+ } else {
+ usersHolder.currentPageEnd = usersHolder.totalCount;
+ }
+
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ RealmModel realm = session.realms().getRealmByName(realmName);
+ usersHolder.users = session.users().getUsers(realm, usersHolder.currentPageStart, usersHolder.currentPageEnd - usersHolder.currentPageStart);
+
+ writeUsers(realmName + "-users-" + (usersHolder.currentPageStart / countPerPage) + ".json", session, realm, usersHolder.users);
+
+ logger.info("Users " + usersHolder.currentPageStart + "-" + (usersHolder.currentPageEnd -1) + " exported");
+ }
+
+ });
+
+ usersHolder.currentPageStart = usersHolder.currentPageEnd;
+ }
+ }
+ }
+
+ protected abstract void writeRealm(String fileName, RealmRepresentation rep) throws IOException;
+
+ protected abstract void writeUsers(String fileName, KeycloakSession session, RealmModel realm, List<UserModel> users) throws IOException;
+
+ public static class RealmsHolder {
+ List<RealmModel> realms;
+
+ }
+
+ public static class UsersHolder {
+ List<UserModel> users;
+ int totalCount;
+ int currentPageStart;
+ int currentPageEnd;
+ }
+}
diff --git a/export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000..9302943
--- /dev/null
+++ b/export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1,2 @@
+org.keycloak.exportimport.ExportSpi
+org.keycloak.exportimport.ImportSpi
\ No newline at end of file
export-import/export-import-dir/pom.xml 66(+66 -0)
diff --git a/export-import/export-import-dir/pom.xml b/export-import/export-import-dir/pom.xml
new file mode 100644
index 0000000..4cd8dc1
--- /dev/null
+++ b/export-import/export-import-dir/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-export-import-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-export-import-dir</artifactId>
+ <name>Keycloak Export Import To Directory</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java
new file mode 100644
index 0000000..512004d
--- /dev/null
+++ b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java
@@ -0,0 +1,123 @@
+package org.keycloak.exportimport.dir;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.exportimport.ImportProvider;
+import org.keycloak.exportimport.Strategy;
+import org.keycloak.exportimport.util.ExportImportJob;
+import org.keycloak.exportimport.util.ExportImportUtils;
+import org.keycloak.exportimport.util.ImportUtils;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class DirImportProvider implements ImportProvider {
+
+ private static final Logger logger = Logger.getLogger(DirImportProvider.class);
+
+ private final File rootDirectory;
+
+ public DirImportProvider() {
+ // Determine system tmp directory
+ String tempDir = System.getProperty("java.io.tmpdir");
+
+ // Delete and recreate directory inside tmp
+ this.rootDirectory = new File(tempDir + "/keycloak-export");
+ if (!this.rootDirectory .exists()) {
+ throw new IllegalStateException("Directory " + this.rootDirectory + " doesn't exists");
+ }
+
+ logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath());
+ }
+
+ public DirImportProvider(File rootDirectory) {
+ this.rootDirectory = rootDirectory;
+
+ logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath());
+ }
+
+ @Override
+ public void importModel(KeycloakSessionFactory factory, Strategy strategy) throws IOException {
+ File[] realmFiles = this.rootDirectory.listFiles(new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String name) {
+ return (name.endsWith("-realm.json"));
+ }
+ });
+
+ List<String> realmNames = new ArrayList<String>();
+ for (File file : realmFiles) {
+ String fileName = file.getName();
+ // Parse "foo" from "foo-realm.json"
+ String realmName = fileName.substring(0, fileName.length() - 11);
+
+ // Ensure that master realm is imported first
+ if (Config.getAdminRealm().equals(realmName)) {
+ realmNames.add(0, realmName);
+ } else {
+ realmNames.add(realmName);
+ }
+ }
+
+ for (String realmName : realmNames) {
+ importRealm(factory, realmName, strategy);
+ }
+ }
+
+ @Override
+ public void importRealm(KeycloakSessionFactory factory, final String realmName, final Strategy strategy) throws IOException {
+ File realmFile = new File(this.rootDirectory + File.separator + realmName + "-realm.json");
+ File[] userFiles = this.rootDirectory.listFiles(new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String name) {
+ return (name.startsWith(realmName)) && (name.endsWith(".json")) && (name.substring(realmName.length()).contains("-users-") );
+ }
+ });
+
+ // Import realm first
+ FileInputStream is = new FileInputStream(realmFile);
+ final RealmRepresentation realmRep = JsonSerialization.readValue(is, RealmRepresentation.class);
+
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ ImportUtils.importRealm(session, realmRep, strategy);
+ }
+
+ });
+
+ // Import users
+ for (File userFile : userFiles) {
+ final FileInputStream fis = new FileInputStream(userFile);
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, fis);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java
new file mode 100644
index 0000000..3e39a77
--- /dev/null
+++ b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java
@@ -0,0 +1,34 @@
+package org.keycloak.exportimport.dir;
+
+import java.io.File;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ImportProvider;
+import org.keycloak.exportimport.ImportProviderFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class DirImportProviderFactory implements ImportProviderFactory {
+
+ @Override
+ public ImportProvider create(KeycloakSession session) {
+ String dir = ExportImportConfig.getDir();
+ return dir!=null ? new DirImportProvider(new File(dir)) : new DirImportProvider();
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return DirExportProviderFactory.PROVIDER_ID;
+ }
+}
diff --git a/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory
new file mode 100644
index 0000000..8a8fea4
--- /dev/null
+++ b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory
@@ -0,0 +1 @@
+org.keycloak.exportimport.dir.DirExportProviderFactory
\ No newline at end of file
diff --git a/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory
new file mode 100644
index 0000000..ba9874e
--- /dev/null
+++ b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory
@@ -0,0 +1 @@
+org.keycloak.exportimport.dir.DirImportProviderFactory
\ No newline at end of file
diff --git a/export-import/export-import-single-file/pom.xml b/export-import/export-import-single-file/pom.xml
new file mode 100644
index 0000000..25d65c0
--- /dev/null
+++ b/export-import/export-import-single-file/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-export-import-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-export-import-single-file</artifactId>
+ <name>Keycloak Export Import To Single File</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java
new file mode 100755
index 0000000..a27f086
--- /dev/null
+++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java
@@ -0,0 +1,85 @@
+package org.keycloak.exportimport.singlefile;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.jboss.logging.Logger;
+import org.keycloak.exportimport.ExportProvider;
+import org.keycloak.exportimport.util.ExportImportJob;
+import org.keycloak.exportimport.util.ExportImportUtils;
+import org.keycloak.exportimport.util.ExportUtils;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SingleFileExportProvider implements ExportProvider {
+
+ private static final Logger logger = Logger.getLogger(SingleFileExportProvider.class);
+
+ private File file;
+
+ public SingleFileExportProvider(File file) {
+ this.file = file;
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ @Override
+ public void exportModel(KeycloakSessionFactory factory) throws IOException {
+ logger.infof("Exporting model into file %s", this.file.getAbsolutePath());
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ List<RealmModel> realms = session.realms().getRealms();
+ List<RealmRepresentation> reps = new ArrayList<RealmRepresentation>();
+ for (RealmModel realm : realms) {
+ reps.add(ExportUtils.exportRealm(session, realm, true));
+ }
+
+ writeToFile(reps);
+ }
+
+ });
+
+ }
+
+ @Override
+ public void exportRealm(KeycloakSessionFactory factory, final String realmName) throws IOException {
+ logger.infof("Exporting realm '%s' into file %s", realmName, this.file.getAbsolutePath());
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ RealmModel realm = session.realms().getRealmByName(realmName);
+ RealmRepresentation realmRep = ExportUtils.exportRealm(session, realm, true);
+ writeToFile(realmRep);
+ }
+
+ });
+ }
+
+ @Override
+ public void close() {
+ }
+
+ private ObjectMapper getObjectMapper() {
+ return JsonSerialization.prettyMapper;
+ }
+
+ private void writeToFile(Object reps) throws IOException {
+ FileOutputStream stream = new FileOutputStream(this.file);
+ getObjectMapper().writeValue(stream, reps);
+ }
+}
diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java
new file mode 100644
index 0000000..614c3ba
--- /dev/null
+++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java
@@ -0,0 +1,36 @@
+package org.keycloak.exportimport.singlefile;
+
+import java.io.File;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ExportProvider;
+import org.keycloak.exportimport.ExportProviderFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SingleFileExportProviderFactory implements ExportProviderFactory {
+
+ public static final String PROVIDER_ID = "singleFile";
+
+ @Override
+ public ExportProvider create(KeycloakSession session) {
+ String fileName = ExportImportConfig.getFile();
+ return new SingleFileExportProvider(new File(fileName));
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java
new file mode 100644
index 0000000..07e8cb2
--- /dev/null
+++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java
@@ -0,0 +1,59 @@
+package org.keycloak.exportimport.singlefile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.logging.Logger;
+import org.keycloak.exportimport.ImportProvider;
+import org.keycloak.exportimport.Strategy;
+import org.keycloak.exportimport.util.ExportImportJob;
+import org.keycloak.exportimport.util.ExportImportUtils;
+import org.keycloak.exportimport.util.ExportUtils;
+import org.keycloak.exportimport.util.ImportUtils;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SingleFileImportProvider implements ImportProvider {
+
+ private static final Logger logger = Logger.getLogger(SingleFileImportProvider.class);
+
+ private File file;
+
+ public SingleFileImportProvider(File file) {
+ this.file = file;
+ }
+
+ @Override
+ public void importModel(KeycloakSessionFactory factory, final Strategy strategy) throws IOException {
+ logger.infof("Full importing from file %s", this.file.getAbsolutePath());
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ FileInputStream is = new FileInputStream(file);
+ ImportUtils.importFromStream(session, JsonSerialization.mapper, is, strategy);
+ }
+
+ });
+ }
+
+ @Override
+ public void importRealm(KeycloakSessionFactory factory, String realmName, Strategy strategy) throws IOException {
+ // TODO: import just that single realm in case that file contains many realms?
+ importModel(factory, strategy);
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java
new file mode 100644
index 0000000..6e3e732
--- /dev/null
+++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java
@@ -0,0 +1,34 @@
+package org.keycloak.exportimport.singlefile;
+
+import java.io.File;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ImportProvider;
+import org.keycloak.exportimport.ImportProviderFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SingleFileImportProviderFactory implements ImportProviderFactory {
+
+ @Override
+ public ImportProvider create(KeycloakSession session) {
+ String fileName = ExportImportConfig.getFile();
+ return new SingleFileImportProvider(new File(fileName));
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return SingleFileExportProviderFactory.PROVIDER_ID;
+ }
+}
diff --git a/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory
new file mode 100644
index 0000000..82a1faf
--- /dev/null
+++ b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory
@@ -0,0 +1 @@
+org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory
\ No newline at end of file
diff --git a/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory
new file mode 100644
index 0000000..d2192b6
--- /dev/null
+++ b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory
@@ -0,0 +1 @@
+org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory
\ No newline at end of file
export-import/export-import-zip/pom.xml 70(+70 -0)
diff --git a/export-import/export-import-zip/pom.xml b/export-import/export-import-zip/pom.xml
new file mode 100644
index 0000000..7738f85
--- /dev/null
+++ b/export-import/export-import-zip/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-export-import-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-export-import-zip</artifactId>
+ <name>Keycloak Export Import To Encrypted ZIP</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>de.idyl</groupId>
+ <artifactId>winzipaes</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java
new file mode 100644
index 0000000..03f0147
--- /dev/null
+++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java
@@ -0,0 +1,44 @@
+package org.keycloak.exportimport.zip;
+
+import java.io.File;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ExportProvider;
+import org.keycloak.exportimport.ExportProviderFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ZipExportProviderFactory implements ExportProviderFactory {
+
+
+ public static final String PROVIDER_ID = "zip";
+
+ @Override
+ public ExportProvider create(KeycloakSession session) {
+ String fileName = ExportImportConfig.getZipFile();
+ String password = ExportImportConfig.getZipPassword();
+ if (fileName == null) {
+ throw new IllegalArgumentException("ZIP file for export not provided");
+ }
+ if (password == null) {
+ throw new IllegalArgumentException("Password for encrypting ZIP not provided");
+ }
+ return new ZipExportProvider(new File(fileName), password);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java
new file mode 100644
index 0000000..6970e87
--- /dev/null
+++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java
@@ -0,0 +1,124 @@
+package org.keycloak.exportimport.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.DataFormatException;
+
+import de.idyl.winzipaes.AesZipFileDecrypter;
+import de.idyl.winzipaes.impl.AESDecrypter;
+import de.idyl.winzipaes.impl.AESDecrypterBC;
+import de.idyl.winzipaes.impl.ExtZipEntry;
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.exportimport.ImportProvider;
+import org.keycloak.exportimport.Strategy;
+import org.keycloak.exportimport.util.ExportImportJob;
+import org.keycloak.exportimport.util.ExportImportUtils;
+import org.keycloak.exportimport.util.ImportUtils;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ZipImportProvider implements ImportProvider {
+
+ private static final Logger logger = Logger.getLogger(ZipImportProvider.class);
+
+ private final AesZipFileDecrypter decrypter;
+ private final String password;
+
+ public ZipImportProvider(File zipFile, String password) {
+ try {
+ if (!zipFile.exists()) {
+ throw new IllegalStateException("File " + zipFile.getAbsolutePath() + " doesn't exists");
+ }
+
+ AESDecrypter decrypter = new AESDecrypterBC();
+ this.decrypter = new AesZipFileDecrypter(zipFile, decrypter);
+ this.password = password;
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ logger.infof("Importing from ZIP file %s", zipFile.getAbsolutePath());
+ }
+
+ @Override
+ public void importModel(KeycloakSessionFactory factory, Strategy strategy) throws IOException {
+ List<String> realmNames = new ArrayList<String>();
+ for (ExtZipEntry entry : this.decrypter.getEntryList()) {
+ String entryName = entry.getName();
+ if (entryName.endsWith("-realm.json")) {
+ // Parse "foo" from "foo-realm.json"
+ String realmName = entryName.substring(0, entryName.length() - 11);
+
+ // Ensure that master realm is imported first
+ if (Config.getAdminRealm().equals(realmName)) {
+ realmNames.add(0, realmName);
+ } else {
+ realmNames.add(realmName);
+ }
+ }
+ }
+
+ for (String realmName : realmNames) {
+ importRealm(factory, realmName, strategy);
+ }
+ }
+
+ @Override
+ public void importRealm(KeycloakSessionFactory factory, final String realmName, final Strategy strategy) throws IOException {
+ try {
+ // Import realm first
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ this.decrypter.extractEntry(this.decrypter.getEntry(realmName + "-realm.json"), bos, this.password);
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ final RealmRepresentation realmRep = JsonSerialization.mapper.readValue(bis, RealmRepresentation.class);
+
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ ImportUtils.importRealm(session, realmRep, strategy);
+ }
+
+ });
+
+
+ // Import users
+ for (ExtZipEntry entry : this.decrypter.getEntryList()) {
+ String name = entry.getName();
+ if ( (name.startsWith(realmName)) && (name.endsWith(".json")) && (name.substring(realmName.length()).contains("-users-")) ) {
+ bos = new ByteArrayOutputStream();
+ this.decrypter.extractEntry(entry, bos, this.password);
+ final ByteArrayInputStream bis2 = new ByteArrayInputStream(bos.toByteArray());
+
+ ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
+
+ @Override
+ public void run(KeycloakSession session) throws IOException {
+ ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, bis2);
+ }
+ });
+ }
+ }
+ } catch (DataFormatException dfe) {
+ throw new RuntimeException(dfe);
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ this.decrypter.close();
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+}
diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java
new file mode 100644
index 0000000..9127a8b
--- /dev/null
+++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java
@@ -0,0 +1,41 @@
+package org.keycloak.exportimport.zip;
+
+import java.io.File;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ImportProvider;
+import org.keycloak.exportimport.ImportProviderFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ZipImportProviderFactory implements ImportProviderFactory {
+
+ @Override
+ public ImportProvider create(KeycloakSession session) {
+ String fileName = ExportImportConfig.getZipFile();
+ String password = ExportImportConfig.getZipPassword();
+ if (fileName == null) {
+ throw new IllegalArgumentException("ZIP file for import not provided");
+ }
+ if (password == null) {
+ throw new IllegalArgumentException("Password for decrypting ZIP not provided");
+ }
+ return new ZipImportProvider(new File(fileName), password);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return ZipExportProviderFactory.PROVIDER_ID;
+ }
+}
diff --git a/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory
new file mode 100644
index 0000000..dce02f1
--- /dev/null
+++ b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory
@@ -0,0 +1 @@
+org.keycloak.exportimport.zip.ZipExportProviderFactory
\ No newline at end of file
diff --git a/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory
new file mode 100644
index 0000000..b88f372
--- /dev/null
+++ b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory
@@ -0,0 +1 @@
+org.keycloak.exportimport.zip.ZipImportProviderFactory
\ No newline at end of file
export-import/pom.xml 4(+3 -1)
diff --git a/export-import/pom.xml b/export-import/pom.xml
index b9a5a4d..72468fa 100755
--- a/export-import/pom.xml
+++ b/export-import/pom.xml
@@ -16,7 +16,9 @@
<modules>
<module>export-import-api</module>
- <module>export-import-impl</module>
+ <module>export-import-dir</module>
+ <module>export-import-single-file</module>
+ <module>export-import-zip</module>
</modules>
</project>
federation/ldap/pom.xml 88(+88 -0)
diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml
new file mode 100755
index 0000000..0ce4c2e
--- /dev/null
+++ b/federation/ldap/pom.xml
@@ -0,0 +1,88 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-ldap-federation</artifactId>
+ <name>Keycloak LDAP Federation</name>
+ <description />
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-common</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-impl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-simple-schema</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/KeycloakLDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/KeycloakLDAPIdentityStore.java
new file mode 100755
index 0000000..1ff6c11
--- /dev/null
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/KeycloakLDAPIdentityStore.java
@@ -0,0 +1,89 @@
+package org.keycloak.federation.ldap;
+
+import org.keycloak.models.utils.reflection.Reflections;
+import org.picketlink.idm.config.LDAPMappingConfiguration;
+import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
+import org.picketlink.idm.ldap.internal.LDAPOperationManager;
+import org.picketlink.idm.model.AttributedType;
+import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.query.IdentityQuery;
+import org.picketlink.idm.spi.IdentityContext;
+
+import javax.naming.directory.BasicAttributes;
+import java.lang.reflect.Method;
+
+import static org.picketlink.common.constants.LDAPConstants.*;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KeycloakLDAPIdentityStore extends LDAPIdentityStore {
+
+ public static Method GET_OPERATION_MANAGER_METHOD;
+ public static Method CREATE_SEARCH_FILTER_METHOD;
+ public static Method EXTRACT_ATTRIBUTES_METHOD;
+ public static Method GET_ENTRY_IDENTIFIER_METHOD;
+
+ public static final String SAM_ACCOUNT_NAME = "sAMAccountName";
+
+ static {
+ GET_OPERATION_MANAGER_METHOD = getMethodOnLDAPStore("getOperationManager");
+ CREATE_SEARCH_FILTER_METHOD = getMethodOnLDAPStore("createIdentityTypeSearchFilter", IdentityQuery.class, LDAPMappingConfiguration.class);
+ EXTRACT_ATTRIBUTES_METHOD = getMethodOnLDAPStore("extractAttributes", AttributedType.class, boolean.class);
+ GET_ENTRY_IDENTIFIER_METHOD = getMethodOnLDAPStore("getEntryIdentifier", AttributedType.class);
+ }
+
+ @Override
+ public void addAttributedType(IdentityContext context, AttributedType attributedType) {
+ LDAPMappingConfiguration userMappingConfig = getConfig().getMappingConfig(attributedType.getClass());
+ String ldapUsernameAttrName = userMappingConfig.getMappedProperties().get(userMappingConfig.getIdProperty().getName());
+
+ if (getConfig().isActiveDirectory() && SAM_ACCOUNT_NAME.equals(ldapUsernameAttrName)) {
+ // TODO: pain, but everything in picketlink is private... Improve if possible...
+ LDAPOperationManager operationManager = Reflections.invokeMethod(false, GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, this);
+
+ String cn = getCommonName(attributedType);
+ String bindingDN = CN + EQUAL + cn + COMMA + userMappingConfig.getBaseDN();
+
+ BasicAttributes ldapAttributes = Reflections.invokeMethod(false, EXTRACT_ATTRIBUTES_METHOD, BasicAttributes.class, this, attributedType, true);
+ ldapAttributes.put(CN, cn);
+
+ operationManager.createSubContext(bindingDN, ldapAttributes);
+
+ String ldapId = Reflections.invokeMethod(false, GET_ENTRY_IDENTIFIER_METHOD, String.class, this, attributedType);
+ attributedType.setId(ldapId);
+ } else {
+ super.addAttributedType(context, attributedType);
+ }
+ }
+
+ // Hack as methods are protected on LDAPIdentityStore :/
+ public static Method getMethodOnLDAPStore(String methodName, Class... classes) {
+ try {
+ Method m = LDAPIdentityStore.class.getDeclaredMethod(methodName, classes);
+ m.setAccessible(true);
+ return m;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected String getCommonName(AttributedType aType) {
+ User user = (User)aType;
+ String fullName;
+ if (user.getFirstName() != null && user.getLastName() != null) {
+ fullName = user.getFirstName() + " " + user.getLastName();
+ } else if (user.getFirstName() != null && user.getFirstName().trim().length() > 0) {
+ fullName = user.getFirstName();
+ } else {
+ fullName = user.getLastName();
+ }
+
+ // Fallback to loginName
+ if (fullName == null || fullName.trim().length() == 0) {
+ fullName = user.getLoginName();
+ }
+
+ return fullName;
+ }
+}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
new file mode 100755
index 0000000..c42f74c
--- /dev/null
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -0,0 +1,296 @@
+package org.keycloak.federation.ldap;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.picketlink.idm.IdentityManagementException;
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.PartitionManager;
+import org.picketlink.idm.credential.Credentials;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.UsernamePasswordCredentials;
+import org.picketlink.idm.model.basic.BasicModel;
+import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.query.IdentityQuery;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class LDAPFederationProvider implements UserFederationProvider {
+ private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class);
+ public static final String LDAP_ID = "LDAP_ID";
+
+ protected KeycloakSession session;
+ protected UserFederationProviderModel model;
+ protected PartitionManager partitionManager;
+
+ protected static final Set<String> supportedCredentialTypes = new HashSet<String>();
+
+ static
+ {
+ supportedCredentialTypes.add(UserCredentialModel.PASSWORD);
+ }
+
+ public LDAPFederationProvider(KeycloakSession session, UserFederationProviderModel model, PartitionManager partitionManager) {
+ this.session = session;
+ this.model = model;
+ this.partitionManager = partitionManager;
+ }
+
+ private ModelException convertIDMException(IdentityManagementException ie) {
+ Throwable realCause = ie;
+ while (realCause.getCause() != null) {
+ realCause = realCause.getCause();
+ }
+
+ // Use the message from the realCause
+ return new ModelException(realCause.getMessage(), ie);
+ }
+
+ public KeycloakSession getSession() {
+ return session;
+ }
+
+ public UserFederationProviderModel getModel() {
+ return model;
+ }
+
+ public PartitionManager getPartitionManager() {
+ return partitionManager;
+ }
+
+ @Override
+ public UserModel proxy(UserModel local) {
+ // todo
+ return new LDAPUserModelDelegate(local, this);
+ }
+
+ @Override
+ public Set<String> getSupportedCredentialTypes() {
+ return supportedCredentialTypes;
+ }
+
+ @Override
+ public boolean isRegistrationSupported() {
+ return true;
+ }
+
+ @Override
+ public UserModel register(RealmModel realm, UserModel user) {
+ IdentityManager identityManager = getIdentityManager();
+
+ try {
+ User picketlinkUser = new User(user.getUsername());
+ picketlinkUser.setFirstName(user.getFirstName());
+ picketlinkUser.setLastName(user.getLastName());
+ picketlinkUser.setEmail(user.getEmail());
+ identityManager.add(picketlinkUser);
+ return proxy(user);
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+
+ }
+
+ @Override
+ public boolean removeUser(RealmModel realm, UserModel user) {
+ IdentityManager identityManager = getIdentityManager();
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, user.getUsername());
+ if (picketlinkUser == null) {
+ return false;
+ }
+ identityManager.remove(picketlinkUser);
+ return true;
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+ }
+
+ @Override
+ public List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm) {
+ IdentityManager identityManager = getIdentityManager();
+ List<UserModel> searchResults =new LinkedList<UserModel>();
+ try {
+ Map<String, User> results = new HashMap<String, User>();
+ if (attributes.containsKey(USERNAME)) {
+ User user = BasicModel.getUser(identityManager, attributes.get(USERNAME));
+ if (user != null) results.put(user.getLoginName(), user);
+ } else if (attributes.containsKey(EMAIL)) {
+ User user = queryByEmail(identityManager, attributes.get(EMAIL));
+ if (user != null) results.put(user.getLoginName(), user);
+ } else if (attributes.containsKey(FIRST_NAME) || attributes.containsKey(LAST_NAME)) {
+ IdentityQuery<User> query = identityManager.createIdentityQuery(User.class);
+ if (attributes.containsKey(FIRST_NAME)) {
+ query.setParameter(User.FIRST_NAME, attributes.get(FIRST_NAME));
+ }
+ if (attributes.containsKey(LAST_NAME)) {
+ query.setParameter(User.LAST_NAME, attributes.get(LAST_NAME));
+ }
+ List<User> agents = query.getResultList();
+ for (User user : agents) {
+ results.put(user.getLoginName(), user);
+ }
+ }
+ for (User user : results.values()) {
+ if (session.userStorage().getUserByUsername(user.getLoginName(), realm) == null) {
+ UserModel imported = importUserFromPicketlink(realm, user);
+ searchResults.add(imported);
+ }
+ }
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+ return searchResults;
+ }
+
+ @Override
+ public boolean isValid(UserModel local) {
+ IdentityManager identityManager = getIdentityManager();
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, local.getUsername());
+ if (picketlinkUser == null) {
+ return false;
+ }
+ return picketlinkUser.getId().equals(local.getAttribute(LDAP_ID));
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+ }
+
+ @Override
+ public UserModel getUserByUsername(RealmModel realm, String username) {
+ IdentityManager identityManager = getIdentityManager();
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, username);
+ if (picketlinkUser == null) {
+ return null;
+ }
+
+ return importUserFromPicketlink(realm, picketlinkUser);
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+ }
+
+ public IdentityManager getIdentityManager() {
+ return partitionManager.createIdentityManager();
+ }
+
+ protected UserModel importUserFromPicketlink(RealmModel realm, User picketlinkUser) {
+ String email = (picketlinkUser.getEmail() != null && picketlinkUser.getEmail().trim().length() > 0) ? picketlinkUser.getEmail() : null;
+ UserModel imported = session.userStorage().addUser(realm, picketlinkUser.getLoginName());
+ imported.setEnabled(true);
+ imported.setEmail(email);
+ imported.setFirstName(picketlinkUser.getFirstName());
+ imported.setLastName(picketlinkUser.getLastName());
+ imported.setFederationLink(model.getId());
+ imported.setAttribute(LDAP_ID, picketlinkUser.getId());
+ return proxy(imported);
+ }
+
+ protected User queryByEmail(IdentityManager identityManager, String email) throws IdentityManagementException {
+ List<User> agents = identityManager.createIdentityQuery(User.class)
+ .setParameter(User.EMAIL, email).getResultList();
+
+ if (agents.isEmpty()) {
+ return null;
+ } else if (agents.size() == 1) {
+ return agents.get(0);
+ } else {
+ throw new IdentityManagementException("Error - multiple Agent objects found with same login name");
+ }
+ }
+
+
+ @Override
+ public UserModel getUserByEmail(RealmModel realm, String email) {
+ IdentityManager identityManager = getIdentityManager();
+
+ try {
+ User picketlinkUser = queryByEmail(identityManager, email);
+ if (picketlinkUser == null) {
+ return null;
+ }
+ return importUserFromPicketlink(realm, picketlinkUser);
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+ }
+
+ @Override
+ public void preRemove(RealmModel realm) {
+ // complete Don't think we have to do anything
+ }
+
+ @Override
+ public void preRemove(RealmModel realm, RoleModel role) {
+ // complete I don't think we have to do anything here
+ }
+
+ public boolean validPassword(String username, String password) {
+ IdentityManager identityManager = getIdentityManager();
+
+ try {
+ UsernamePasswordCredentials credential = new UsernamePasswordCredentials();
+ credential.setUsername(username);
+ credential.setPassword(new Password(password.toCharArray()));
+ identityManager.validateCredentials(credential);
+ if (credential.getStatus() == Credentials.Status.VALID) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+ }
+
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+ for (UserCredentialModel cred : input) {
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ return validPassword(user.getUsername(), cred.getValue());
+ } else {
+ return false; // invalid cred type
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
+ for (UserCredentialModel cred : input) {
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ return validPassword(user.getUsername(), cred.getValue());
+ } else {
+ return false; // invalid cred type
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void close() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
new file mode 100755
index 0000000..b656d7a
--- /dev/null
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
@@ -0,0 +1,43 @@
+package org.keycloak.federation.ldap;
+
+import org.keycloak.Config;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderFactory;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.picketlink.idm.PartitionManager;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class LDAPFederationProviderFactory implements UserFederationProviderFactory {
+ public static final String PROVIDER_NAME = "ldap";
+ PartitionManagerRegistry registry;
+
+ @Override
+ public UserFederationProvider create(KeycloakSession session) {
+ throw new IllegalAccessError("Illegal to call this method");
+ }
+
+ @Override
+ public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
+ PartitionManager partition = registry.getPartitionManager(model);
+ return new LDAPFederationProvider(session, model, partition);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ registry = new PartitionManagerRegistry();
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_NAME;
+ }
+}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPKeycloakCredentialHandler.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPKeycloakCredentialHandler.java
new file mode 100755
index 0000000..7078d87
--- /dev/null
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPKeycloakCredentialHandler.java
@@ -0,0 +1,168 @@
+package org.keycloak.federation.ldap;
+
+import org.keycloak.models.utils.reflection.Reflections;
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.config.LDAPMappingConfiguration;
+import org.picketlink.idm.credential.Credentials;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.UsernamePasswordCredentials;
+import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
+import org.picketlink.idm.ldap.internal.LDAPOperationManager;
+import org.picketlink.idm.ldap.internal.LDAPPlainTextPasswordCredentialHandler;
+import org.picketlink.idm.model.Account;
+import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.query.IdentityQuery;
+import org.picketlink.idm.spi.IdentityContext;
+
+import javax.naming.NamingException;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchResult;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.List;
+
+import static org.picketlink.idm.IDMLog.CREDENTIAL_LOGGER;
+import static org.picketlink.idm.model.basic.BasicModel.getUser;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredentialHandler {
+
+ // Used just in ActiveDirectory to put account into "enabled" state (aka userAccountControl=512, see http://support.microsoft.com/kb/305144/en ) after password update. If value is -1, it's ignored
+ private String userAccountControlAfterPasswordUpdate;
+
+ @Override
+ public void setup(LDAPIdentityStore store) {
+ // TODO: Don't setup it here once PLINK-508 is fixed
+ if (store.getConfig().isActiveDirectory() || Boolean.getBoolean("keycloak.ldap.ad.skipUserAccountControlAfterPasswordUpdate")) {
+ String userAccountControlProp = System.getProperty("keycloak.ldap.ad.userAccountControlAfterPasswordUpdate");
+ this.userAccountControlAfterPasswordUpdate = userAccountControlProp!=null ? userAccountControlProp : "512";
+ CREDENTIAL_LOGGER.info("Will use userAccountControl=" + userAccountControlAfterPasswordUpdate + " after password update of user in Active Directory");
+ }
+ }
+
+ // Overridden as in Keycloak, we don't have Agents
+ @Override
+ protected User getAccount(IdentityContext context, String loginName) {
+ IdentityManager identityManager = getIdentityManager(context);
+
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Trying to find account [%s] using default account type [%s]", loginName, User.class);
+ }
+
+ return getUser(identityManager, loginName);
+ }
+
+ @Override
+ public void update(IdentityContext context, Account account, Password password, LDAPIdentityStore store, Date effectiveDate, Date expiryDate) {
+ if (!store.getConfig().isActiveDirectory()) {
+ super.update(context, account, password, store, effectiveDate, expiryDate);
+ } else {
+ User user = (User)account;
+ LDAPOperationManager operationManager = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, store);
+ IdentityManager identityManager = getIdentityManager(context);
+ String userDN = getDNOfUser(user, identityManager, store, operationManager);
+
+ updateADPassword(userDN, new String(password.getValue()), operationManager);
+
+ if (userAccountControlAfterPasswordUpdate != null) {
+ ModificationItem[] mods = new ModificationItem[1];
+ BasicAttribute mod0 = new BasicAttribute("userAccountControl", userAccountControlAfterPasswordUpdate);
+ mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
+ operationManager.modifyAttribute(userDN, mod0);
+ }
+ }
+ }
+
+ protected void updateADPassword(String userDN, String password, LDAPOperationManager operationManager) {
+ try {
+ // Replace the "unicdodePwd" attribute with a new value
+ // Password must be both Unicode and a quoted string
+ String newQuotedPassword = "\"" + password + "\"";
+ byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
+ BasicAttribute unicodePwd = new BasicAttribute("unicodePwd", newUnicodePassword);
+ operationManager.modifyAttribute(userDN, unicodePwd);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void validate(IdentityContext context, UsernamePasswordCredentials credentials,
+ LDAPIdentityStore store) {
+ credentials.setStatus(Credentials.Status.INVALID);
+ credentials.setValidatedAccount(null);
+
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Validating credentials [%s][%s] using identity store [%s] and credential handler [%s].", credentials.getClass(), credentials, store, this);
+ }
+
+ User account = getAccount(context, credentials.getUsername());
+
+ // If the user for the provided username cannot be found we fail validation
+ if (account != null) {
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Found account [%s] from credentials [%s].", account, credentials);
+ }
+
+ if (account.isEnabled()) {
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Account [%s] is ENABLED.", account, credentials);
+ }
+
+ char[] password = credentials.getPassword().getValue();
+
+ // String bindingDN = store.getBindingDN(account);
+
+ LDAPOperationManager operationManager = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, store);
+ String bindingDN = getDNOfUser(account, getIdentityManager(context), store, operationManager);
+
+ if (operationManager.authenticate(bindingDN, new String(password))) {
+ credentials.setValidatedAccount(account);
+ credentials.setStatus(Credentials.Status.VALID);
+ }
+ } else {
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Account [%s] is DISABLED.", account, credentials);
+ }
+ credentials.setStatus(Credentials.Status.ACCOUNT_DISABLED);
+ }
+ } else {
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Account NOT FOUND for credentials [%s][%s].", credentials.getClass(), credentials);
+ }
+ }
+
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Credential [%s][%s] validated using identity store [%s] and credential handler [%s]. Status [%s]. Validated Account [%s]",
+ credentials.getClass(), credentials, store, this, credentials.getStatus(), credentials.getValidatedAccount());
+ }
+ }
+
+ // TODO: remove later... It's needed just because LDAPIdentityStore.getBindingDN, which always uses idProperty as first part of DN, but in AD it doesn't work as we may have idProperty 'sAMAccountName'
+ // but DN like: cn=John Doe,OU=foo,DC=bar
+ protected String getDNOfUser(User user, IdentityManager identityManager, LDAPIdentityStore ldapStore, LDAPOperationManager operationManager) {
+
+ LDAPMappingConfiguration ldapEntryConfig = ldapStore.getConfig().getMappingConfig(User.class);
+ IdentityQuery<User> identityQuery = identityManager.createIdentityQuery(User.class)
+ .setParameter(User.LOGIN_NAME, user.getLoginName());
+ StringBuilder filter = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.CREATE_SEARCH_FILTER_METHOD, StringBuilder.class, ldapStore, identityQuery, ldapEntryConfig);
+
+ List<SearchResult> search = null;
+ try {
+ search = operationManager.search(ldapEntryConfig.getBaseDN(), filter.toString(), ldapEntryConfig);
+ } catch (NamingException ne) {
+ throw new RuntimeException(ne);
+ }
+
+ if (search.size() > 0) {
+ SearchResult sr1 = search.get(0);
+ return sr1.getNameInNamespace();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUserModelDelegate.java
new file mode 100755
index 0000000..2a7fad5
--- /dev/null
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUserModelDelegate.java
@@ -0,0 +1,269 @@
+package org.keycloak.federation.ldap;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.picketlink.idm.IdentityManagementException;
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.TOTPCredential;
+import org.picketlink.idm.model.basic.BasicModel;
+import org.picketlink.idm.model.basic.User;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class LDAPUserModelDelegate implements UserModel {
+ private static final Logger logger = Logger.getLogger(LDAPUserModelDelegate.class);
+
+ protected UserModel delegate;
+ protected LDAPFederationProvider provider;
+
+ public LDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) {
+ this.delegate = delegate;
+ this.provider = provider;
+ }
+
+ @Override
+ public String getId() {
+ return delegate.getId();
+ }
+
+ @Override
+ public void setAttribute(String name, String value) {
+ delegate.setAttribute(name, value);
+ }
+
+ @Override
+ public boolean isEmailVerified() {
+ return delegate.isEmailVerified();
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ delegate.removeAttribute(name);
+ }
+
+ @Override
+ public String getLastName() {
+ return delegate.getLastName();
+ }
+
+ @Override
+ public void setFederationLink(String link) {
+ delegate.setFederationLink(link);
+ }
+
+ @Override
+ public AuthenticationLinkModel getAuthenticationLink() {
+ return delegate.getAuthenticationLink();
+ }
+
+ @Override
+ public Map<String, String> getAttributes() {
+ return delegate.getAttributes();
+ }
+
+ @Override
+ public boolean hasRole(RoleModel role) {
+ return delegate.hasRole(role);
+ }
+
+ @Override
+ public void grantRole(RoleModel role) {
+ delegate.grantRole(role);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ delegate.setEnabled(enabled);
+ }
+
+ @Override
+ public void removeRequiredAction(UserModel.RequiredAction action) {
+ delegate.removeRequiredAction(action);
+ }
+
+ @Override
+ public void deleteRoleMapping(RoleModel role) {
+ delegate.deleteRoleMapping(role);
+ }
+
+ @Override
+ public void setUsername(String username) {
+ IdentityManager identityManager = provider.getIdentityManager();
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
+ if (picketlinkUser == null) {
+ throw new IllegalStateException("User not found in LDAP storage!");
+ }
+ picketlinkUser.setLoginName(username);
+ identityManager.update(picketlinkUser);
+ } catch (IdentityManagementException ie) {
+ throw new ModelException(ie);
+ }
+ delegate.setUsername(username);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return delegate.isEnabled();
+ }
+
+ @Override
+ public String getFirstName() {
+ return delegate.getFirstName();
+ }
+
+ @Override
+ public void setLastName(String lastName) {
+ IdentityManager identityManager = provider.getIdentityManager();
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
+ if (picketlinkUser == null) {
+ throw new IllegalStateException("User not found in LDAP storage!");
+ }
+ picketlinkUser.setLastName(lastName);
+ identityManager.update(picketlinkUser);
+ } catch (IdentityManagementException ie) {
+ throw new ModelException(ie);
+ }
+ delegate.setLastName(lastName);
+ }
+
+ @Override
+ public void setEmailVerified(boolean verified) {
+ delegate.setEmailVerified(verified);
+ }
+
+ @Override
+ public void updateCredential(UserCredentialModel cred) {
+ if (!provider.getSupportedCredentialTypes().contains(cred.getType())) {
+ delegate.updateCredential(cred);
+ return;
+ }
+ IdentityManager identityManager = provider.getIdentityManager();
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, getUsername());
+ if (picketlinkUser == null) {
+ logger.debugf("User '%s' doesn't exists. Skip password update", getUsername());
+ throw new IllegalStateException("User doesn't exist in LDAP storage");
+ }
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ identityManager.updateCredential(picketlinkUser, new Password(cred.getValue().toCharArray()));
+ } else if (cred.getType().equals(UserCredentialModel.TOTP)) {
+ TOTPCredential credential = new TOTPCredential(cred.getValue());
+ credential.setDevice(cred.getDevice());
+ identityManager.updateCredential(picketlinkUser, credential);
+ }
+ } catch (IdentityManagementException ie) {
+ throw new ModelException(ie);
+ }
+
+ }
+
+ @Override
+ public void setEmail(String email) {
+ IdentityManager identityManager = provider.getIdentityManager();
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
+ if (picketlinkUser == null) {
+ throw new IllegalStateException("User not found in LDAP storage!");
+ }
+ picketlinkUser.setEmail(email);
+ identityManager.update(picketlinkUser);
+ } catch (IdentityManagementException ie) {
+ throw new ModelException(ie);
+ }
+ delegate.setEmail(email);
+ }
+
+ @Override
+ public void addRequiredAction(UserModel.RequiredAction action) {
+ delegate.addRequiredAction(action);
+ }
+
+ @Override
+ public List<UserCredentialValueModel> getCredentialsDirectly() {
+ return delegate.getCredentialsDirectly();
+ }
+
+ @Override
+ public boolean isTotp() {
+ return delegate.isTotp();
+ }
+
+ @Override
+ public void setFirstName(String firstName) {
+ delegate.setFirstName(firstName);
+ }
+
+ @Override
+ public Set<UserModel.RequiredAction> getRequiredActions() {
+ return delegate.getRequiredActions();
+ }
+
+ @Override
+ public String getEmail() {
+ return delegate.getEmail();
+ }
+
+ @Override
+ public void setTotp(boolean totp) {
+ delegate.setTotp(totp);
+ }
+
+ @Override
+ public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
+ delegate.setAuthenticationLink(authenticationLink);
+ }
+
+ @Override
+ public String getUsername() {
+ return delegate.getUsername();
+ }
+
+ @Override
+ public String getFederationLink() {
+ return delegate.getFederationLink();
+ }
+
+ @Override
+ public Set<RoleModel> getRealmRoleMappings() {
+ return delegate.getRealmRoleMappings();
+ }
+
+ @Override
+ public Set<RoleModel> getRoleMappings() {
+ return delegate.getRoleMappings();
+ }
+
+ @Override
+ public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
+ return delegate.getApplicationRoleMappings(app);
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ return delegate.getAttribute(name);
+ }
+
+ @Override
+ public void updateCredentialDirectly(UserCredentialValueModel cred) {
+ delegate.updateCredentialDirectly(cred);
+ }
+}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/PartitionManagerRegistry.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/PartitionManagerRegistry.java
new file mode 100755
index 0000000..5c83bda
--- /dev/null
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/PartitionManagerRegistry.java
@@ -0,0 +1,158 @@
+package org.keycloak.federation.ldap;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.LDAPConstants;
+import org.picketlink.idm.PartitionManager;
+import org.picketlink.idm.config.AbstractIdentityStoreConfiguration;
+import org.picketlink.idm.config.IdentityConfiguration;
+import org.picketlink.idm.config.IdentityConfigurationBuilder;
+import org.picketlink.idm.config.IdentityStoreConfiguration;
+import org.picketlink.idm.config.LDAPIdentityStoreConfiguration;
+import org.picketlink.idm.internal.DefaultPartitionManager;
+import org.picketlink.idm.model.basic.User;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.picketlink.common.constants.LDAPConstants.*;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PartitionManagerRegistry {
+
+ private static final Logger logger = Logger.getLogger(PartitionManagerRegistry.class);
+
+ private Map<String, PartitionManagerContext> partitionManagers = new ConcurrentHashMap<String, PartitionManagerContext>();
+
+ public PartitionManager getPartitionManager(UserFederationProviderModel model) {
+ PartitionManagerContext context = partitionManagers.get(model.getId());
+
+ // Ldap config might have changed for the realm. In this case, we must re-initialize
+ Map<String, String> config = model.getConfig();
+ if (context == null || !config.equals(context.config)) {
+ logger.infof("Creating new partition manager for the federation provider: %s, LDAP Connection URL: %s, LDAP Base DN: %s, LDAP Vendor: %s", model.getId(),
+ config.get(LDAPConstants.CONNECTION_URL), config.get(LDAPConstants.BASE_DN), config.get(LDAPConstants.VENDOR));
+ PartitionManager manager = createPartitionManager(config);
+ context = new PartitionManagerContext(config, manager);
+ partitionManagers.put(model.getId(), context);
+ }
+ return context.partitionManager;
+ }
+
+ /**
+ * @param ldapConfig from realm
+ * @return PartitionManager instance based on LDAP store
+ */
+ protected PartitionManager createPartitionManager(Map<String,String> ldapConfig) {
+ IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
+
+ Properties connectionProps = new Properties();
+ connectionProps.put("com.sun.jndi.ldap.connect.pool", "true");
+
+ checkSystemProperty("com.sun.jndi.ldap.connect.pool.authentication", "none simple");
+ checkSystemProperty("com.sun.jndi.ldap.connect.pool.initsize", "1");
+ checkSystemProperty("com.sun.jndi.ldap.connect.pool.maxsize", "10");
+ checkSystemProperty("com.sun.jndi.ldap.connect.pool.prefsize", "5");
+ checkSystemProperty("com.sun.jndi.ldap.connect.pool.timeout", "300000");
+ checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain");
+ checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off");
+
+ String vendor = ldapConfig.get(LDAPConstants.VENDOR);
+
+ // RHDS is using "nsuniqueid" as unique identifier instead of "entryUUID"
+ if (vendor != null && vendor.equals(LDAPConstants.VENDOR_RHDS)) {
+ checkSystemProperty(LDAPIdentityStoreConfiguration.ENTRY_IDENTIFIER_ATTRIBUTE_NAME, "nsuniqueid");
+ }
+
+ boolean activeDirectory = vendor != null && vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY);
+
+ String ldapLoginNameMapping = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
+ if (ldapLoginNameMapping == null) {
+ ldapLoginNameMapping = activeDirectory ? CN : UID;
+ }
+
+ // Try to compute properties based on LDAP server type, but still allow to override them through System properties TODO: Should allow better way than overriding from System properties. Perhaps init from XML?
+ ldapLoginNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.loginName", ldapLoginNameMapping, ldapLoginNameMapping, activeDirectory);
+ String ldapFirstNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.firstName", CN, "givenName", activeDirectory);
+ String ldapLastNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.lastName", SN, SN, activeDirectory);
+ String ldapEmailMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.email", EMAIL, EMAIL, activeDirectory);
+
+ String[] userObjectClasses = getUserObjectClasses(ldapConfig);
+
+ logger.infof("LDAP Attributes mapping: loginName: %s, firstName: %s, lastName: %s, email: %s", ldapLoginNameMapping, ldapFirstNameMapping, ldapLastNameMapping, ldapEmailMapping);
+
+ // Use same mapping for User and Agent for now
+ builder
+ .named("SIMPLE_LDAP_STORE_CONFIG")
+ .stores()
+ .ldap()
+ .connectionProperties(connectionProps)
+ .addCredentialHandler(LDAPKeycloakCredentialHandler.class)
+ .baseDN(ldapConfig.get(LDAPConstants.BASE_DN))
+ .bindDN(ldapConfig.get(LDAPConstants.BIND_DN))
+ .bindCredential(ldapConfig.get(LDAPConstants.BIND_CREDENTIAL))
+ .url(ldapConfig.get(LDAPConstants.CONNECTION_URL))
+ .activeDirectory(activeDirectory)
+ .supportAllFeatures()
+ .mapping(User.class)
+ .baseDN(ldapConfig.get(LDAPConstants.USER_DN_SUFFIX))
+ .objectClasses(userObjectClasses)
+ .attribute("loginName", ldapLoginNameMapping, true)
+ .attribute("firstName", ldapFirstNameMapping)
+ .attribute("lastName", ldapLastNameMapping)
+ .attribute("email", ldapEmailMapping);
+
+ // Workaround to override the LDAPIdentityStore with our own :/
+ List<IdentityConfiguration> identityConfigs = builder.buildAll();
+ IdentityStoreConfiguration identityStoreConfig = identityConfigs.get(0).getStoreConfiguration().get(0);
+ ((AbstractIdentityStoreConfiguration)identityStoreConfig).setIdentityStoreType(KeycloakLDAPIdentityStore.class);
+
+ return new DefaultPartitionManager(identityConfigs);
+ }
+
+ private void checkSystemProperty(String name, String defaultValue) {
+ if (System.getProperty(name) == null) {
+ System.setProperty(name, defaultValue);
+ }
+ }
+
+ private String getNameOfLDAPAttribute(String systemPropertyName, String defaultAttrName, String defaultAttrNameInActiveDirectory, boolean activeDirectory) {
+ // System property has biggest priority if available
+ String sysProperty = System.getProperty(systemPropertyName);
+ if (sysProperty != null) {
+ return sysProperty;
+ }
+
+ return activeDirectory ? defaultAttrNameInActiveDirectory : defaultAttrName;
+ }
+
+ // Parse array of strings like [ "inetOrgPerson", "organizationalPerson" ] from the string like: "inetOrgPerson, organizationalPerson"
+ private String[] getUserObjectClasses(Map<String,String> ldapConfig) {
+ String objClassesCfg = ldapConfig.get(LDAPConstants.USER_OBJECT_CLASSES);
+ String objClassesStr = (objClassesCfg != null && objClassesCfg.length() > 0) ? objClassesCfg.trim() : "inetOrgPerson, organizationalPerson";
+
+ String[] objectClasses = objClassesStr.split(",");
+
+ // Trim them
+ String[] userObjectClasses = new String[objectClasses.length];
+ for (int i=0 ; i<objectClasses.length ; i++) {
+ userObjectClasses[i] = objectClasses[i].trim();
+ }
+ return userObjectClasses;
+ }
+
+ private class PartitionManagerContext {
+
+ private PartitionManagerContext(Map<String,String> config, PartitionManager manager) {
+ this.config = config;
+ this.partitionManager = manager;
+ }
+
+ private Map<String,String> config;
+ private PartitionManager partitionManager;
+ }
+}
diff --git a/federation/ldap/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory
new file mode 100755
index 0000000..b8921e9
--- /dev/null
+++ b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory
@@ -0,0 +1 @@
+org.keycloak.federation.ldap.LDAPFederationProviderFactory
\ No newline at end of file
federation/pom.xml 22(+22 -0)
diff --git a/federation/pom.xml b/federation/pom.xml
new file mode 100755
index 0000000..5e15f90
--- /dev/null
+++ b/federation/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>pom</packaging>
+
+ <artifactId>keycloak-federation-parent</artifactId>
+ <name>Keycloak Federation</name>
+ <description />
+
+ <modules>
+ <module>ldap</module>
+ </modules>
+
+</project>
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
index e7d9636..ee84cc6 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
@@ -124,7 +124,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
attributes.put("totp", new TotpBean(user, baseUri));
break;
case SOCIAL:
- attributes.put("social", new AccountSocialBean(realm, user, uriInfo.getBaseUri()));
+ attributes.put("social", new AccountSocialBean(session, realm, user, uriInfo.getBaseUri()));
break;
case LOG:
attributes.put("log", new LogBean(events));
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java
index b07d38b..06ef6c2 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java
@@ -22,7 +22,7 @@ public class AccountBean {
}
public String getUsername() {
- return user.getLoginName();
+ return user.getUsername();
}
public String getEmail() {
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
index 074174f..ad2786c 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
@@ -8,6 +8,7 @@ import java.util.Set;
import javax.ws.rs.core.UriBuilder;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserModel;
@@ -22,13 +23,15 @@ public class AccountSocialBean {
private final List<SocialLinkEntry> socialLinks;
private final boolean removeLinkPossible;
+ private final KeycloakSession session;
- public AccountSocialBean(RealmModel realm, UserModel user, URI baseUri) {
+ public AccountSocialBean(KeycloakSession session, RealmModel realm, UserModel user, URI baseUri) {
+ this.session = session;
URI accountSocialUpdateUri = Urls.accountSocialUpdate(baseUri, realm.getName());
this.socialLinks = new LinkedList<SocialLinkEntry>();
Map<String, String> socialConfig = realm.getSocialConfig();
- Set<SocialLinkModel> userSocialLinks = realm.getSocialLinks(user);
+ Set<SocialLinkModel> userSocialLinks = session.users().getSocialLinks(user, realm);
int availableLinks = 0;
if (socialConfig != null && !socialConfig.isEmpty()) {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index 2671891..60560f3 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -44,20 +44,48 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
});
module.controller('ApplicationSessionsCtrl', function($scope, realm, sessionCount, application,
- ApplicationUserSessions,
- $location, Dialog, Notifications) {
+ ApplicationUserSessions) {
$scope.realm = realm;
$scope.count = sessionCount.count;
$scope.sessions = [];
$scope.application = application;
+ $scope.page = 0;
+
+ $scope.query = {
+ realm : realm.realm,
+ application: $scope.application.name,
+ max : 5,
+ first : 0
+ }
+
+ $scope.firstPage = function() {
+ $scope.query.first = 0;
+ if ($scope.query.first < 0) {
+ $scope.query.first = 0;
+ }
+ $scope.loadUsers();
+ }
+
+ $scope.previousPage = function() {
+ $scope.query.first -= parseInt($scope.query.max);
+ if ($scope.query.first < 0) {
+ $scope.query.first = 0;
+ }
+ $scope.loadUsers();
+ }
+
+ $scope.nextPage = function() {
+ $scope.query.first += parseInt($scope.query.max);
+ $scope.loadUsers();
+ }
+
$scope.toDate = function(val) {
return new Date(val);
};
$scope.loadUsers = function() {
- ApplicationUserSessions.query({ realm : realm.realm, application: $scope.application.name }, function(updated) {
- $scope.count = updated.length;
+ ApplicationUserSessions.query($scope.query, function(updated) {
$scope.sessions = updated;
})
};
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index e38b4e1..571bbfc 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -134,18 +134,42 @@ module.controller('UserSocialCtrl', function($scope, realm, user, socialLinks) {
module.controller('UserListCtrl', function($scope, realm, User) {
$scope.realm = realm;
+ $scope.page = 0;
+
+ $scope.query = {
+ realm: realm.realm,
+ max : 5,
+ first : 0
+ }
+
+ $scope.firstPage = function() {
+ $scope.query.first = 0;
+ if ($scope.query.first < 0) {
+ $scope.query.first = 0;
+ }
+ $scope.searchQuery();
+ }
+
+ $scope.previousPage = function() {
+ $scope.query.first -= parseInt($scope.query.max);
+ if ($scope.query.first < 0) {
+ $scope.query.first = 0;
+ }
+ $scope.searchQuery();
+ }
+
+ $scope.nextPage = function() {
+ $scope.query.first += parseInt($scope.query.max);
+ $scope.searchQuery();
+ }
+
$scope.searchQuery = function() {
$scope.searchLoaded = false;
$scope.currentSearch = $scope.search;
- var params = { realm: realm.realm };
- if ($scope.search) {
- params.search = $scope.search;
- }
-
- $scope.users = User.query(params, function() {
+ $scope.users = User.query($scope.query, function() {
$scope.searchLoaded = true;
- $scope.lastSearch = params.search;
+ $scope.lastSearch = $scope.query.search;
});
};
});
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index 415734a..57a3a27 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -908,6 +908,7 @@ module.factory('PasswordPolicy', function() {
var p = {};
p.policyMessages = {
+ hashIterations: "Number of hashing iterations. Default is 1. Recommended is 50000.",
length: "Minimal password length (integer type). Default value is 8.",
digits: "Minimal number (integer type) of digits in password. Default value is 1.",
lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.",
@@ -916,6 +917,7 @@ module.factory('PasswordPolicy', function() {
}
p.allPolicies = [
+ { name: 'hashIterations', value: 1 },
{ name: 'length', value: 8 },
{ name: 'digits', value: 1 },
{ name: 'lowerCase', value: 1 },
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-sessions.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-sessions.html
index 93c388e..248838a 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-sessions.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-sessions.html
@@ -28,19 +28,30 @@
</form>
<table class="table table-striped table-bordered" data-ng-show="count > 0">
<thead>
- <tr>
+ <tr data-ng-hide="sessions">
<th class="kc-table-actions" colspan="3">
<div class="pull-right">
- <a class="btn btn-primary" ng-click="loadUsers()">Show Users</a>
+ <a class="btn btn-primary" ng-click="loadUsers()">Show Sessions</a>
</div>
</th>
</tr>
- <tr>
+ <tr data-ng-show="sessions">
<th>User</th>
<th>From IP</th>
<th>Session Start</th>
</tr>
</thead>
+ <tfoot data-ng-show="sessions">
+ <tr>
+ <td colspan="7">
+ <div class="table-nav">
+ <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">First page</button>
+ <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
+ <button data-ng-click="nextPage()" class="next" ng-disabled="sessions.length < query.max">Next page</button>
+ </div>
+ </td>
+ </tr>
+ </tfoot>
<tbody>
<tr data-ng-repeat="session in sessions">
<td><a href="#/realms/{{realm.realm}}/users/{{session.user}}">{{session.user}}</a></td>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html
index 75a4b7a..b590604 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html
@@ -50,7 +50,7 @@
<input class="form-control disabled" type="text" value="{{p.name|capitalize}}" readonly>
</td>
<td>
- <input class="form-control" ng-model="p.value" type="number" placeholder="No value assigned" min="1" max="50">
+ <input class="form-control" ng-model="p.value" type="number" placeholder="No value assigned" min="1">
</td>
<td class="actions">
<div class="action-div"><i class="pficon pficon-delete" ng-click="removePolicy($index)" tooltip-placement="right" tooltip="Remove Policy"></i></div>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
index 3b735dd..bfaaa59 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
@@ -80,7 +80,7 @@
</div>
</fieldset>
<fieldset>
- <legend><span class="text">Optional Settings</span></legend>
+ <legend><span class="text">Theme Settings</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="loginTheme">Login Theme</label>
<div class="col-sm-4">
@@ -130,6 +130,21 @@
</div>
</div>
</fieldset>
+ <fieldset>
+ <legend><span class="text">Cache Settings</span></legend>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="enabled">Realm Cache Enabled</label>
+ <div class="col-sm-4">
+ <input ng-model="realm.realmCacheEnabled" name="realmCacheEnabled" id="realmCacheEnabled" onoffswitch />
+ </div>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="enabled">User Cache Enabled</label>
+ <div class="col-sm-4">
+ <input ng-model="realm.userCacheEnabled" name="userCacheEnabled" id="userCacheEnabled" onoffswitch />
+ </div>
+ </div>
+ </fieldset>
<div class="pull-right form-actions" data-ng-show="createRealm && access.manageRealm">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html
index dc54a55..d6dcd49 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-list.html
@@ -15,7 +15,7 @@
<tr>
<th class="kc-table-actions" colspan="4">
<div class="search-comp clearfix">
- <input type="text" placeholder="Search..." data-ng-model="search" class="form-control search"
+ <input type="text" placeholder="Search..." data-ng-model="query.search" class="form-control search"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button data-ng-click="searchQuery()" type="submit"
class="kc-icon-search" tooltip-placement="right"
@@ -38,19 +38,17 @@
</tr>
</tr>
</thead>
- <!-- todo -->
- <!--<tfoot data-ng-show="users && users.length > 10">
+ <tfoot data-ng-hide="!users || users.length == 0">
<tr>
- <td colspan="4">
+ <td colspan="7">
<div class="table-nav">
- <a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
- page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
- class="next">Next
- page</a><a href="#" class="last">Last page</a>
+ <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">First page</button>
+ <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
+ <button data-ng-click="nextPage()" class="next" ng-disabled="users.length < query.max">Next page</button>
</div>
</td>
</tr>
- </tfoot>-->
+ </tfoot>
<tbody>
<tr ng-repeat="user in users">
<td><a href="#/realms/{{realm.realm}}/users/{{user.username}}">{{user.username}}</a></td>
integration/js/pom.xml 35(+26 -9)
diff --git a/integration/js/pom.xml b/integration/js/pom.xml
index a08f3df..d9958b5 100755
--- a/integration/js/pom.xml
+++ b/integration/js/pom.xml
@@ -17,15 +17,32 @@
</dependencies>
<build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>${maven.compiler.source}</source>
- <target>${maven.compiler.target}</target>
- </configuration>
- </plugin>
+ <plugins>
+ <plugin>
+ <groupId>com.samaxes.maven</groupId>
+ <artifactId>minify-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>min-js</id>
+ <phase>compile</phase>
+ <configuration>
+ <charset>utf-8</charset>
+ <webappSourceDir>${basedir}/src/main/resources</webappSourceDir>
+ <jsSourceDir>.</jsSourceDir>
+ <jsSourceFiles>
+ <jsSourceFile>keycloak.js</jsSourceFile>
+ </jsSourceFiles>
+
+ <webappTargetDir>${project.build.directory}/classes</webappTargetDir>
+ <jsTargetDir>.</jsTargetDir>
+ <jsFinalFile>keycloak.js</jsFinalFile>
+ </configuration>
+ <goals>
+ <goal>minify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
integration/js/src/main/resources/keycloak.js 53(+41 -12)
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index 617cbc4..d3881af 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -57,6 +57,17 @@
processCallback(callback, initPromise);
return;
} else if (initOptions) {
+ var doLogin = function(prompt) {
+ if (!prompt) {
+ options.prompt = 'none';
+ }
+ kc.login(options).success(function () {
+ initPromise.setSuccess();
+ }).error(function () {
+ initPromise.setError();
+ });
+ }
+
if (initOptions.token || initOptions.refreshToken) {
setToken(initOptions.token, initOptions.refreshToken);
initPromise.setSuccess();
@@ -64,16 +75,20 @@
var options = {};
switch (initOptions.onLoad) {
case 'check-sso':
- options.prompt = 'none';
- case 'login-required':
- var p = kc.login(options);
- if (p) {
- p.success(function() {
- initPromise.setSuccess();
- }).error(function() {
- initPromise.setError();
+ if (loginIframe.enable) {
+ setupCheckLoginIframe().success(function() {
+ checkLoginIframe().success(function () {
+ doLogin(false);
+ }).error(function () {
+ initPromise.setSuccess();
+ });
});
- };
+ } else {
+ doLogin(false);
+ }
+ break;
+ case 'login-required':
+ doLogin(true);
break;
default:
throw 'Invalid value for onLoad';
@@ -525,7 +540,14 @@
}
function setupCheckLoginIframe() {
- if (!loginIframe.enable || loginIframe.iframe) {
+ var promise = createPromise();
+
+ if (!loginIframe.enable) {
+ return;
+ }
+
+ if (loginIframe.iframe) {
+ promise.setSuccess();
return;
}
@@ -539,6 +561,7 @@
loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
}
loginIframe.iframe = iframe;
+ promise.setSuccess();
}
var src = getRealmUrl() + '/login-status-iframe.html?client_id=' + encodeURIComponent(kc.clientId) + '&origin=' + window.location.origin;
@@ -553,7 +576,8 @@
var data = event.data;
var promise = loginIframe.callbackMap[data.callbackId];
delete loginIframe.callbackMap[data.callbackId];
- if (kc.sessionId == data.session && data.loggedIn) {
+
+ if ((!kc.sessionId || kc.sessionId == data.session) && data.loggedIn) {
promise.setSuccess();
} else {
clearToken();
@@ -570,12 +594,14 @@
};
setTimeout(check, loginIframe.interval * 1000);
+
+ return promise.promise;
}
function checkLoginIframe() {
var promise = createPromise();
- if (loginIframe.iframe || loginIframe.iframeOrigin) {
+ if (loginIframe.iframe && loginIframe.iframeOrigin) {
var msg = {};
msg.callbackId = createCallbackId();
loginIframe.callbackMap[msg.callbackId] = promise;
@@ -593,14 +619,17 @@
return {
login: function(options) {
window.location.href = kc.createLoginUrl(options);
+ return createPromise().promise;
},
logout: function(options) {
window.location.href = kc.createLogoutUrl(options);
+ return createPromise().promise;
},
accountManagement : function() {
window.location.href = kc.createAccountUrl();
+ return createPromise().promise;
},
redirectUri: function(options) {
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
index 88ae3c2..7557127 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
@@ -99,12 +99,14 @@ public class KeycloakServletExtension implements ServletExtension {
InputStream is = getConfigInputStream(servletContext);
KeycloakDeployment deployment = null;
if (is == null) {
- throw new RuntimeException("Unable to find realm config in /WEB-INF/keycloak.json or in keycloak subsystem.");
+ log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+ deployment = new KeycloakDeployment();
} else {
deployment = KeycloakDeploymentBuilder.build(is);
}
AdapterDeploymentContext deploymentContext = new AdapterDeploymentContext(deployment);
+ servletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
final ServletKeycloakAuthMech mech = createAuthenticationMechanism(deploymentInfo, deploymentContext, userSessionManagement);
diff --git a/integration/wildfly-subsystem/pom.xml b/integration/wildfly-subsystem/pom.xml
index 5311ee2..ff4ebf7 100755
--- a/integration/wildfly-subsystem/pom.xml
+++ b/integration/wildfly-subsystem/pom.xml
@@ -65,18 +65,22 @@
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-controller</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-server</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ee</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-undertow</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
diff --git a/integration/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak.xml b/integration/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak.xml
new file mode 100644
index 0000000..9f3d0ed
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Template used by WildFly build when directed to include Keycloak subsystem in a configuration. -->
+<config>
+ <extension-module>org.keycloak.keycloak-wildfly-subsystem</extension-module>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.0"/>
+</config>
model/api/pom.xml 5(+5 -0)
diff --git a/model/api/pom.xml b/model/api/pom.xml
index f72d856..d0af4dc 100755
--- a/model/api/pom.xml
+++ b/model/api/pom.xml
@@ -31,6 +31,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java
index 321d014..33ee8e2 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -1,6 +1,5 @@
package org.keycloak.models;
-import java.util.List;
import java.util.Set;
/**
@@ -75,7 +74,4 @@ public interface ClientModel {
void setNotBefore(int notBefore);
- Set<UserSessionModel> getUserSessions();
-
- int getActiveUserSessions();
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java b/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java
old mode 100644
new mode 100755
index 82bc922..6255836
--- a/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java
@@ -9,6 +9,7 @@ public class CredentialEntity {
private String value;
private String device;
private byte[] salt;
+ private int hashIterations;
public String getType() {
return type;
@@ -41,4 +42,12 @@ public class CredentialEntity {
public void setSalt(byte[] salt) {
this.salt = salt;
}
+
+ public int getHashIterations() {
+ return hashIterations;
+ }
+
+ public void setHashIterations(int hashIterations) {
+ this.hashIterations = hashIterations;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/FederationProviderEntity.java b/model/api/src/main/java/org/keycloak/models/entities/FederationProviderEntity.java
new file mode 100755
index 0000000..0330acc
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/FederationProviderEntity.java
@@ -0,0 +1,37 @@
+package org.keycloak.models.entities;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class FederationProviderEntity {
+ protected String id;
+ protected String providerName;
+ private Map<String, String> config;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ public Map<String, String> getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index 3c841a5..061cfe4 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -51,6 +51,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private List<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
private List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
+ private List<FederationProviderEntity> federationProviders = new ArrayList<FederationProviderEntity>();
private Map<String, String> smtpConfig = new HashMap<String, String>();
private Map<String, String> socialConfig = new HashMap<String, String>();
@@ -381,4 +382,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
public void setAdminAppId(String adminAppId) {
this.adminAppId = adminAppId;
}
+
+ public List<FederationProviderEntity> getFederationProviders() {
+ return federationProviders;
+ }
+
+ public void setFederationProviders(List<FederationProviderEntity> federationProviders) {
+ this.federationProviders = federationProviders;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
old mode 100644
new mode 100755
index f1248b0..9aa0b0b
--- a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
@@ -1,24 +1,23 @@
package org.keycloak.models.entities;
+import org.keycloak.models.UserModel;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import org.keycloak.models.UserModel;
-
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserEntity extends AbstractIdentifiableEntity {
- private String loginName;
+ private String username;
private String firstName;
private String lastName;
private String email;
private boolean emailVerified;
private boolean totp;
private boolean enabled;
- private int notBefore;
private String realmId;
@@ -29,13 +28,14 @@ public class UserEntity extends AbstractIdentifiableEntity {
private List<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
private List<SocialLinkEntity> socialLinks;
private AuthenticationLinkEntity authenticationLink;
+ private String federationLink;
- public String getLoginName() {
- return loginName;
+ public String getUsername() {
+ return username;
}
- public void setLoginName(String loginName) {
- this.loginName = loginName;
+ public void setUsername(String username) {
+ this.username = username;
}
public String getFirstName() {
@@ -86,14 +86,6 @@ public class UserEntity extends AbstractIdentifiableEntity {
this.enabled = enabled;
}
- public int getNotBefore() {
- return notBefore;
- }
-
- public void setNotBefore(int notBefore) {
- this.notBefore = notBefore;
- }
-
public String getRealmId() {
return realmId;
}
@@ -149,5 +141,13 @@ public class UserEntity extends AbstractIdentifiableEntity {
public void setAuthenticationLink(AuthenticationLinkEntity authenticationLink) {
this.authenticationLink = authenticationLink;
}
+
+ public String getFederationLink() {
+ return federationLink;
+ }
+
+ public void setFederationLink(String federationLink) {
+ this.federationLink = federationLink;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
index ba2c776..506d211 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
@@ -2,8 +2,6 @@ package org.keycloak.models;
import org.keycloak.provider.Provider;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -11,9 +9,8 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public interface KeycloakSession {
- // Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
- KeycloakTransaction getTransaction();
+ KeycloakTransactionManager getTransaction();
<T extends Provider> T getProvider(Class<T> clazz);
@@ -23,45 +20,41 @@ public interface KeycloakSession {
<T extends Provider> Set<T> getAllProviders(Class<T> clazz);
- RealmModel createRealm(String name);
- RealmModel createRealm(String id, String name);
- RealmModel getRealm(String id);
- RealmModel getRealmByName(String name);
+ void enlistForClose(Provider provider);
- UserModel getUserById(String id, RealmModel realm);
- UserModel getUserByUsername(String username, RealmModel realm);
- UserModel getUserByEmail(String email, RealmModel realm);
- UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm);
- List<UserModel> getUsers(RealmModel realm);
- List<UserModel> searchForUser(String search, RealmModel realm);
- List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm);
+ KeycloakSessionFactory getKeycloakSessionFactory();
- Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm);
- SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm);
+ /**
+ * Returns a managed provider instance. Will start a provider transaction. This transaction is managed by the KeycloakSession
+ * transaction.
+ *
+ * @return
+ * @throws IllegalStateException if transaction is not active
+ */
+ RealmProvider realms();
+ /**
+ * Returns a managed provider instance. Will start a provider transaction. This transaction is managed by the KeycloakSession
+ * transaction.
+ *
+ * @return
+ * @throws IllegalStateException if transaction is not active
+ */
+ UserSessionProvider sessions();
- RoleModel getRoleById(String id, RealmModel realm);
- ApplicationModel getApplicationById(String id, RealmModel realm);
- OAuthClientModel getOAuthClientById(String id, RealmModel realm);
- List<RealmModel> getRealms();
- boolean removeRealm(String id);
- UsernameLoginFailureModel getUserLoginFailure(String username, RealmModel realm);
- UsernameLoginFailureModel addUserLoginFailure(String username, RealmModel realm);
- List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm);
-
- UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress);
- UserSessionModel getUserSession(String id, RealmModel realm);
- List<UserSessionModel> getUserSessions(UserModel user, RealmModel realm);
- Set<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client);
- int getActiveUserSessions(RealmModel realm, ClientModel client);
- void removeUserSession(UserSessionModel session);
- void removeUserSessions(RealmModel realm, UserModel user);
- void removeExpiredUserSessions(RealmModel realm);
- void removeUserSessions(RealmModel realm);
-
-
- void removeAllData();
void close();
+
+ /**
+ * Possibly both cached and federated view of users depending on configuration.
+ *
+ * @return
+ */
+ UserProvider users();
+
+ /**
+ * Keycloak user storage. Non-federated, but possibly cache (if it is on) view of users.
+ */
+ UserProvider userStorage();
}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java b/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java
index 582b7ea..af31c09 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java
@@ -1,10 +1,18 @@
package org.keycloak.models;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface KeycloakSessionFactory {
KeycloakSession create();
+
+ <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz);
+
+ <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id);
+
void close();
}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakTransaction.java b/model/api/src/main/java/org/keycloak/models/KeycloakTransaction.java
index 5db29f3..cf9103e 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakTransaction.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakTransaction.java
@@ -10,4 +10,5 @@ public interface KeycloakTransaction {
void rollback();
void setRollbackOnly();
boolean getRollbackOnly();
- boolean isActive();}
+ boolean isActive();
+}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakTransactionManager.java b/model/api/src/main/java/org/keycloak/models/KeycloakTransactionManager.java
new file mode 100755
index 0000000..abc64ea
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakTransactionManager.java
@@ -0,0 +1,12 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface KeycloakTransactionManager extends KeycloakTransaction {
+
+ void enlist(KeycloakTransaction transaction);
+ void enlistAfterCompletion(KeycloakTransaction transaction);
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
old mode 100644
new mode 100755
index 99636b9..bea471d
--- a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
+++ b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
@@ -52,11 +52,28 @@ public class PasswordPolicy {
list.add(new UpperCase(args));
} else if (name.equals(SpecialChars.NAME)) {
list.add(new SpecialChars(args));
+ } else if (name.equals(HashIterations.NAME)) {
+ list.add(new HashIterations(args));
}
}
return list;
}
+ /**
+ *
+ * @return -1 if no hash iterations setting
+ */
+ public int getHashIterations() {
+ if (policies == null) return -1;
+ for (Policy p : policies) {
+ if (p instanceof HashIterations) {
+ return ((HashIterations)p).iterations;
+ }
+
+ }
+ return -1;
+ }
+
public String validate(String password) {
for (Policy p : policies) {
String error = p.validate(password);
@@ -71,6 +88,20 @@ public class PasswordPolicy {
public String validate(String password);
}
+ private static class HashIterations implements Policy {
+ private static final String NAME = "hashIterations";
+ private int iterations;
+
+ public HashIterations(String[] args) {
+ iterations = intArg(NAME, 1, args);
+ }
+
+ @Override
+ public String validate(String password) {
+ return null;
+ }
+ }
+
private static class Length implements Policy {
private static final String NAME = "length";
private int min;
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 f669968..1884dec 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -106,22 +106,6 @@ public interface RealmModel extends RoleContainerModel {
void setPasswordPolicy(PasswordPolicy policy);
- boolean validatePassword(UserModel user, String password);
-
- boolean validateTOTP(UserModel user, String password, String token);
-
- UserModel getUser(String name);
-
- UserModel getUserByEmail(String email);
-
- UserModel getUserById(String name);
-
- UserModel addUser(String id, String username);
-
- UserModel addUser(String username);
-
- boolean removeUser(String name);
-
RoleModel getRoleById(String id);
List<String> getDefaultRoles();
@@ -147,16 +131,6 @@ public interface RealmModel extends RoleContainerModel {
void updateRequiredCredentials(Set<String> creds);
- UserModel getUserBySocialLink(SocialLinkModel socialLink);
-
- Set<SocialLinkModel> getSocialLinks(UserModel user);
-
- SocialLinkModel getSocialLink(UserModel user, String socialProvider);
-
- void addSocialLink(UserModel user, SocialLinkModel socialLink);
-
- boolean removeSocialLink(UserModel user, String socialProvider);
-
boolean isSocial();
void setSocial(boolean social);
@@ -165,16 +139,6 @@ public interface RealmModel extends RoleContainerModel {
void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin);
- UsernameLoginFailureModel getUserLoginFailure(String username);
- UsernameLoginFailureModel addUserLoginFailure(String username);
- List<UsernameLoginFailureModel> getAllUserLoginFailures();
-
- List<UserModel> getUsers();
-
- List<UserModel> searchForUser(String search);
-
- List<UserModel> searchForUserByAttributes(Map<String, String> attributes);
-
OAuthClientModel addOAuthClient(String name);
OAuthClientModel addOAuthClient(String id, String name);
@@ -201,6 +165,10 @@ public interface RealmModel extends RoleContainerModel {
void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders);
+ List<UserFederationProviderModel> getUserFederationProviders();
+
+ void setUserFederationProviders(List<UserFederationProviderModel> providers);
+
String getLoginTheme();
void setLoginTheme(String name);
@@ -245,19 +213,6 @@ public interface RealmModel extends RoleContainerModel {
void setMasterAdminApp(ApplicationModel app);
- UserSessionModel createUserSession(UserModel user, String ipAddress);
-
- UserSessionModel getUserSession(String id);
-
- List<UserSessionModel> getUserSessions(UserModel user);
-
- void removeUserSession(UserSessionModel session);
-
- void removeUserSessions(UserModel user);
-
- void removeExpiredUserSessions();
-
ClientModel findClientById(String id);
- void removeUserSessions();
}
diff --git a/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java b/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java
index 2cad371..f8d0000 100755
--- a/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java
@@ -1,6 +1,5 @@
package org.keycloak.models;
-import java.util.List;
import java.util.Set;
/**
diff --git a/model/api/src/main/java/org/keycloak/models/ScopeMapperModel.java b/model/api/src/main/java/org/keycloak/models/ScopeMapperModel.java
index 0bfd293..6515ab0 100755
--- a/model/api/src/main/java/org/keycloak/models/ScopeMapperModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ScopeMapperModel.java
@@ -1,8 +1,5 @@
package org.keycloak.models;
-import java.util.List;
-import java.util.Set;
-
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
diff --git a/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java b/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
index d096b97..4c5275b 100755
--- a/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
@@ -35,6 +35,13 @@ public class UserCredentialModel {
return model;
}
+ public static UserCredentialModel totp(String key) {
+ UserCredentialModel model = new UserCredentialModel();
+ model.setType(TOTP);
+ model.setValue(key);
+ return model;
+ }
+
public static UserCredentialModel generateSecret() {
UserCredentialModel model = new UserCredentialModel();
model.setType(SECRET);
diff --git a/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java b/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java
old mode 100644
new mode 100755
index 3702274..bd1213f
--- a/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java
@@ -11,6 +11,7 @@ public class UserCredentialValueModel {
private String value;
private String device;
private byte[] salt;
+ private int hashIterations;
public String getType() {
return type;
@@ -43,4 +44,12 @@ public class UserCredentialValueModel {
public void setSalt(byte[] salt) {
this.salt = salt;
}
+
+ public int getHashIterations() {
+ return hashIterations;
+ }
+
+ public void setHashIterations(int iterations) {
+ this.hashIterations = iterations;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
new file mode 100755
index 0000000..14be8cc
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -0,0 +1,358 @@
+package org.keycloak.models;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserFederationManager implements UserProvider {
+ protected KeycloakSession session;
+
+ public UserFederationManager(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles) {
+ UserModel user = session.userStorage().addUser(realm, id, username, addDefaultRoles);
+ for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
+ UserFederationProvider fed = session.getProvider(UserFederationProvider.class, federation.getProviderName());
+ if (fed.isRegistrationSupported()) {
+ return fed.register(realm, user);
+ }
+ }
+ return user;
+ }
+
+ protected UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
+ UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, model.getProviderName());
+ return factory.getInstance(session, model);
+
+ }
+
+ @Override
+ public UserModel addUser(RealmModel realm, String username) {
+ UserModel user = session.userStorage().addUser(realm, username);
+ for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
+ UserFederationProvider fed = getFederationProvider(federation);
+ if (fed.isRegistrationSupported()) {
+ return fed.register(realm, user);
+ }
+ }
+ return user;
+ }
+
+ protected UserFederationProvider getFederationLink(RealmModel realm, UserModel user) {
+ if (user.getFederationLink() == null) return null;
+ for (UserFederationProviderModel fed : realm.getUserFederationProviders()) {
+ if (fed.getId().equals(user.getFederationLink())) {
+ return getFederationProvider(fed);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean removeUser(RealmModel realm, UserModel user) {
+ UserFederationProvider link = getFederationLink(realm, user);
+ if (link != null) {
+ return link.removeUser(realm, user);
+ }
+ return session.userStorage().removeUser(realm, user);
+
+ }
+
+ protected void validateUser(RealmModel realm, UserModel user) {
+ UserFederationProvider link = getFederationLink(realm, user);
+ if (link != null && !link.isValid(user)) {
+ deleteInvalidUser(realm, user);
+ throw new IllegalStateException("Federated user no longer valid");
+ }
+
+ }
+
+ protected void deleteInvalidUser(RealmModel realm, UserModel user) {
+ KeycloakSession tx = session.getKeycloakSessionFactory().create();
+ try {
+ tx.getTransaction().begin();
+ RealmModel realmModel = tx.realms().getRealm(realm.getId());
+ UserModel deletedUser = tx.userStorage().getUserById(user.getId(), realmModel);
+ tx.userStorage().removeUser(realmModel, deletedUser);
+ tx.getTransaction().commit();
+ } finally {
+ tx.close();
+ }
+ }
+
+ protected boolean isValid(RealmModel realm, UserModel user) {
+ UserFederationProvider link = getFederationLink(realm, user);
+ if (link != null) return link.isValid(user);
+ return true;
+ }
+
+
+ protected UserModel validateAndProxyUser(RealmModel realm, UserModel user) {
+ UserFederationProvider link = getFederationLink(realm, user);
+ if (link != null) {
+ if (isValid(realm, user)) {
+ return link.proxy(user);
+ } else {
+ deleteInvalidUser(realm, user);
+ return null;
+ }
+ }
+ return user;
+ }
+
+ @Override
+ public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
+ validateUser(realm, user);
+ session.userStorage().addSocialLink(realm, user, socialLink);
+ }
+
+ @Override
+ public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
+ validateUser(realm, user);
+ if (user == null) throw new IllegalStateException("Federated user no longer valid");
+ return session.userStorage().removeSocialLink(realm, user, socialProvider);
+ }
+
+ @Override
+ public UserModel getUserById(String id, RealmModel realm) {
+ UserModel user = session.userStorage().getUserById(id, realm);
+ if (user != null) {
+ user = validateAndProxyUser(realm, user);
+ }
+ return user;
+ }
+
+ @Override
+ public UserModel getUserByUsername(String username, RealmModel realm) {
+ UserModel user = session.userStorage().getUserByUsername(username, realm);
+ if (user != null) {
+ user = validateAndProxyUser(realm, user);
+ if (user != null) return user;
+ }
+ for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
+ UserFederationProvider fed = getFederationProvider(federation);
+ user = fed.getUserByUsername(realm, username);
+ if (user != null) return user;
+ }
+ return user;
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email, RealmModel realm) {
+ UserModel user = session.userStorage().getUserByEmail(email, realm);
+ if (user != null) {
+ user = validateAndProxyUser(realm, user);
+ if (user != null) return user;
+ }
+ for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
+ UserFederationProvider fed = getFederationProvider(federation);
+ user = fed.getUserByEmail(realm, email);
+ if (user != null) return user;
+ }
+ return user;
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+ UserModel user = session.userStorage().getUserBySocialLink(socialLink, realm);
+ if (user != null) {
+ user = validateAndProxyUser(realm, user);
+ }
+ return user;
+ }
+
+ @Override
+ public List<UserModel> getUsers(RealmModel realm) {
+ return getUsers(realm, 0, Integer.MAX_VALUE);
+
+ }
+
+ @Override
+ public int getUsersCount(RealmModel realm) {
+ return session.userStorage().getUsersCount(realm);
+ }
+
+ interface PaginatedQuery {
+ List<UserModel> query(RealmModel realm, int first, int max);
+ }
+
+ protected List<UserModel> query(PaginatedQuery pagedQuery, RealmModel realm, int firstResult, int maxResults) {
+ List<UserModel> results = new LinkedList<UserModel>();
+ if (maxResults <= 0) return results;
+ int first = firstResult;
+ int max = maxResults;
+ do {
+ List<UserModel> query = pagedQuery.query(realm, first, max);
+ if (query == null || query.size() == 0) return results;
+ int added = 0;
+ for (UserModel user : query) {
+ user = validateAndProxyUser(realm, user);
+ if (user == null) continue;
+ results.add(user);
+ added++;
+ }
+ if (results.size() == maxResults) return results;
+ if (query.size() < max) return results;
+ first = query.size();
+ max -= added;
+ } while (true);
+ }
+
+ @Override
+ public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
+ return query(new PaginatedQuery() {
+ @Override
+ public List<UserModel> query(RealmModel realm, int first, int max) {
+ return session.userStorage().getUsers(realm, first, max);
+ }
+ }, realm, firstResult, maxResults);
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search, RealmModel realm) {
+ return searchForUser(search, realm, 0, Integer.MAX_VALUE);
+ }
+
+ void federationLoad(RealmModel realm, Map<String, String> attributes) {
+ for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
+ UserFederationProvider fed = getFederationProvider(federation);
+ fed.searchByAttributes(attributes, realm);
+ }
+ }
+
+ @Override
+ public List<UserModel> searchForUser(final String search, RealmModel realm, int firstResult, int maxResults) {
+ Map<String, String> attributes = new HashMap<String, String>();
+ int spaceIndex = search.lastIndexOf(' ');
+ if (spaceIndex > -1) {
+ String firstName = search.substring(0, spaceIndex).trim();
+ String lastName = search.substring(spaceIndex).trim();
+ attributes.put(UserModel.FIRST_NAME, firstName);
+ attributes.put(UserModel.LAST_NAME, lastName);
+ } else if (search.indexOf('@') > -1) {
+ attributes.put(UserModel.USERNAME, search.trim());
+ attributes.put(UserModel.EMAIL, search.trim());
+ } else {
+ attributes.put(UserModel.LAST_NAME, search.trim());
+ attributes.put(UserModel.USERNAME, search.trim());
+ }
+ federationLoad(realm, attributes);
+ return query(new PaginatedQuery() {
+ @Override
+ public List<UserModel> query(RealmModel realm, int first, int max) {
+ return session.userStorage().searchForUser(search, realm, first, max);
+ }
+ }, realm, firstResult, maxResults);
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+ return searchForUserByAttributes(attributes, realm, 0, Integer.MAX_VALUE);
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(final Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
+ federationLoad(realm, attributes);
+ return query(new PaginatedQuery() {
+ @Override
+ public List<UserModel> query(RealmModel realm, int first, int max) {
+ return session.userStorage().searchForUserByAttributes(attributes, realm, first, max);
+ }
+ }, realm, firstResult, maxResults);
+ }
+
+ @Override
+ public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+ validateUser(realm, user);
+ if (user == null) throw new IllegalStateException("Federated user no longer valid");
+ return session.userStorage().getSocialLinks(user, realm);
+ }
+
+ @Override
+ public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
+ validateUser(realm, user);
+ if (user == null) throw new IllegalStateException("Federated user no longer valid");
+ return session.userStorage().getSocialLink(user, socialProvider, realm);
+ }
+
+ @Override
+ public void preRemove(RealmModel realm) {
+ for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
+ UserFederationProvider fed = getFederationProvider(federation);
+ fed.preRemove(realm);
+ }
+ session.userStorage().preRemove(realm);
+ }
+
+ @Override
+ public void preRemove(RealmModel realm, RoleModel role) {
+ for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
+ UserFederationProvider fed = getFederationProvider(federation);
+ fed.preRemove(realm, role);
+ }
+ session.userStorage().preRemove(realm, role);
+ }
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+ UserFederationProvider link = getFederationLink(realm, user);
+ if (link != null) {
+ validateUser(realm, user);
+ if (link.getSupportedCredentialTypes().size() > 0) {
+ List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
+ List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
+ for (UserCredentialModel cred : input) {
+ if (fedCreds.contains(cred.getType())) {
+ fedCreds.add(cred);
+ } else {
+ localCreds.add(cred);
+ }
+ }
+ if (!link.validCredentials(realm, user, fedCreds)) {
+ return false;
+ }
+ return session.userStorage().validCredentials(realm, user, localCreds);
+ }
+ }
+ return session.userStorage().validCredentials(realm, user, input);
+ }
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
+ UserFederationProvider link = getFederationLink(realm, user);
+ if (link != null) {
+ validateUser(realm, user);
+ Set<String> supportedCredentialTypes = link.getSupportedCredentialTypes();
+ if (supportedCredentialTypes.size() > 0) {
+ List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
+ List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
+ for (UserCredentialModel cred : input) {
+ if (supportedCredentialTypes.contains(cred.getType())) {
+ fedCreds.add(cred);
+ } else {
+ localCreds.add(cred);
+ }
+ }
+ if (!link.validCredentials(realm, user, fedCreds)) {
+ return false;
+ }
+ return session.userStorage().validCredentials(realm, user, localCreds);
+ }
+ }
+ return session.userStorage().validCredentials(realm, user, input);
+ }
+
+ @Override
+ public void close() {
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
new file mode 100755
index 0000000..2ea322f
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
@@ -0,0 +1,61 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserFederationProvider extends Provider {
+
+ public static final String USERNAME = UserModel.USERNAME;
+ public static final String EMAIL = UserModel.EMAIL;
+ public static final String FIRST_NAME = UserModel.EMAIL;
+ public static final String LAST_NAME = UserModel.LAST_NAME;
+
+ UserModel proxy(UserModel local);
+ boolean isRegistrationSupported();
+ UserModel register(RealmModel realm, UserModel user);
+ boolean removeUser(RealmModel realm, UserModel user);
+
+ /**
+ * Required to import into local storage any user found.
+ *
+ * @param realm
+ * @param username
+ * @return
+ */
+ UserModel getUserByUsername(RealmModel realm, String username);
+
+ /**
+ * Required to import into local storage any user found.
+ *
+ * @param realm
+ * @param email
+ * @return
+ */
+ UserModel getUserByEmail(RealmModel realm, String email);
+
+ /**
+ * Required to import into local storage any user found. Must not import if user already exists in KeycloakSession.userStorage()!
+ * Currently only attributes USERNAME, EMAIL, FIRST_NAME and LAST_NAME will be used.
+ *
+ * @param attributes
+ * @param realm
+ * @return
+ */
+ List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm);
+
+ void preRemove(RealmModel realm);
+ void preRemove(RealmModel realm, RoleModel role);
+
+ boolean isValid(UserModel local);
+ Set<String> getSupportedCredentialTypes();
+ boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
+ boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
+ void close();}
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProviderFactory.java b/model/api/src/main/java/org/keycloak/models/UserFederationProviderFactory.java
new file mode 100755
index 0000000..b723f3d
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderFactory.java
@@ -0,0 +1,11 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserFederationProviderFactory extends ProviderFactory<UserFederationProvider> {
+ UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model);
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
new file mode 100755
index 0000000..35fd727
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
@@ -0,0 +1,45 @@
+package org.keycloak.models;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class UserFederationProviderModel {
+
+ private String id;
+ private String providerName;
+ private Map<String, String> config = new HashMap<String, String>();
+
+ public UserFederationProviderModel() {};
+
+ public UserFederationProviderModel(String id, String providerName, Map<String, String> config) {
+ this.id = id;
+ this.providerName = providerName;
+ if (config != null) {
+ this.config.putAll(config);
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ public Map<String, String> getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationSpi.java b/model/api/src/main/java/org/keycloak/models/UserFederationSpi.java
new file mode 100755
index 0000000..6a22337
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserFederationSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "userFederation";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return UserFederationProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return UserFederationProviderFactory.class;
+ }
+
+}
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 3a7bd0b..52f398e 100755
--- a/model/api/src/main/java/org/keycloak/models/UserModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserModel.java
@@ -9,16 +9,16 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public interface UserModel {
- public static final String LOGIN_NAME = "username";
+ public static final String USERNAME = "username";
public static final String LAST_NAME = "lastName";
public static final String FIRST_NAME = "firstName";
public static final String EMAIL = "email";
String getId();
- String getLoginName();
+ String getUsername();
- void setLoginName(String loginName);
+ void setUsername(String username);
boolean isEnabled();
@@ -58,9 +58,6 @@ public interface UserModel {
void setTotp(boolean totp);
- int getNotBefore();
- void setNotBefore(int notBefore);
-
void updateCredential(UserCredentialModel cred);
List<UserCredentialValueModel> getCredentialsDirectly();
@@ -80,6 +77,9 @@ public interface UserModel {
Set<RoleModel> getRoleMappings();
void deleteRoleMapping(RoleModel role);
+ String getFederationLink();
+ void setFederationLink(String link);
+
diff --git a/model/api/src/main/java/org/keycloak/models/UserProvider.java b/model/api/src/main/java/org/keycloak/models/UserProvider.java
new file mode 100755
index 0000000..0e063d1
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserProvider.java
@@ -0,0 +1,43 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserProvider extends Provider {
+ // Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
+
+ UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles);
+ UserModel addUser(RealmModel realm, String username);
+ boolean removeUser(RealmModel realm, UserModel user);
+
+ public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink);
+ public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider);
+
+ UserModel getUserById(String id, RealmModel realm);
+ UserModel getUserByUsername(String username, RealmModel realm);
+ UserModel getUserByEmail(String email, RealmModel realm);
+ UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm);
+ List<UserModel> getUsers(RealmModel realm);
+ int getUsersCount(RealmModel realm);
+ List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults);
+ List<UserModel> searchForUser(String search, RealmModel realm);
+ List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
+ List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm);
+ List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults);
+ Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm);
+ SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm);
+
+ void preRemove(RealmModel realm);
+ void preRemove(RealmModel realm, RoleModel role);
+
+ boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
+ boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
+ void close();
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserProviderFactory.java b/model/api/src/main/java/org/keycloak/models/UserProviderFactory.java
new file mode 100755
index 0000000..f052e39
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserProviderFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserProviderFactory extends ProviderFactory<UserProvider> {
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
index ada5899..e7ce35c 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
@@ -1,7 +1,6 @@
package org.keycloak.models;
import java.util.List;
-import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -16,10 +15,22 @@ public interface UserSessionModel {
void setUser(UserModel user);
+ String getLoginUsername();
+
+ void setLoginUsername(String loginUsername);
+
String getIpAddress();
void setIpAddress(String ipAddress);
+ String getAuthMethod();
+
+ void setAuthMethod(String authMethod);
+
+ boolean isRememberMe();
+
+ void setRememberMe(boolean rememberMe);
+
int getStarted();
void setStarted(int started);
@@ -33,4 +44,5 @@ public interface UserSessionModel {
List<ClientModel> getClientAssociations();
void removeAssociatedClient(ClientModel client);
+
}
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java b/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
new file mode 100755
index 0000000..d91061d
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -0,0 +1,34 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserSessionProvider extends Provider {
+
+ UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe);
+ UserSessionModel getUserSession(RealmModel realm, String id);
+ List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user);
+ List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client);
+ List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults);
+ int getActiveUserSessions(RealmModel realm, ClientModel client);
+ void removeUserSession(RealmModel realm, UserSessionModel session);
+ void removeUserSessions(RealmModel realm, UserModel user);
+ void removeExpiredUserSessions(RealmModel realm);
+ void removeUserSessions(RealmModel realm);
+
+ UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username);
+ UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username);
+ List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm);
+
+ void onRealmRemoved(RealmModel realm);
+ void onClientRemoved(RealmModel realm, ClientModel client);
+ void onUserRemoved(RealmModel realm, UserModel user);
+
+ void close();
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionProviderFactory.java b/model/api/src/main/java/org/keycloak/models/UserSessionProviderFactory.java
new file mode 100755
index 0000000..5e57609
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionProviderFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserSessionProviderFactory extends ProviderFactory<UserSessionProvider> {
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionSpi.java b/model/api/src/main/java/org/keycloak/models/UserSessionSpi.java
new file mode 100644
index 0000000..e470db5
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionSpi.java
@@ -0,0 +1,29 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionSpi implements Spi {
+
+ public static final String NAME = "userSessions";
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return UserSessionProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return UserSessionProviderFactory.class;
+ }
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/UserSpi.java b/model/api/src/main/java/org/keycloak/models/UserSpi.java
new file mode 100755
index 0000000..e68299c
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.models;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "user";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return UserProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return UserProviderFactory.class;
+ }
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
new file mode 100755
index 0000000..901571f
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
@@ -0,0 +1,130 @@
+package org.keycloak.models.utils;
+
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CredentialValidation {
+
+ private static int hashIterations(RealmModel realm) {
+ PasswordPolicy policy = realm.getPasswordPolicy();
+ if (policy != null) {
+ return policy.getHashIterations();
+ }
+ return -1;
+
+ }
+
+ /**
+ * Will update password if hash iteration policy has changed
+ *
+ * @param realm
+ * @param user
+ * @param password
+ * @return
+ */
+ public static boolean validPassword(RealmModel realm, UserModel user, String password) {
+ boolean validated = false;
+ UserCredentialValueModel passwordCred = null;
+ for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ validated = new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue(), cred.getHashIterations());
+ passwordCred = cred;
+ }
+ }
+ if (validated) {
+ int iterations = hashIterations(realm);
+ if (iterations > -1 && iterations != passwordCred.getHashIterations()) {
+ UserCredentialValueModel newCred = new UserCredentialValueModel();
+ newCred.setType(passwordCred.getType());
+ newCred.setDevice(passwordCred.getDevice());
+ newCred.setSalt(passwordCred.getSalt());
+ newCred.setHashIterations(iterations);
+ newCred.setValue(new Pbkdf2PasswordEncoder(newCred.getSalt()).encode(password, iterations));
+ user.updateCredentialDirectly(newCred);
+ }
+
+ }
+ return validated;
+
+ }
+
+ public static boolean validTOTP(RealmModel realm, UserModel user, String otp) {
+ UserCredentialValueModel passwordCred = null;
+ for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
+ if (cred.getType().equals(UserCredentialModel.TOTP)) {
+ if (new TimeBasedOTP().validate(otp, cred.getValue().getBytes())) {
+ return true;
+ }
+ }
+ }
+ return false;
+
+ }
+ public static boolean validSecret(RealmModel realm, UserModel user, String secret) {
+ for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
+ if (cred.getType().equals(UserCredentialModel.SECRET)) {
+ if (cred.getValue().equals(secret)) return true;
+ }
+ }
+ return false;
+
+ }
+
+ /**
+ * Must validate all credentials. FYI, password hashes may be rehashed and updated based on realm hash password policies.
+ *
+ * @param realm
+ * @param user
+ * @param credentials
+ * @return
+ */
+ public static boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> credentials) {
+ for (UserCredentialModel credential : credentials) {
+ if (!validCredential(realm, user, credential)) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Must validate all credentials. FYI, password hashes may be rehashed and updated based on realm hash password policies.
+ *
+ * @param realm
+ * @param user
+ * @param credentials
+ * @return
+ */
+ public static boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... credentials) {
+ for (UserCredentialModel credential : credentials) {
+ if (!validCredential(realm, user, credential)) return false;
+ }
+ return true;
+ }
+
+ private static boolean validCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
+ if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
+ if (!validPassword(realm, user, credential.getValue())) {
+ return false;
+ }
+ } else if (credential.getType().equals(UserCredentialModel.TOTP)) {
+ if (!validTOTP(realm, user, credential.getValue())) {
+ return false;
+ }
+ } else if (credential.getType().equals(UserCredentialModel.SECRET)) {
+ if (!validSecret(realm, user, credential.getValue())) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
old mode 100644
new mode 100755
index 939eeba..4a00ff9
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -1,19 +1,25 @@
package org.keycloak.models.utils;
+import org.bouncycastle.openssl.PEMWriter;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.util.PemUtils;
+
import java.io.IOException;
import java.io.StringWriter;
import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.bouncycastle.openssl.PEMWriter;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.util.PemUtils;
/**
* Set of helper methods, which are useful in various model implementations.
@@ -65,6 +71,30 @@ public final class KeycloakModelUtils {
return PemUtils.removeBeginEnd(s);
}
+ public static void generateRealmKeys(RealmModel realm) {
+ KeyPair keyPair = null;
+ try {
+ keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ realm.setPrivateKey(keyPair.getPrivate());
+ realm.setPublicKey(keyPair.getPublic());
+ }
+
+ public static UserCredentialModel generateSecret(ClientModel app) {
+ UserCredentialModel secret = UserCredentialModel.generateSecret();
+ app.setSecret(secret.getValue());
+ return secret;
+ }
+
+ public static ApplicationModel createApplication(RealmModel realm, String name) {
+ ApplicationModel app = realm.addApplication(name);
+ generateSecret(app);
+
+ return app;
+ }
+
/**
* Deep search if given role is descendant of composite role
*
@@ -92,10 +122,10 @@ public final class KeycloakModelUtils {
* @param username username or email of user
* @return found user
*/
- public static UserModel findUserByNameOrEmail(RealmModel realm, String username) {
- UserModel user = realm.getUser(username);
+ public static UserModel findUserByNameOrEmail(KeycloakSession session, RealmModel realm, String username) {
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null && username.contains("@")) {
- user = realm.getUserByEmail(username);
+ user = session.users().getUserByEmail(username, realm);
}
return user;
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/Pbkdf2PasswordEncoder.java b/model/api/src/main/java/org/keycloak/models/utils/Pbkdf2PasswordEncoder.java
index 33c7c41..32f0d15 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/Pbkdf2PasswordEncoder.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/Pbkdf2PasswordEncoder.java
@@ -43,7 +43,7 @@ public class Pbkdf2PasswordEncoder {
* @param rawPassword The password used as a master key to derive into a session key
* @return encoded password in Base64
*/
- public String encode(String rawPassword) {
+ public String encode(String rawPassword, int iterations) {
String encodedPassword;
@@ -59,6 +59,10 @@ public class Pbkdf2PasswordEncoder {
return encodedPassword;
}
+ public String encode(String rawPassword) {
+ return encode(rawPassword, iterations);
+ }
+
/**
* Encode the password provided and compare with the hash stored into the database
* @param rawPassword The password provided
@@ -70,6 +74,16 @@ public class Pbkdf2PasswordEncoder {
}
/**
+ * Encode the password provided and compare with the hash stored into the database
+ * @param rawPassword The password provided
+ * @param encodedPassword Encoded hash stored into the database
+ * @return true if the password is valid, otherwise false for invalid credentials
+ */
+ public boolean verify(String rawPassword, String encodedPassword, int iterations) {
+ return encode(rawPassword, iterations).equals(encodedPassword);
+ }
+
+ /**
* Generate a salt for each password
* @return cryptographically strong random number
*/
diff --git a/model/api/src/main/java/org/keycloak/models/utils/reflection/Properties.java b/model/api/src/main/java/org/keycloak/models/utils/reflection/Properties.java
old mode 100644
new mode 100755
index 3348d8b..fd7c06b
--- a/model/api/src/main/java/org/keycloak/models/utils/reflection/Properties.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/reflection/Properties.java
@@ -1,7 +1,5 @@
package org.keycloak.models.utils.reflection;
-import java.lang.reflect.Field;
-import java.lang.reflect.Member;
import java.lang.reflect.Method;
/**
diff --git a/model/api/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java b/model/api/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java
old mode 100644
new mode 100755
index 2d2c41d..604b83b
--- a/model/api/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java
@@ -1,6 +1,5 @@
package org.keycloak.models.utils.reflection;
-import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
new file mode 100755
index 0000000..6b3ec2d
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -0,0 +1,702 @@
+package org.keycloak.models.utils;
+
+import net.iharder.Base64;
+import org.jboss.logging.Logger;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.ClaimMask;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.UserFederationProviderRepresentation;
+import org.keycloak.representations.idm.ApplicationRepresentation;
+import org.keycloak.representations.idm.AuthenticationLinkRepresentation;
+import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
+import org.keycloak.representations.idm.ClaimRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.OAuthClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.ScopeMappingRepresentation;
+import org.keycloak.representations.idm.SocialLinkRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RepresentationToModel {
+
+ private static Logger logger = Logger.getLogger(RepresentationToModel.class);
+
+ public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm) {
+ newRealm.setName(rep.getRealm());
+ if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled());
+ if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial());
+ if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected());
+ if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
+ if (rep.getMinimumQuickLoginWaitSeconds() != null) newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
+ if (rep.getWaitIncrementSeconds() != null) newRealm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds());
+ if (rep.getQuickLoginCheckMilliSeconds() != null) newRealm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds());
+ if (rep.getMaxDeltaTimeSeconds() != null) newRealm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds());
+ if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor());
+
+ if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore());
+
+ if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
+ else newRealm.setAccessTokenLifespan(300);
+
+ if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
+ else newRealm.setSsoSessionIdleTimeout(600);
+ if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
+ else newRealm.setSsoSessionMaxLifespan(36000);
+
+ if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
+ else newRealm.setAccessCodeLifespan(60);
+
+ if (rep.getAccessCodeLifespanUserAction() != null)
+ newRealm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
+ else newRealm.setAccessCodeLifespanUserAction(300);
+
+ if (rep.isSslNotRequired() != null) newRealm.setSslNotRequired(rep.isSslNotRequired());
+ if (rep.isPasswordCredentialGrantAllowed() != null) newRealm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed());
+ 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)
+ newRealm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin());
+ if (rep.getPrivateKey() == null || rep.getPublicKey() == null) {
+ KeycloakModelUtils.generateRealmKeys(newRealm);
+ } else {
+ newRealm.setPrivateKeyPem(rep.getPrivateKey());
+ newRealm.setPublicKeyPem(rep.getPublicKey());
+ }
+ if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme());
+ if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme());
+ if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme());
+ if (rep.getEmailTheme() != null) newRealm.setEmailTheme(rep.getEmailTheme());
+
+ if (rep.getRequiredCredentials() != null) {
+ for (String requiredCred : rep.getRequiredCredentials()) {
+ addRequiredCredential(newRealm, requiredCred);
+ }
+ } else {
+ addRequiredCredential(newRealm, CredentialRepresentation.PASSWORD);
+ }
+
+ if (rep.getPasswordPolicy() != null) newRealm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
+
+ if (rep.getApplications() != null) {
+ Map<String, ApplicationModel> appMap = createApplications(rep, newRealm);
+ }
+
+ if (rep.getRoles() != null) {
+ if (rep.getRoles().getRealm() != null) { // realm roles
+ for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
+ createRole(newRealm, roleRep);
+ }
+ }
+ if (rep.getRoles().getApplication() != null) {
+ for (Map.Entry<String, List<RoleRepresentation>> entry : rep.getRoles().getApplication().entrySet()) {
+ ApplicationModel app = newRealm.getApplicationByName(entry.getKey());
+ if (app == null) {
+ throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey());
+ }
+ for (RoleRepresentation roleRep : entry.getValue()) {
+ // Application role may already exists (for example if it is defaultRole)
+ RoleModel role = roleRep.getId()!=null ? app.addRole(roleRep.getId(), roleRep.getName()) : app.addRole(roleRep.getName());
+ role.setDescription(roleRep.getDescription());
+ }
+ }
+ }
+ // now that all roles are created, re-iterate and set up composites
+ if (rep.getRoles().getRealm() != null) { // realm roles
+ for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
+ RoleModel role = newRealm.getRole(roleRep.getName());
+ addComposites(role, roleRep, newRealm);
+ }
+ }
+ if (rep.getRoles().getApplication() != null) {
+ for (Map.Entry<String, List<RoleRepresentation>> entry : rep.getRoles().getApplication().entrySet()) {
+ ApplicationModel app = newRealm.getApplicationByName(entry.getKey());
+ if (app == null) {
+ throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey());
+ }
+ for (RoleRepresentation roleRep : entry.getValue()) {
+ RoleModel role = app.getRole(roleRep.getName());
+ addComposites(role, roleRep, newRealm);
+ }
+ }
+ }
+ }
+
+ // Setup realm default roles
+ if (rep.getDefaultRoles() != null) {
+ for (String roleString : rep.getDefaultRoles()) {
+ newRealm.addDefaultRole(roleString.trim());
+ }
+ }
+ // Setup application default roles
+ if (rep.getApplications() != null) {
+ for (ApplicationRepresentation resourceRep : rep.getApplications()) {
+ if (resourceRep.getDefaultRoles() != null) {
+ ApplicationModel appModel = newRealm.getApplicationByName(resourceRep.getName());
+ appModel.updateDefaultRoles(resourceRep.getDefaultRoles());
+ }
+ }
+ }
+
+ if (rep.getOauthClients() != null) {
+ createOAuthClients(rep, newRealm);
+ }
+
+
+ // Now that all possible roles and applications are created, create scope mappings
+
+ Map<String, ApplicationModel> appMap = newRealm.getApplicationNameMap();
+
+ if (rep.getApplicationScopeMappings() != null) {
+
+ for (Map.Entry<String, List<ScopeMappingRepresentation>> entry : rep.getApplicationScopeMappings().entrySet()) {
+ ApplicationModel app = appMap.get(entry.getKey());
+ if (app == null) {
+ throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey());
+ }
+ createApplicationScopeMappings(newRealm, app, entry.getValue());
+ }
+ }
+
+ if (rep.getScopeMappings() != null) {
+ for (ScopeMappingRepresentation scope : rep.getScopeMappings()) {
+ ClientModel client = newRealm.findClient(scope.getClient());
+ for (String roleString : scope.getRoles()) {
+ RoleModel role = newRealm.getRole(roleString.trim());
+ if (role == null) {
+ role = newRealm.addRole(roleString.trim());
+ }
+ client.addScopeMapping(role);
+ }
+
+ }
+ }
+
+ if (rep.getSmtpServer() != null) {
+ newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
+ }
+
+ if (rep.getSocialProviders() != null) {
+ newRealm.setSocialConfig(new HashMap(rep.getSocialProviders()));
+ }
+ if (rep.getLdapServer() != null) {
+ newRealm.setLdapServerConfig(new HashMap(rep.getLdapServer()));
+ }
+
+ if (rep.getAuthenticationProviders() != null) {
+ List<AuthenticationProviderModel> authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders());
+ newRealm.setAuthenticationProviders(authProviderModels);
+ } else {
+ List<AuthenticationProviderModel> authProviderModels = Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER);
+ newRealm.setAuthenticationProviders(authProviderModels);
+ }
+
+ if (rep.getUserFederationProviders() != null) {
+ List<UserFederationProviderModel> providerModels = convertFederationProviders(rep.getUserFederationProviders());
+ newRealm.setUserFederationProviders(providerModels);
+ }
+
+ // create users and their role mappings and social mappings
+
+ if (rep.getUsers() != null) {
+ for (UserRepresentation userRep : rep.getUsers()) {
+ UserModel user = createUser(session, newRealm, userRep, appMap);
+ }
+ }
+ }
+
+ public static void updateRealm(RealmRepresentation rep, RealmModel realm) {
+ if (rep.getRealm() != null) {
+ realm.setName(rep.getRealm());
+ }
+ if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled());
+ if (rep.isSocial() != null) realm.setSocial(rep.isSocial());
+ if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected());
+ if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
+ if (rep.getMinimumQuickLoginWaitSeconds() != null) realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
+ if (rep.getWaitIncrementSeconds() != null) realm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds());
+ if (rep.getQuickLoginCheckMilliSeconds() != null) realm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds());
+ if (rep.getMaxDeltaTimeSeconds() != null) realm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds());
+ if (rep.getFailureFactor() != null) realm.setFailureFactor(rep.getFailureFactor());
+ if (rep.isPasswordCredentialGrantAllowed() != null) realm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed());
+ 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)
+ realm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin());
+ if (rep.isSslNotRequired() != null) realm.setSslNotRequired((rep.isSslNotRequired()));
+ if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
+ if (rep.getAccessCodeLifespanUserAction() != null)
+ realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
+ if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore());
+ if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
+ if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
+ if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
+ if (rep.getRequiredCredentials() != null) {
+ realm.updateRequiredCredentials(rep.getRequiredCredentials());
+ }
+ if (rep.getLoginTheme() != null) realm.setLoginTheme(rep.getLoginTheme());
+ if (rep.getAccountTheme() != null) realm.setAccountTheme(rep.getAccountTheme());
+ if (rep.getAdminTheme() != null) realm.setAdminTheme(rep.getAdminTheme());
+ if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme());
+
+ if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
+
+ if (rep.getDefaultRoles() != null) {
+ realm.updateDefaultRoles(rep.getDefaultRoles().toArray(new String[rep.getDefaultRoles().size()]));
+ }
+
+ if (rep.getSmtpServer() != null) {
+ realm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
+ }
+
+ if (rep.getSocialProviders() != null) {
+ realm.setSocialConfig(new HashMap(rep.getSocialProviders()));
+ }
+
+ if (rep.getLdapServer() != null) {
+ realm.setLdapServerConfig(new HashMap(rep.getLdapServer()));
+ }
+ if (rep.getAuthenticationProviders() != null) {
+ List<AuthenticationProviderModel> authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders());
+ realm.setAuthenticationProviders(authProviderModels);
+ }
+
+ if (rep.getUserFederationProviders() != null) {
+ List<UserFederationProviderModel> providerModels = convertFederationProviders(rep.getUserFederationProviders());
+ realm.setUserFederationProviders(providerModels);
+ }
+
+ if ("GENERATE".equals(rep.getPublicKey())) {
+ KeycloakModelUtils.generateRealmKeys(realm);
+ }
+ }
+
+ // Basic realm stuff
+
+ public static void addRequiredCredential(RealmModel newRealm, String requiredCred) {
+ newRealm.addRequiredCredential(requiredCred);
+ }
+
+
+ private static List<AuthenticationProviderModel> convertAuthenticationProviders(List<AuthenticationProviderRepresentation> authenticationProviders) {
+ List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
+
+ for (AuthenticationProviderRepresentation representation : authenticationProviders) {
+ AuthenticationProviderModel model = new AuthenticationProviderModel(representation.getProviderName(),
+ representation.isPasswordUpdateSupported(), representation.getConfig());
+ result.add(model);
+ }
+ return result;
+ }
+
+ private static List<UserFederationProviderModel> convertFederationProviders(List<UserFederationProviderRepresentation> providers) {
+ List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
+
+ for (UserFederationProviderRepresentation representation : providers) {
+ UserFederationProviderModel model = new UserFederationProviderModel(representation.getId(), representation.getProviderName(),
+ representation.getConfig());
+ result.add(model);
+ }
+ return result;
+ }
+
+ // Roles
+
+ public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
+ RoleModel role = roleRep.getId()!=null ? newRealm.addRole(roleRep.getId(), roleRep.getName()) : newRealm.addRole(roleRep.getName());
+ if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
+ }
+
+ private static void addComposites(RoleModel role, RoleRepresentation roleRep, RealmModel realm) {
+ if (roleRep.getComposites() == null) return;
+ if (roleRep.getComposites().getRealm() != null) {
+ for (String roleStr : roleRep.getComposites().getRealm()) {
+ RoleModel realmRole = realm.getRole(roleStr);
+ if (realmRole == null) throw new RuntimeException("Unable to find composite realm role: " + roleStr);
+ role.addCompositeRole(realmRole);
+ }
+ }
+ if (roleRep.getComposites().getApplication() != null) {
+ for (Map.Entry<String, List<String>> entry : roleRep.getComposites().getApplication().entrySet()) {
+ ApplicationModel app = realm.getApplicationByName(entry.getKey());
+ if (app == null) {
+ throw new RuntimeException("App doesn't exist in role definitions: " + roleRep.getName());
+ }
+ for (String roleStr : entry.getValue()) {
+ RoleModel appRole = app.getRole(roleStr);
+ if (appRole == null) throw new RuntimeException("Unable to find composite app role: " + roleStr);
+ role.addCompositeRole(appRole);
+ }
+
+ }
+
+ }
+
+ }
+
+ // APPLICATIONS
+
+ private static Map<String, ApplicationModel> createApplications(RealmRepresentation rep, RealmModel realm) {
+ Map<String, ApplicationModel> appMap = new HashMap<String, ApplicationModel>();
+ for (ApplicationRepresentation resourceRep : rep.getApplications()) {
+ ApplicationModel app = createApplication(realm, resourceRep, false);
+ appMap.put(app.getName(), app);
+ }
+ return appMap;
+ }
+
+ /**
+ * Does not create scope or role mappings!
+ *
+ * @param realm
+ * @param resourceRep
+ * @return
+ */
+ public static ApplicationModel createApplication(RealmModel realm, ApplicationRepresentation resourceRep, boolean addDefaultRoles) {
+ logger.debug("************ CREATE APPLICATION: {0}" + resourceRep.getName());
+ ApplicationModel applicationModel = resourceRep.getId()!=null ? realm.addApplication(resourceRep.getId(), resourceRep.getName()) : realm.addApplication(resourceRep.getName());
+ if (resourceRep.isEnabled() != null) applicationModel.setEnabled(resourceRep.isEnabled());
+ applicationModel.setManagementUrl(resourceRep.getAdminUrl());
+ if (resourceRep.isSurrogateAuthRequired() != null)
+ applicationModel.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
+ applicationModel.setBaseUrl(resourceRep.getBaseUrl());
+ if (resourceRep.isBearerOnly() != null) applicationModel.setBearerOnly(resourceRep.isBearerOnly());
+ if (resourceRep.isPublicClient() != null) applicationModel.setPublicClient(resourceRep.isPublicClient());
+ applicationModel.updateApplication();
+
+ if (resourceRep.getNotBefore() != null) {
+ applicationModel.setNotBefore(resourceRep.getNotBefore());
+ }
+
+ applicationModel.setSecret(resourceRep.getSecret());
+ if (applicationModel.getSecret() == null) {
+ KeycloakModelUtils.generateSecret(applicationModel);
+ }
+
+
+ if (resourceRep.getRedirectUris() != null) {
+ for (String redirectUri : resourceRep.getRedirectUris()) {
+ applicationModel.addRedirectUri(redirectUri);
+ }
+ }
+ if (resourceRep.getWebOrigins() != null) {
+ for (String webOrigin : resourceRep.getWebOrigins()) {
+ logger.debugv("Application: {0} webOrigin: {1}", resourceRep.getName(), webOrigin);
+ applicationModel.addWebOrigin(webOrigin);
+ }
+ } else {
+ // add origins from redirect uris
+ if (resourceRep.getRedirectUris() != null) {
+ Set<String> origins = new HashSet<String>();
+ for (String redirectUri : resourceRep.getRedirectUris()) {
+ logger.info("add redirectUri to origin: " + redirectUri);
+ if (redirectUri.startsWith("http:")) {
+ URI uri = URI.create(redirectUri);
+ String origin = uri.getScheme() + "://" + uri.getHost();
+ if (uri.getPort() != -1) {
+ origin += ":" + uri.getPort();
+ }
+ logger.debugv("adding default application origin: {0}" , origin);
+ origins.add(origin);
+ }
+ }
+ if (origins.size() > 0) {
+ applicationModel.setWebOrigins(origins);
+ }
+ }
+ }
+
+ if (addDefaultRoles && resourceRep.getDefaultRoles() != null) {
+ applicationModel.updateDefaultRoles(resourceRep.getDefaultRoles());
+ }
+
+ if (resourceRep.getClaims() != null) {
+ setClaims(applicationModel, resourceRep.getClaims());
+ } else {
+ applicationModel.setAllowedClaimsMask(ClaimMask.USERNAME);
+ }
+
+ return applicationModel;
+ }
+
+ public static void updateApplication(ApplicationRepresentation rep, ApplicationModel resource) {
+ if (rep.getName() != null) resource.setName(rep.getName());
+ if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled());
+ if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly());
+ if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
+ if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl());
+ if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl());
+ if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
+ resource.updateApplication();
+
+ if (rep.getNotBefore() != null) {
+ resource.setNotBefore(rep.getNotBefore());
+ }
+ if (rep.getDefaultRoles() != null) {
+ resource.updateDefaultRoles(rep.getDefaultRoles());
+ }
+
+ List<String> redirectUris = rep.getRedirectUris();
+ if (redirectUris != null) {
+ resource.setRedirectUris(new HashSet<String>(redirectUris));
+ }
+
+ List<String> webOrigins = rep.getWebOrigins();
+ if (webOrigins != null) {
+ resource.setWebOrigins(new HashSet<String>(webOrigins));
+ }
+
+ if (rep.getClaims() != null) {
+ setClaims(resource, rep.getClaims());
+ }
+ }
+
+ public static void setClaims(ClientModel model, ClaimRepresentation rep) {
+ long mask = model.getAllowedClaimsMask();
+ if (rep.getAddress()) {
+ mask |= ClaimMask.ADDRESS;
+ } else {
+ mask &= ~ClaimMask.ADDRESS;
+ }
+ if (rep.getEmail()) {
+ mask |= ClaimMask.EMAIL;
+ } else {
+ mask &= ~ClaimMask.EMAIL;
+ }
+ if (rep.getGender()) {
+ mask |= ClaimMask.GENDER;
+ } else {
+ mask &= ~ClaimMask.GENDER;
+ }
+ if (rep.getLocale()) {
+ mask |= ClaimMask.LOCALE;
+ } else {
+ mask &= ~ClaimMask.LOCALE;
+ }
+ if (rep.getName()) {
+ mask |= ClaimMask.NAME;
+ } else {
+ mask &= ~ClaimMask.NAME;
+ }
+ if (rep.getPhone()) {
+ mask |= ClaimMask.PHONE;
+ } else {
+ mask &= ~ClaimMask.PHONE;
+ }
+ if (rep.getPicture()) {
+ mask |= ClaimMask.PICTURE;
+ } else {
+ mask &= ~ClaimMask.PICTURE;
+ }
+ if (rep.getProfile()) {
+ mask |= ClaimMask.PROFILE;
+ } else {
+ mask &= ~ClaimMask.PROFILE;
+ }
+ if (rep.getUsername()) {
+ mask |= ClaimMask.USERNAME;
+ } else {
+ mask &= ~ClaimMask.USERNAME;
+ }
+ if (rep.getWebsite()) {
+ mask |= ClaimMask.WEBSITE;
+ } else {
+ mask &= ~ClaimMask.WEBSITE;
+ }
+ model.setAllowedClaimsMask(mask);
+ }
+
+ // OAuth clients
+
+ private static void createOAuthClients(RealmRepresentation realmRep, RealmModel realm) {
+ for (OAuthClientRepresentation rep : realmRep.getOauthClients()) {
+ createOAuthClient(rep, realm);
+ }
+ }
+
+ public static OAuthClientModel createOAuthClient(String id, String name, RealmModel realm) {
+ OAuthClientModel model = id!=null ? realm.addOAuthClient(id, name) : realm.addOAuthClient(name);
+ KeycloakModelUtils.generateSecret(model);
+ return model;
+ }
+
+ public static OAuthClientModel createOAuthClient(OAuthClientRepresentation rep, RealmModel realm) {
+ OAuthClientModel model = createOAuthClient(rep.getId(), rep.getName(), realm);
+ updateOAuthClient(rep, model);
+ return model;
+ }
+
+ public static void updateOAuthClient(OAuthClientRepresentation rep, OAuthClientModel model) {
+ if (rep.getName() != null) model.setClientId(rep.getName());
+ if (rep.isEnabled() != null) model.setEnabled(rep.isEnabled());
+ if (rep.isPublicClient() != null) model.setPublicClient(rep.isPublicClient());
+ if (rep.isDirectGrantsOnly() != null) model.setDirectGrantsOnly(rep.isDirectGrantsOnly());
+ if (rep.getClaims() != null) {
+ setClaims(model, rep.getClaims());
+ }
+ if (rep.getNotBefore() != null) {
+ model.setNotBefore(rep.getNotBefore());
+ }
+ if (rep.getSecret() != null) model.setSecret(rep.getSecret());
+ List<String> redirectUris = rep.getRedirectUris();
+ if (redirectUris != null) {
+ model.setRedirectUris(new HashSet<String>(redirectUris));
+ }
+
+ List<String> webOrigins = rep.getWebOrigins();
+ if (webOrigins != null) {
+ model.setWebOrigins(new HashSet<String>(webOrigins));
+ }
+
+ if (rep.getClaims() != null) {
+ setClaims(model, rep.getClaims());
+ }
+
+ if (rep.getNotBefore() != null) {
+ model.setNotBefore(rep.getNotBefore());
+ }
+
+ }
+
+ // Scope mappings
+
+ public static void createApplicationScopeMappings(RealmModel realm, ApplicationModel applicationModel, List<ScopeMappingRepresentation> mappings) {
+ for (ScopeMappingRepresentation mapping : mappings) {
+ ClientModel client = realm.findClient(mapping.getClient());
+ for (String roleString : mapping.getRoles()) {
+ RoleModel role = applicationModel.getRole(roleString.trim());
+ if (role == null) {
+ role = applicationModel.addRole(roleString.trim());
+ }
+ client.addScopeMapping(role);
+ }
+ }
+ }
+
+ // Users
+
+ public static UserModel createUser(KeycloakSession session, RealmModel newRealm, UserRepresentation userRep, Map<String, ApplicationModel> appMap) {
+ UserModel user = session.users().addUser(newRealm, userRep.getId(), userRep.getUsername(), false);
+ user.setEnabled(userRep.isEnabled());
+ user.setEmail(userRep.getEmail());
+ user.setFirstName(userRep.getFirstName());
+ user.setLastName(userRep.getLastName());
+ user.setFederationLink(userRep.getFederationLink());
+ if (userRep.getAttributes() != null) {
+ for (Map.Entry<String, String> entry : userRep.getAttributes().entrySet()) {
+ user.setAttribute(entry.getKey(), entry.getValue());
+ }
+ }
+ if (userRep.getRequiredActions() != null) {
+ for (String requiredAction : userRep.getRequiredActions()) {
+ user.addRequiredAction(UserModel.RequiredAction.valueOf(requiredAction));
+ }
+ }
+ if (userRep.getCredentials() != null) {
+ for (CredentialRepresentation cred : userRep.getCredentials()) {
+ updateCredential(user, cred);
+ }
+ }
+ if (userRep.getAuthenticationLink() != null) {
+ AuthenticationLinkRepresentation link = userRep.getAuthenticationLink();
+ AuthenticationLinkModel authLink = new AuthenticationLinkModel(link.getAuthProvider(), link.getAuthUserId());
+ user.setAuthenticationLink(authLink);
+ }
+ if (userRep.getSocialLinks() != null) {
+ for (SocialLinkRepresentation socialLink : userRep.getSocialLinks()) {
+ SocialLinkModel mappingModel = new SocialLinkModel(socialLink.getSocialProvider(), socialLink.getSocialUserId(), socialLink.getSocialUsername());
+ session.users().addSocialLink(newRealm, user, mappingModel);
+ }
+ }
+ if (userRep.getRealmRoles() != null) {
+ for (String roleString : userRep.getRealmRoles()) {
+ RoleModel role = newRealm.getRole(roleString.trim());
+ if (role == null) {
+ role = newRealm.addRole(roleString.trim());
+ }
+ user.grantRole(role);
+ }
+ }
+ if (userRep.getApplicationRoles() != null) {
+ for (Map.Entry<String, List<String>> entry : userRep.getApplicationRoles().entrySet()) {
+ ApplicationModel app = appMap.get(entry.getKey());
+ if (app == null) {
+ throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey());
+ }
+ createApplicationRoleMappings(app, user, entry.getValue());
+ }
+ }
+ return user;
+ }
+
+ // Detect if it is "plain-text" or "hashed" representation and update model according to it
+ private static void updateCredential(UserModel user, CredentialRepresentation cred) {
+ if (cred.getValue() != null) {
+ UserCredentialModel plainTextCred = convertCredential(cred);
+ user.updateCredential(plainTextCred);
+ } else {
+ UserCredentialValueModel hashedCred = new UserCredentialValueModel();
+ hashedCred.setType(cred.getType());
+ hashedCred.setDevice(cred.getDevice());
+ hashedCred.setHashIterations(cred.getHashIterations());
+ try {
+ hashedCred.setSalt(Base64.decode(cred.getSalt()));
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ hashedCred.setValue(cred.getHashedSaltedValue());
+ user.updateCredentialDirectly(hashedCred);
+ }
+ }
+
+ public static UserCredentialModel convertCredential(CredentialRepresentation cred) {
+ UserCredentialModel credential = new UserCredentialModel();
+ credential.setType(cred.getType());
+ credential.setValue(cred.getValue());
+ return credential;
+ }
+
+ // Role mappings
+
+ public static void createApplicationRoleMappings(ApplicationModel applicationModel, UserModel user, List<String> roleNames) {
+ if (user == null) {
+ throw new RuntimeException("User not found");
+ }
+
+ for (String roleName : roleNames) {
+ RoleModel role = applicationModel.getRole(roleName.trim());
+ if (role == null) {
+ role = applicationModel.addRole(roleName.trim());
+ }
+ user.grantRole(role);
+
+ }
+ }
+
+}
diff --git a/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
old mode 100644
new mode 100755
index 48ff59e..e071894
--- a/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1 +1,4 @@
-org.keycloak.models.ModelSpi
\ No newline at end of file
+org.keycloak.models.UserFederationSpi
+org.keycloak.models.RealmSpi
+org.keycloak.models.UserSessionSpi
+org.keycloak.models.UserSpi
\ No newline at end of file
model/hybrid/pom.xml 30(+30 -0)
diff --git a/model/hybrid/pom.xml b/model/hybrid/pom.xml
new file mode 100755
index 0000000..344fef3
--- /dev/null
+++ b/model/hybrid/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-model-hybrid</artifactId>
+ <name>Keycloak Model Hybrid</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/ApplicationAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/ApplicationAdapter.java
new file mode 100755
index 0000000..713d651
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/ApplicationAdapter.java
@@ -0,0 +1,137 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.realms.Application;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ApplicationAdapter extends ClientAdapter implements ApplicationModel {
+
+ private Application application;
+
+ ApplicationAdapter(HybridModelProvider provider, Application application) {
+ super(provider, application);
+ this.application = application;
+ }
+
+ Application getApplication() {
+ return application;
+ }
+
+ @Override
+ public void updateApplication() {
+ application.updateApplication();
+ }
+
+ @Override
+ public String getName() {
+ return application.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ application.setName(name);
+ }
+
+ @Override
+ public boolean isSurrogateAuthRequired() {
+ return application.isSurrogateAuthRequired();
+ }
+
+ @Override
+ public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
+ application.setSurrogateAuthRequired(surrogateAuthRequired);
+ }
+
+ @Override
+ public String getManagementUrl() {
+ return application.getManagementUrl();
+ }
+
+ @Override
+ public void setManagementUrl(String url) {
+ application.setManagementUrl(url);
+ }
+
+ @Override
+ public String getBaseUrl() {
+ return application.getBaseUrl();
+ }
+
+ @Override
+ public void setBaseUrl(String url) {
+ application.setBaseUrl(url);
+ }
+
+ @Override
+ public List<String> getDefaultRoles() {
+ return application.getDefaultRoles();
+ }
+
+ @Override
+ public void addDefaultRole(String name) {
+ if (getRole(name) == null) {
+ addRole(name);
+ }
+
+ application.addDefaultRole(name);
+ }
+
+ @Override
+ public void updateDefaultRoles(String[] defaultRoles) {
+ application.updateDefaultRoles(defaultRoles);
+ }
+
+ @Override
+ public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
+ return provider.mappings().wrap(application.getApplicationScopeMappings(provider.mappings().unwrap(client)));
+ }
+
+ @Override
+ public boolean isBearerOnly() {
+ return application.isBearerOnly();
+ }
+
+ @Override
+ public void setBearerOnly(boolean only) {
+ application.setBearerOnly(only);
+ }
+
+ @Override
+ public RoleModel getRole(String name) {
+ return provider.mappings().wrap(application.getRole(name));
+ }
+
+ @Override
+ public RoleModel addRole(String name) {
+ return addRole(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RoleModel addRole(String id, String name) {
+ return provider.mappings().wrap(application.addRole(id, name));
+ }
+
+ @Override
+ public boolean removeRole(RoleModel role) {
+ if (application.removeRole(provider.mappings().unwrap(role))) {
+ provider.users().onRoleRemoved(role.getId());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Set<RoleModel> getRoles() {
+ return provider.mappings().wrap(application.getRoles());
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/ClientAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/ClientAdapter.java
new file mode 100755
index 0000000..dfaa612
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/ClientAdapter.java
@@ -0,0 +1,191 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.realms.Client;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class ClientAdapter implements ClientModel {
+
+ protected HybridModelProvider provider;
+
+ protected Client client;
+
+ ClientAdapter(HybridModelProvider provider, Client client) {
+ this.provider = provider;
+ this.client = client;
+ }
+
+ @Override
+ public String getId() {
+ return client.getId();
+ }
+
+ @Override
+ public String getClientId() {
+ return client.getClientId();
+ }
+
+ @Override
+ public long getAllowedClaimsMask() {
+ return client.getAllowedClaimsMask();
+ }
+
+ @Override
+ public void setAllowedClaimsMask(long mask) {
+ client.setAllowedClaimsMask(mask);
+ }
+
+ @Override
+ public Set<String> getWebOrigins() {
+ return client.getWebOrigins();
+ }
+
+ @Override
+ public void setWebOrigins(Set<String> webOrigins) {
+ client.setWebOrigins(webOrigins);
+ }
+
+ @Override
+ public void addWebOrigin(String webOrigin) {
+ client.addWebOrigin(webOrigin);
+ }
+
+ @Override
+ public void removeWebOrigin(String webOrigin) {
+ client.removeWebOrigin(webOrigin);
+ }
+
+ @Override
+ public Set<String> getRedirectUris() {
+ return client.getRedirectUris();
+ }
+
+ @Override
+ public void setRedirectUris(Set<String> redirectUris) {
+ client.setRedirectUris(redirectUris);
+ }
+
+ @Override
+ public void addRedirectUri(String redirectUri) {
+ client.addRedirectUri(redirectUri);
+ }
+
+ @Override
+ public void removeRedirectUri(String redirectUri) {
+ client.removeRedirectUri(redirectUri);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return client.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ client.setEnabled(enabled);
+ }
+
+ @Override
+ public boolean validateSecret(String secret) {
+ return client.validateSecret(secret);
+ }
+
+ @Override
+ public String getSecret() {
+ return client.getSecret();
+ }
+
+ @Override
+ public void setSecret(String secret) {
+ client.setSecret(secret);
+ }
+
+ @Override
+ public boolean isPublicClient() {
+ return client.isPublicClient();
+ }
+
+ @Override
+ public void setPublicClient(boolean flag) {
+ client.setPublicClient(flag);
+ }
+
+ @Override
+ public boolean isDirectGrantsOnly() {
+ return client.isDirectGrantsOnly();
+ }
+
+ @Override
+ public void setDirectGrantsOnly(boolean flag) {
+ client.setDirectGrantsOnly(flag);
+ }
+
+ @Override
+ public Set<RoleModel> getScopeMappings() {
+ return provider.mappings().wrap(client.getScopeMappings());
+ }
+
+ @Override
+ public void addScopeMapping(RoleModel role) {
+ if (!hasScope(role)) {
+ client.addScopeMapping(provider.mappings().unwrap(role));
+ }
+ }
+
+ @Override
+ public void deleteScopeMapping(RoleModel role) {
+ client.deleteScopeMapping(provider.mappings().unwrap(role));
+ }
+
+ @Override
+ public Set<RoleModel> getRealmScopeMappings() {
+ return provider.mappings().wrap(client.getRealmScopeMappings());
+ }
+
+ @Override
+ public boolean hasScope(RoleModel role) {
+ Set<RoleModel> roles = getScopeMappings();
+ if (roles.contains(role)) {
+ return true;
+ }
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public RealmModel getRealm() {
+ return provider.mappings().wrap(client.getRealm());
+ }
+
+ @Override
+ public int getNotBefore() {
+ return client.getNotBefore();
+ }
+
+ @Override
+ public void setNotBefore(int notBefore) {
+ client.setNotBefore(notBefore);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!this.getClass().equals(o.getClass())) return false;
+
+ ClientAdapter that = (ClientAdapter) o;
+ return that.getId().equals(getId());
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridKeycloakTransaction.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridKeycloakTransaction.java
new file mode 100644
index 0000000..b6a54ea
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridKeycloakTransaction.java
@@ -0,0 +1,65 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.KeycloakTransaction;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HybridKeycloakTransaction implements KeycloakTransaction {
+
+ private KeycloakTransaction[] txs;
+
+ public HybridKeycloakTransaction(KeycloakTransaction... txs) {
+ this.txs = txs;
+ }
+
+ @Override
+ public void begin() {
+ for (KeycloakTransaction tx : txs) {
+ tx.begin();
+ }
+ }
+
+ @Override
+ public void commit() {
+ // TODO What do we do if one tx fails?
+ for (KeycloakTransaction tx : txs) {
+ tx.commit();
+ }
+ }
+
+ @Override
+ public void rollback() {
+ for (KeycloakTransaction tx : txs) {
+ tx.rollback();
+ }
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ for (KeycloakTransaction tx : txs) {
+ tx.setRollbackOnly();
+ }
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ for (KeycloakTransaction tx : txs) {
+ if (tx.getRollbackOnly()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isActive() {
+ for (KeycloakTransaction tx : txs) {
+ if (tx.isActive()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridModelProvider.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridModelProvider.java
new file mode 100755
index 0000000..1b27841
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridModelProvider.java
@@ -0,0 +1,202 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.ModelProvider;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.realms.RealmProvider;
+import org.keycloak.models.sessions.SessionProvider;
+import org.keycloak.models.users.UserProvider;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HybridModelProvider implements ModelProvider {
+
+ private final Mappings mappings;
+
+ private KeycloakSession session;
+
+ private HybridKeycloakTransaction tx;
+
+ public HybridModelProvider(KeycloakSession session) {
+ this.session = session;
+ this.mappings = new Mappings(this);
+ }
+
+ @Override
+ public KeycloakTransaction getTransaction() {
+ if (tx == null) {
+ tx = new HybridKeycloakTransaction(realms().getTransaction(), users().getTransaction(), sessions().getTransaction());
+ }
+
+ return tx;
+ }
+
+ Mappings mappings() {
+ return mappings;
+ }
+
+ SessionProvider sessions() {
+ return session.getProvider(SessionProvider.class);
+ }
+
+ UserProvider users() {
+ return session.getProvider(UserProvider.class);
+ }
+
+ RealmProvider realms() {
+ return session.getProvider(RealmProvider.class);
+ }
+
+ @Override
+ public RealmModel createRealm(String name) {
+ return createRealm(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RealmModel createRealm(String id, String name) {
+ return mappings.wrap(realms().createRealm(id, name));
+ }
+
+ @Override
+ public RealmModel getRealm(String id) {
+ return mappings.wrap(realms().getRealm(id));
+ }
+
+ @Override
+ public RealmModel getRealmByName(String name) {
+ return mappings.wrap(realms().getRealmByName(name));
+ }
+
+ @Override
+ public UserModel getUserById(String id, RealmModel realm) {
+ return mappings.wrap(realm, users().getUserById(id, realm.getId()));
+ }
+
+ @Override
+ public UserModel getUserByUsername(String username, RealmModel realm) {
+ return mappings.wrap(realm, users().getUserByUsername(username, realm.getId()));
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email, RealmModel realm) {
+ return mappings.wrap(realm, users().getUserByEmail(email, realm.getId()));
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+ return mappings.wrap(realm, users().getUserByAttribute("keycloak.socialLink." + socialLink.getSocialProvider() + ".userId", socialLink.getSocialUserId(), realm.getId()));
+ }
+
+ @Override
+ public List<UserModel> getUsers(RealmModel realm) {
+ return mappings.wrapUsers(realm, users().getUsers(realm.getId()));
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search, RealmModel realm) {
+ return mappings.wrapUsers(realm, users().searchForUser(search, realm.getId()));
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+ return mappings.wrapUsers(realm, users().searchForUserByAttributes(attributes, realm.getId()));
+ }
+
+ @Override
+ public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+ Set<SocialLinkModel> links = new HashSet<SocialLinkModel>();
+
+ for (Map.Entry<String, String> e : user.getAttributes().entrySet()) {
+ if (e.getKey().matches("keycloak\\.socialLink\\..*\\.userId")) {
+ String provider = e.getKey().split("\\.")[2];
+
+ SocialLinkModel link = new SocialLinkModel();
+ link.setSocialProvider(provider);
+ link.setSocialUserId(e.getValue());
+ link.setSocialUsername(user.getAttribute("keycloak.socialLink." + provider + ".username"));
+
+ links.add(link);
+ }
+
+ }
+
+ return links;
+ }
+
+ @Override
+ public SocialLinkModel getSocialLink(UserModel user, String provider, RealmModel realm) {
+ if (user.getAttribute("keycloak.socialLink." + provider + ".userId") != null) {
+ SocialLinkModel link = new SocialLinkModel();
+ link.setSocialProvider(provider);
+ link.setSocialUserId(user.getAttribute("keycloak.socialLink." + provider + ".userId"));
+ link.setSocialUsername(user.getAttribute("keycloak.socialLink." + provider + ".username"));
+ return link;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public RoleModel getRoleById(String id, RealmModel realm) {
+ return mappings.wrap(realms().getRoleById(id, realm.getId()));
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id, RealmModel realm) {
+ return mappings.wrap(realms().getApplicationById(id, realm.getId()));
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
+ return mappings.wrap(realms().getOAuthClientById(id, realm.getId()));
+ }
+
+ @Override
+ public List<RealmModel> getRealms() {
+ return mappings.wrap(realms().getRealms());
+ }
+
+ @Override
+ public boolean removeRealm(String id) {
+ if (realms().removeRealm(id)) {
+ users().onRealmRemoved(id);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public UsernameLoginFailureModel getUserLoginFailure(String username, RealmModel realm) {
+ return mappings.wrap(sessions().getUserLoginFailure(username, realm.getId()));
+ }
+
+ @Override
+ public UsernameLoginFailureModel addUserLoginFailure(String username, RealmModel realm) {
+ return mappings.wrap(sessions().addUserLoginFailure(username, realm.getId()));
+ }
+
+ @Override
+ public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
+ return mappings.wrapLoginFailures(sessions().getAllUserLoginFailures(realm.getId()));
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridModelProviderFactory.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridModelProviderFactory.java
new file mode 100755
index 0000000..3a644d4
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/HybridModelProviderFactory.java
@@ -0,0 +1,31 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelProvider;
+import org.keycloak.models.ModelProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HybridModelProviderFactory implements ModelProviderFactory {
+
+ @Override
+ public String getId() {
+ return "hybrid";
+ }
+
+ @Override
+ public ModelProvider create(KeycloakSession session) {
+ return new HybridModelProvider(session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/Mappings.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/Mappings.java
new file mode 100644
index 0000000..39972ae
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/Mappings.java
@@ -0,0 +1,220 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.realms.Application;
+import org.keycloak.models.realms.Client;
+import org.keycloak.models.realms.OAuthClient;
+import org.keycloak.models.realms.Realm;
+import org.keycloak.models.realms.Role;
+import org.keycloak.models.sessions.LoginFailure;
+import org.keycloak.models.sessions.Session;
+import org.keycloak.models.users.User;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class Mappings {
+
+ private final HybridModelProvider provider;
+ private Map<Object, Object> mappings = new HashMap<Object, Object>();
+
+ public Mappings(HybridModelProvider provider) {
+ this.provider = provider;
+ }
+
+ public RealmModel wrap(Realm realm) {
+ if (realm == null) return null;
+
+ RealmAdapter adapter = (RealmAdapter) mappings.get(realm);
+ if (adapter == null) {
+ adapter = new RealmAdapter(provider, realm);
+ mappings.put(realm, adapter);
+ }
+ return adapter;
+ }
+
+ public List<RealmModel> wrap(List<Realm> realms) {
+ List<RealmModel> adapters = new LinkedList<RealmModel>();
+ for (Realm realm : realms) {
+ adapters.add(wrap(realm));
+ }
+ return adapters;
+ }
+
+ public RoleModel wrap(Role role) {
+ if (role == null) return null;
+
+ RoleAdapter adapter = (RoleAdapter) mappings.get(role);
+ if (adapter == null) {
+ adapter = new RoleAdapter(provider, role);
+ mappings.put(role, adapter);
+ }
+ return adapter;
+ }
+
+ public Set<RoleModel> wrap(Set<Role> roles) {
+ Set<RoleModel> adapters = new HashSet<RoleModel>();
+ for (Role role : roles) {
+ adapters.add(wrap(role));
+ }
+ return adapters;
+ }
+
+ public Role unwrap(RoleModel role) {
+ if (role instanceof RoleAdapter) {
+ return ((RoleAdapter) role).getRole();
+ } else {
+ return provider.realms().getRoleById(role.getId(), getRealm(role.getContainer()));
+ }
+ }
+
+ public ApplicationModel wrap(Application application) {
+ return application != null ? new ApplicationAdapter(provider, application) : null;
+ }
+
+ public List<ApplicationModel> wrapApps(List<Application> applications) {
+ List<ApplicationModel> adapters = new LinkedList<ApplicationModel>();
+ for (Application application : applications) {
+ adapters.add(wrap(application));
+ }
+ return adapters;
+ }
+
+ public Map<String, ApplicationModel> wrap(Map<String, Application> applications) {
+ Map<String, ApplicationModel> adapters = new HashMap<String, ApplicationModel>();
+ for (Map.Entry<String, Application> e : applications.entrySet()) {
+ adapters.put(e.getKey(), wrap(e.getValue()));
+ }
+ return adapters;
+ }
+
+ public OAuthClientModel wrap(OAuthClient client) {
+ return client != null ? new OAuthClientAdapter(provider, client) : null;
+ }
+
+ public List<OAuthClientModel> wrapClients(List<OAuthClient> clients) {
+ List<OAuthClientModel> adapters = new LinkedList<OAuthClientModel>();
+ for (OAuthClient client : clients) {
+ adapters.add(wrap(client));
+ }
+ return adapters;
+ }
+
+ public Client unwrap(ClientModel client) {
+ if (client == null) {
+ return null;
+ }
+
+ if (client instanceof ApplicationAdapter) {
+ return ((ApplicationAdapter) client).getApplication();
+ } else if (client instanceof OAuthClientAdapter) {
+ return ((OAuthClientAdapter) client).getOauthClient();
+ } else {
+ throw new IllegalArgumentException("Not a hybrid model");
+ }
+ }
+
+ public Application unwrap(ApplicationModel application) {
+ if (application == null) {
+ return null;
+ }
+
+ if (!(application instanceof ApplicationAdapter)) {
+ throw new IllegalArgumentException("Not a hybrid model");
+ }
+
+ return ((ApplicationAdapter) application).getApplication();
+ }
+
+ public UserModel wrap(RealmModel realm, User user) {
+ return user != null ? new UserAdapter(provider, realm, user) : null;
+ }
+
+ public List<UserModel> wrapUsers(RealmModel realm, List<User> users) {
+ List<UserModel> adapters = new LinkedList<UserModel>();
+ for (User user : users) {
+ adapters.add(wrap(realm, user));
+ }
+ return adapters;
+ }
+
+ public static User unwrap(UserModel user) {
+ if (user == null) {
+ return null;
+ }
+
+ if (!(user instanceof UserAdapter)) {
+ throw new IllegalArgumentException("Not a hybrid model");
+ }
+
+ return ((UserAdapter) user).getUser();
+ }
+
+ public UserSessionModel wrap(RealmModel realm, Session session) {
+ return session != null ? new UserSessionAdapter(provider, realm, session) : null;
+ }
+
+ public List<UserSessionModel> wrapSessions(RealmModel realm, List<Session> sessions) {
+ List<UserSessionModel> adapters = new LinkedList<UserSessionModel>();
+ for (Session session : sessions) {
+ adapters.add(wrap(realm, session));
+ }
+ return adapters;
+ }
+
+ public Set<UserSessionModel> wrapSessions(RealmModel realm, Set<Session> sessions) {
+ Set<UserSessionModel> adapters = new HashSet<UserSessionModel>();
+ for (Session session : sessions) {
+ adapters.add(wrap(realm, session));
+ }
+ return adapters;
+ }
+
+ public Session unwrap(UserSessionModel session) {
+ if (session == null) {
+ return null;
+ }
+
+ if (!(session instanceof UserSessionAdapter)) {
+ throw new IllegalArgumentException("Not a hybrid model");
+ }
+
+ return ((UserSessionAdapter) session).getSession();
+ }
+
+ public UsernameLoginFailureModel wrap(LoginFailure loginFailure) {
+ return loginFailure != null ? new UsernameLoginFailureAdapter(provider, loginFailure) : null;
+ }
+
+ public List<UsernameLoginFailureModel> wrapLoginFailures(List<LoginFailure> loginFailures) {
+ List<UsernameLoginFailureModel> adapters = new LinkedList<UsernameLoginFailureModel>();
+ for (LoginFailure loginFailure : loginFailures) {
+ adapters.add(wrap(loginFailure));
+ }
+ return adapters;
+ }
+
+ private String getRealm(RoleContainerModel container) {
+ if (container instanceof RealmModel) {
+ return ((RealmModel) container).getId();
+ } else {
+ return ((ApplicationModel) container).getRealm().getId();
+ }
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/OAuthClientAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/OAuthClientAdapter.java
new file mode 100644
index 0000000..308e819
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/OAuthClientAdapter.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.realms.OAuthClient;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OAuthClientAdapter extends ClientAdapter implements OAuthClientModel {
+
+ private OAuthClient oauthClient;
+
+ OAuthClientAdapter(HybridModelProvider provider, OAuthClient oauthClient) {
+ super(provider, oauthClient);
+ this.oauthClient = oauthClient;
+ }
+
+ OAuthClient getOauthClient() {
+ return oauthClient;
+ }
+
+ @Override
+ public void setClientId(String id) {
+ oauthClient.setClientId(id);
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/RealmAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/RealmAdapter.java
new file mode 100755
index 0000000..f8b5415
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/RealmAdapter.java
@@ -0,0 +1,794 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.realms.Application;
+import org.keycloak.models.realms.Client;
+import org.keycloak.models.realms.OAuthClient;
+import org.keycloak.models.realms.Realm;
+import org.keycloak.models.users.Credentials;
+import org.keycloak.models.users.Feature;
+import org.keycloak.models.users.User;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+import org.keycloak.models.utils.TimeBasedOTP;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RealmAdapter implements RealmModel {
+
+ private HybridModelProvider provider;
+ private Realm realm;
+
+ RealmAdapter(HybridModelProvider provider, Realm realm) {
+ this.provider = provider;
+ this.realm = realm;
+ }
+
+ Realm getRealm() {
+ return realm;
+ }
+
+ @Override
+ public String getId() {
+ return realm.getId();
+ }
+
+ @Override
+ public String getName() {
+ return realm.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ realm.setName(name);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return realm.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ realm.setEnabled(enabled);
+ }
+
+ @Override
+ public boolean isSslNotRequired() {
+ return realm.isSslNotRequired();
+ }
+
+ @Override
+ public void setSslNotRequired(boolean sslNotRequired) {
+ realm.setSslNotRequired(sslNotRequired);
+ }
+
+ @Override
+ public boolean isRegistrationAllowed() {
+ return realm.isRegistrationAllowed();
+ }
+
+ @Override
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ realm.setRegistrationAllowed(registrationAllowed);
+ }
+
+ @Override
+ public boolean isPasswordCredentialGrantAllowed() {
+ return realm.isPasswordCredentialGrantAllowed();
+ }
+
+ @Override
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
+ }
+
+ @Override
+ public boolean isRememberMe() {
+ return realm.isRememberMe();
+ }
+
+ @Override
+ public void setRememberMe(boolean rememberMe) {
+ realm.setRememberMe(rememberMe);
+ }
+
+ @Override
+ public boolean isBruteForceProtected() {
+ return realm.isBruteForceProtected();
+ }
+
+ @Override
+ public void setBruteForceProtected(boolean value) {
+ realm.setBruteForceProtected(value);
+ }
+
+ @Override
+ public int getMaxFailureWaitSeconds() {
+ return realm.getMaxFailureWaitSeconds();
+ }
+
+ @Override
+ public void setMaxFailureWaitSeconds(int val) {
+ realm.setMaxFailureWaitSeconds(val);
+ }
+
+ @Override
+ public int getWaitIncrementSeconds() {
+ return realm.getWaitIncrementSeconds();
+ }
+
+ @Override
+ public void setWaitIncrementSeconds(int val) {
+ realm.setWaitIncrementSeconds(val);
+ }
+
+ @Override
+ public int getMinimumQuickLoginWaitSeconds() {
+ return realm.getMinimumQuickLoginWaitSeconds();
+ }
+
+ @Override
+ public void setMinimumQuickLoginWaitSeconds(int val) {
+ realm.setMinimumQuickLoginWaitSeconds(val);
+ }
+
+ @Override
+ public long getQuickLoginCheckMilliSeconds() {
+ return realm.getQuickLoginCheckMilliSeconds();
+ }
+
+ @Override
+ public void setQuickLoginCheckMilliSeconds(long val) {
+ realm.setQuickLoginCheckMilliSeconds(val);
+ }
+
+ @Override
+ public int getMaxDeltaTimeSeconds() {
+ return realm.getMaxDeltaTimeSeconds();
+ }
+
+ @Override
+ public void setMaxDeltaTimeSeconds(int val) {
+ realm.setMaxDeltaTimeSeconds(val);
+ }
+
+ @Override
+ public int getFailureFactor() {
+ return realm.getFailureFactor();
+ }
+
+ @Override
+ public void setFailureFactor(int failureFactor) {
+ realm.setFailureFactor(failureFactor);
+ }
+
+ @Override
+ public boolean isVerifyEmail() {
+ return realm.isVerifyEmail();
+ }
+
+ @Override
+ public void setVerifyEmail(boolean verifyEmail) {
+ realm.setVerifyEmail(verifyEmail);
+ }
+
+ @Override
+ public boolean isResetPasswordAllowed() {
+ return realm.isResetPasswordAllowed();
+ }
+
+ @Override
+ public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
+ realm.setResetPasswordAllowed(resetPasswordAllowed);
+ }
+
+ @Override
+ public int getSsoSessionIdleTimeout() {
+ return realm.getSsoSessionIdleTimeout();
+ }
+
+ @Override
+ public void setSsoSessionIdleTimeout(int seconds) {
+ realm.setSsoSessionIdleTimeout(seconds);
+ }
+
+ @Override
+ public int getSsoSessionMaxLifespan() {
+ return realm.getSsoSessionMaxLifespan();
+ }
+
+ @Override
+ public void setSsoSessionMaxLifespan(int seconds) {
+ realm.setSsoSessionMaxLifespan(seconds);
+ }
+
+ @Override
+ public int getAccessTokenLifespan() {
+ return realm.getAccessTokenLifespan();
+ }
+
+ @Override
+ public void setAccessTokenLifespan(int seconds) {
+ realm.setAccessTokenLifespan(seconds);
+ }
+
+ @Override
+ public int getAccessCodeLifespan() {
+ return realm.getAccessCodeLifespan();
+ }
+
+ @Override
+ public void setAccessCodeLifespan(int seconds) {
+ realm.setAccessCodeLifespan(seconds);
+ }
+
+ @Override
+ public int getAccessCodeLifespanUserAction() {
+ return realm.getAccessCodeLifespanUserAction();
+ }
+
+ @Override
+ public void setAccessCodeLifespanUserAction(int seconds) {
+ realm.setAccessCodeLifespanUserAction(seconds);
+ }
+
+ @Override
+ public String getPublicKeyPem() {
+ return realm.getPublicKeyPem();
+ }
+
+ @Override
+ public void setPublicKeyPem(String publicKeyPem) {
+ realm.setPublicKeyPem(publicKeyPem);
+ }
+
+ @Override
+ public String getPrivateKeyPem() {
+ return realm.getPrivateKeyPem();
+ }
+
+ @Override
+ public void setPrivateKeyPem(String privateKeyPem) {
+ realm.setPrivateKeyPem(privateKeyPem);
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return realm.getPublicKey();
+ }
+
+ @Override
+ public void setPublicKey(PublicKey publicKey) {
+ realm.setPublicKey(publicKey);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey() {
+ return realm.getPrivateKey();
+ }
+
+ @Override
+ public void setPrivateKey(PrivateKey privateKey) {
+ realm.setPrivateKey(privateKey);
+ }
+
+ @Override
+ public List<RequiredCredentialModel> getRequiredCredentials() {
+ return realm.getRequiredCredentials();
+ }
+
+ @Override
+ public void addRequiredCredential(String cred) {
+ realm.addRequiredCredential(cred);
+ }
+
+ @Override
+ public PasswordPolicy getPasswordPolicy() {
+ return realm.getPasswordPolicy();
+ }
+
+ @Override
+ public void setPasswordPolicy(PasswordPolicy policy) {
+ realm.setPasswordPolicy(policy);
+ }
+
+ @Override
+ public boolean validatePassword(UserModel userModel, String password) {
+ if (provider.users().supports(Feature.VERIFY_CREDENTIALS)) {
+ User user = provider.mappings().unwrap(userModel);
+ return provider.users().verifyCredentials(user, new Credentials(UserCredentialModel.PASSWORD, password));
+ } else {
+ for (UserCredentialValueModel cred : userModel.getCredentialsDirectly()) {
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue());
+ }
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public boolean validateTOTP(UserModel userModel, String password, String token) {
+ if (provider.users().supports(Feature.VERIFY_CREDENTIALS)) {
+ User user = provider.mappings().unwrap(userModel);
+ return provider.users().verifyCredentials(user, new Credentials(UserCredentialModel.PASSWORD, password),
+ new Credentials(UserCredentialModel.TOTP, token));
+ } else {
+ if (!validatePassword(userModel, password)) return false;
+ for (UserCredentialValueModel cred : userModel.getCredentialsDirectly()) {
+ if (cred.getType().equals(UserCredentialModel.TOTP)) {
+ return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
+ }
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public UserModel getUser(String name) {
+ return provider.getUserByUsername(name, this);
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email) {
+ return provider.getUserByEmail(email, this);
+ }
+
+ @Override
+ public UserModel getUserById(String name) {
+ return provider.getUserById(name, this);
+ }
+
+ @Override
+ public UserModel addUser(String id, String username, boolean addDefaultRoles) {
+ if (id == null) {
+ id = KeycloakModelUtils.generateId();
+ }
+
+ Set<String> initialRoles = new HashSet<String>();
+
+ if (addDefaultRoles) {
+ for (String r : realm.getDefaultRoles()) {
+ initialRoles.add(realm.getRole(r).getId());
+ }
+
+ for (Application app : realm.getApplications()) {
+ for (String r : app.getDefaultRoles()) {
+ initialRoles.add(app.getRole(r).getId());
+ }
+ }
+ }
+
+ return provider.mappings().wrap(this, provider.users().addUser(id, username, initialRoles, realm.getId()));
+ }
+
+ @Override
+ public UserModel addUser(String username) {
+ return addUser(null, username, true);
+ }
+
+ @Override
+ public boolean removeUser(String name) {
+ return provider.users().removeUser(name, realm.getId());
+ }
+
+ @Override
+ public RoleModel getRoleById(String id) {
+ return provider.mappings().wrap(provider.realms().getRoleById(id, realm.getId()));
+ }
+
+ @Override
+ public List<String> getDefaultRoles() {
+ return realm.getDefaultRoles();
+ }
+
+ @Override
+ public void addDefaultRole(String name) {
+ if (getRole(name) == null) {
+ addRole(name);
+ }
+
+ realm.addDefaultRole(name);
+ }
+
+ @Override
+ public void updateDefaultRoles(String[] defaultRoles) {
+ for (String name : defaultRoles) {
+ if (getRole(name) == null) {
+ addRole(name);
+ }
+ }
+
+ realm.updateDefaultRoles(defaultRoles);
+ }
+
+ @Override
+ public ClientModel findClient(String clientId) {
+ Client client = realm.findClient(clientId);
+ if (client instanceof Application) {
+ return provider.mappings().wrap((Application) client);
+ } else if (client instanceof OAuthClient) {
+ return provider.mappings().wrap((OAuthClient) client);
+ } else {
+ throw new IllegalArgumentException("Unsupported client type");
+ }
+ }
+
+ @Override
+ public Map<String, ApplicationModel> getApplicationNameMap() {
+ return provider.mappings().wrap(realm.getApplicationNameMap());
+ }
+
+ @Override
+ public List<ApplicationModel> getApplications() {
+ return provider.mappings().wrapApps(realm.getApplications());
+ }
+
+ @Override
+ public ApplicationModel addApplication(String name) {
+ return addApplication(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public ApplicationModel addApplication(String id, String name) {
+ return provider.mappings().wrap(realm.addApplication(id, name));
+ }
+
+ @Override
+ public boolean removeApplication(String id) {
+ Application application = provider.realms().getApplicationById(id, realm.getId());
+ if (application != null) {
+ return realm.removeApplication(application);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id) {
+ return provider.getApplicationById(id, this);
+ }
+
+ @Override
+ public ApplicationModel getApplicationByName(String name) {
+ return provider.mappings().wrap(realm.getApplicationByName(name));
+ }
+
+ @Override
+ public void updateRequiredCredentials(Set<String> creds) {
+ realm.updateRequiredCredentials(creds);
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
+ return provider.getUserBySocialLink(socialLink, this);
+ }
+
+ @Override
+ public Set<SocialLinkModel> getSocialLinks(UserModel user) {
+ return provider.getSocialLinks(user, this);
+ }
+
+ @Override
+ public SocialLinkModel getSocialLink(UserModel user, String socialProvider) {
+ return provider.getSocialLink(user, socialProvider, this);
+ }
+
+ @Override
+ public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
+ user.setAttribute("keycloak.socialLink." + socialLink.getSocialProvider() + ".userId", socialLink.getSocialUserId());
+ user.setAttribute("keycloak.socialLink." + socialLink.getSocialProvider() + ".username", socialLink.getSocialUsername());
+ }
+
+ @Override
+ public boolean removeSocialLink(UserModel user, String socialProvider) {
+ if (user.getAttribute("keycloak.socialLink." + socialProvider + ".userId") != null) {
+ user.removeAttribute("keycloak.socialLink." + socialProvider + ".userId");
+ user.removeAttribute("keycloak.socialLink." + socialProvider + ".username");
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isSocial() {
+ return realm.isSocial();
+ }
+
+ @Override
+ public void setSocial(boolean social) {
+ realm.setSocial(social);
+ }
+
+ @Override
+ public boolean isUpdateProfileOnInitialSocialLogin() {
+ return realm.isUpdateProfileOnInitialSocialLogin();
+ }
+
+ @Override
+ public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+ realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
+ }
+
+ @Override
+ public UsernameLoginFailureModel getUserLoginFailure(String username) {
+ return provider.getUserLoginFailure(username, this);
+ }
+
+ @Override
+ public UsernameLoginFailureModel addUserLoginFailure(String username) {
+ return provider.addUserLoginFailure(username, this);
+ }
+
+ @Override
+ public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
+ return provider.getAllUserLoginFailures(this);
+ }
+
+ @Override
+ public List<UserModel> getUsers() {
+ return provider.getUsers(this);
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search) {
+ return provider.searchForUser(search, this);
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
+ return provider.searchForUserByAttributes(attributes, this);
+ }
+
+ @Override
+ public OAuthClientModel addOAuthClient(String name) {
+ return addOAuthClient(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public OAuthClientModel addOAuthClient(String id, String name) {
+ return provider.mappings().wrap(realm.addOAuthClient(id, name));
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClient(String name) {
+ return provider.mappings().wrap(realm.getOAuthClient(name));
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id) {
+ return provider.getOAuthClientById(id, this);
+ }
+
+ @Override
+ public boolean removeOAuthClient(String id) {
+ OAuthClient client = provider.realms().getOAuthClientById(id, realm.getId());
+ if (client != null) {
+ return realm.removeOAuthClient(client);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public List<OAuthClientModel> getOAuthClients() {
+ return provider.mappings().wrapClients(realm.getOAuthClients());
+ }
+
+ @Override
+ public Map<String, String> getSmtpConfig() {
+ return realm.getSmtpConfig();
+ }
+
+ @Override
+ public void setSmtpConfig(Map<String, String> smtpConfig) {
+ realm.setSmtpConfig(smtpConfig);
+ }
+
+ @Override
+ public Map<String, String> getSocialConfig() {
+ return realm.getSocialConfig();
+ }
+
+ @Override
+ public void setSocialConfig(Map<String, String> socialConfig) {
+ realm.setSocialConfig(socialConfig);
+ }
+
+ @Override
+ public Map<String, String> getLdapServerConfig() {
+ return realm.getLdapServerConfig();
+ }
+
+ @Override
+ public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
+ realm.setLdapServerConfig(ldapServerConfig);
+ }
+
+ @Override
+ public List<AuthenticationProviderModel> getAuthenticationProviders() {
+ return realm.getAuthenticationProviders();
+ }
+
+ @Override
+ public void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders) {
+ realm.setAuthenticationProviders(authenticationProviders);
+ }
+
+ @Override
+ public String getLoginTheme() {
+ return realm.getLoginTheme();
+ }
+
+ @Override
+ public void setLoginTheme(String name) {
+ realm.setLoginTheme(name);
+ }
+
+ @Override
+ public String getAccountTheme() {
+ return realm.getAccountTheme();
+ }
+
+ @Override
+ public void setAccountTheme(String name) {
+ realm.setAccountTheme(name);
+ }
+
+ @Override
+ public String getAdminTheme() {
+ return realm.getAdminTheme();
+ }
+
+ @Override
+ public void setAdminTheme(String name) {
+ realm.setAdminTheme(name);
+ }
+
+ @Override
+ public String getEmailTheme() {
+ return realm.getEmailTheme();
+ }
+
+ @Override
+ public void setEmailTheme(String name) {
+ realm.setEmailTheme(name);
+ }
+
+ @Override
+ public int getNotBefore() {
+ return realm.getNotBefore();
+ }
+
+ @Override
+ public void setNotBefore(int notBefore) {
+ realm.setNotBefore(notBefore);
+ }
+
+ @Override
+ public boolean isAuditEnabled() {
+ return realm.isAuditEnabled();
+ }
+
+ @Override
+ public void setAuditEnabled(boolean enabled) {
+ realm.setAuditEnabled(enabled);
+ }
+
+ @Override
+ public long getAuditExpiration() {
+ return realm.getAuditExpiration();
+ }
+
+ @Override
+ public void setAuditExpiration(long expiration) {
+ realm.setAuditExpiration(expiration);
+ }
+
+ @Override
+ public Set<String> getAuditListeners() {
+ return realm.getAuditListeners();
+ }
+
+ @Override
+ public void setAuditListeners(Set<String> listeners) {
+ realm.setAuditListeners(listeners);
+ }
+
+ @Override
+ public ApplicationModel getMasterAdminApp() {
+ return provider.mappings().wrap(realm.getMasterAdminApp());
+ }
+
+ @Override
+ public void setMasterAdminApp(ApplicationModel app) {
+ realm.setMasterAdminApp(provider.mappings().unwrap(app));
+ }
+
+ @Override
+ public ClientModel findClientById(String id) {
+ Application application = provider.realms().getApplicationById(id, realm.getId());
+ if (application != null) {
+ return provider.mappings().wrap(application);
+ }
+
+ OAuthClient client = provider.realms().getOAuthClientById(id, realm.getId());
+ if (client != null) {
+ return provider.mappings().wrap(client);
+ }
+
+ return null;
+ }
+
+ @Override
+ public RoleModel getRole(String name) {
+ return provider.mappings().wrap(realm.getRole(name));
+ }
+
+ @Override
+ public RoleModel addRole(String name) {
+ return addRole(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RoleModel addRole(String id, String name) {
+ return provider.mappings().wrap(realm.addRole(id, name));
+
+ }
+
+ @Override
+ public boolean removeRoleById(String id) {
+ RoleModel role = getRoleById(id);
+ if (role != null) {
+ if (role.getContainer().removeRole(role)) {
+ provider.users().onRoleRemoved(role.getId());
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean removeRole(RoleModel role) {
+ return removeRoleById(role.getId());
+ }
+
+ @Override
+ public Set<RoleModel> getRoles() {
+ return provider.mappings().wrap(realm.getRoles());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof RealmModel)) return false;
+
+ RealmModel that = (RealmModel) o;
+ return that.getId().equals(getId());
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/RoleAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/RoleAdapter.java
new file mode 100755
index 0000000..7b10d04
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/RoleAdapter.java
@@ -0,0 +1,114 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.realms.Application;
+import org.keycloak.models.realms.Realm;
+import org.keycloak.models.realms.Role;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RoleAdapter implements RoleModel {
+
+ private HybridModelProvider provider;
+
+ private Role role;
+
+ RoleAdapter(HybridModelProvider provider, Role role) {
+ this.provider = provider;
+ this.role = role;
+ }
+
+ Role getRole() {
+ return role;
+ }
+
+ @Override
+ public String getName() {
+ return role.getName();
+ }
+
+ @Override
+ public String getDescription() {
+ return role.getDescription();
+ }
+
+ @Override
+ public void setDescription(String description) {
+ role.setDescription(description);
+ }
+
+ @Override
+ public String getId() {
+ return role.getId();
+ }
+
+ @Override
+ public void setName(String name) {
+ role.setName(name);
+ }
+
+ @Override
+ public boolean isComposite() {
+ return role.isComposite();
+ }
+
+ @Override
+ public void addCompositeRole(RoleModel role) {
+ this.role.addCompositeRole(provider.mappings().unwrap(role));
+ }
+
+ @Override
+ public void removeCompositeRole(RoleModel role) {
+ this.role.removeCompositeRole(provider.mappings().unwrap(role));
+ }
+
+ @Override
+ public Set<RoleModel> getComposites() {
+ return provider.mappings().wrap(role.getComposites());
+ }
+
+ @Override
+ public RoleContainerModel getContainer() {
+ if (role.getContainer() instanceof Application) {
+ return provider.mappings().wrap((Application) role.getContainer());
+ } else if (role.getContainer() instanceof Realm) {
+ return provider.mappings().wrap((Realm) role.getContainer());
+ } else {
+ throw new IllegalArgumentException("Unsupported role container");
+ }
+ }
+
+ @Override
+ public boolean hasRole(RoleModel role) {
+ if (this.equals(role)) {
+ return true;
+ }
+ if (!isComposite()) {
+ return false;
+ }
+
+ Set<RoleModel> visited = new HashSet<RoleModel>();
+ return KeycloakModelUtils.searchFor(role, this, visited);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof RoleModel)) return false;
+
+ RoleModel that = (RoleModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/UserAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/UserAdapter.java
new file mode 100644
index 0000000..e082fc6
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/UserAdapter.java
@@ -0,0 +1,325 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.users.Attributes;
+import org.keycloak.models.users.Credentials;
+import org.keycloak.models.users.Feature;
+import org.keycloak.models.users.User;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserAdapter implements UserModel {
+
+ private HybridModelProvider provider;
+ private RealmModel realm;
+ private User user;
+
+ UserAdapter(HybridModelProvider provider, RealmModel realm, User user) {
+ this.provider = provider;
+ this.realm = realm;
+ this.user = user;
+ }
+
+ User getUser() {
+ return user;
+ }
+
+ @Override
+ public String getId() {
+ return user.getId();
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getUsername();
+ }
+
+ @Override
+ public void setUsername(String username) {
+ user.setUsername(username);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return user.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ user.setEnabled(enabled);
+ }
+
+ @Override
+ public void setAttribute(String name, String value) {
+ user.setAttribute(name, value);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ user.removeAttribute(name);
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ return user.getAttribute(name);
+ }
+
+ @Override
+ public Map<String, String> getAttributes() {
+ return user.getAttributes();
+ }
+
+ @Override
+ public Set<RequiredAction> getRequiredActions() {
+ String value = user.getAttribute(Attributes.REQUIRED_ACTIONS);
+ if (value == null) {
+ return Collections.emptySet();
+ }
+
+ Set<RequiredAction> actions = new HashSet<RequiredAction>();
+ for (String a : value.substring(1, value.length() - 1).split(",")) {
+ actions.add(RequiredAction.valueOf(a.trim()));
+ }
+ return actions;
+ }
+
+ @Override
+ public void addRequiredAction(RequiredAction action) {
+ Set<RequiredAction> actions;
+ if (user.getAttribute(Attributes.REQUIRED_ACTIONS) == null) {
+ actions = new HashSet<RequiredAction>();
+ } else {
+ actions = getRequiredActions();
+ }
+
+ if (!actions.contains(action)) {
+ actions.add(action);
+ user.setAttribute(Attributes.REQUIRED_ACTIONS, actions.toString());
+ }
+ }
+
+ @Override
+ public void removeRequiredAction(RequiredAction action) {
+ Set<RequiredAction> actions = getRequiredActions();
+ if (actions.contains(action)) {
+ actions.remove(action);
+
+ if (actions.isEmpty()) {
+ user.removeAttribute(Attributes.REQUIRED_ACTIONS);
+ } else {
+ user.setAttribute(Attributes.REQUIRED_ACTIONS, actions.toString());
+ }
+ }
+ }
+
+ @Override
+ public String getFirstName() {
+ return user.getFirstName();
+ }
+
+ @Override
+ public void setFirstName(String firstName) {
+ user.setFirstName(firstName);
+ }
+
+ @Override
+ public String getLastName() {
+ return user.getLastName();
+ }
+
+ @Override
+ public void setLastName(String lastName) {
+ user.setLastName(lastName);
+ }
+
+ @Override
+ public String getEmail() {
+ return user.getEmail();
+ }
+
+ @Override
+ public void setEmail(String email) {
+ user.setEmail(email);
+ }
+
+ @Override
+ public boolean isEmailVerified() {
+ return isBooleanAttribute(Attributes.EMAIL_VERIFIED);
+ }
+
+ @Override
+ public void setEmailVerified(boolean verified) {
+ setBooleanAttribute(Attributes.EMAIL_VERIFIED, verified);
+ }
+
+ @Override
+ public boolean isTotp() {
+ return isBooleanAttribute(Attributes.TOTP_ENABLED);
+ }
+
+ @Override
+ public void setTotp(boolean totp) {
+ setBooleanAttribute(Attributes.TOTP_ENABLED, totp);
+ }
+
+ @Override
+ public void updateCredential(UserCredentialModel model) {
+ if (provider.users().supports(Feature.UPDATE_CREDENTIALS)) {
+ Credentials credentials;
+
+ if (model.getType().equals(UserCredentialModel.PASSWORD)) {
+ byte[] salt = getSalt();
+ int hashIterations = 1;
+ PasswordPolicy policy = realm.getPasswordPolicy();
+ if (policy != null) {
+ hashIterations = policy.getHashIterations();
+ if (hashIterations == -1) hashIterations = 1;
+ }
+ String value = new Pbkdf2PasswordEncoder(salt).encode(model.getValue(), hashIterations);
+
+ credentials = new Credentials(model.getType(), salt, value, hashIterations, model.getDevice());
+ } else {
+ credentials = new Credentials(model.getType(), model.getValue(), model.getDevice());
+ }
+
+ user.updateCredential(credentials);
+ } else {
+ throw new RuntimeException("Users store doesn't support updating credentials");
+ }
+ }
+
+ @Override
+ public List<UserCredentialValueModel> getCredentialsDirectly() {
+ if (provider.users().supports(Feature.READ_CREDENTIALS)) {
+ List<UserCredentialValueModel> models = new LinkedList<UserCredentialValueModel>();
+ for (Credentials cred : user.getCredentials()) {
+ UserCredentialValueModel model = new UserCredentialValueModel();
+ model.setType(cred.getType());
+ model.setValue(cred.getValue());
+ model.setDevice(cred.getDevice());
+ model.setSalt(cred.getSalt());
+ model.setHashIterations(cred.getHashIterations());
+ models.add(model);
+ }
+ return models;
+ } else {
+ throw new IllegalStateException("Users provider doesn't support reading credentials");
+ }
+ }
+
+ @Override
+ public void updateCredentialDirectly(UserCredentialValueModel model) {
+ if (provider.users().supports(Feature.UPDATE_CREDENTIALS)) {
+ Credentials credentials = new Credentials(model.getType(), model.getSalt(), model.getValue(), model.getHashIterations(), model.getDevice());
+ user.updateCredential(credentials);
+ } else {
+ throw new IllegalStateException("Users provider doesn't support updating credentials");
+ }
+ }
+
+ @Override
+ public AuthenticationLinkModel getAuthenticationLink() {
+ for (Map.Entry<String, String> e : user.getAttributes().entrySet()) {
+ if (e.getKey().matches("keycloak\\.authenticationLink\\..*\\.userId")) {
+ String provider = e.getKey().split("\\.")[2];
+ return new AuthenticationLinkModel(provider, e.getValue());
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
+ Iterator<Map.Entry<String, String>> itr = user.getAttributes().entrySet().iterator();
+ while (itr.hasNext()) {
+ if (itr.next().getKey().matches("keycloak\\.authenticationLink\\..*\\.userId")) {
+ itr.remove();
+ }
+ }
+ user.setAttribute("keycloak.authenticationLink." + authenticationLink.getAuthProvider() + ".userId", authenticationLink.getAuthUserId());
+ }
+
+ @Override
+ public Set<RoleModel> getRealmRoleMappings() {
+ Set<RoleModel> roles = new HashSet<RoleModel>();
+ for (RoleModel r : getRoleMappings()) {
+ if (r.getContainer() instanceof RealmModel) {
+ roles.add(r);
+ }
+ }
+ return roles;
+ }
+
+ @Override
+ public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
+ Set<RoleModel> roles = new HashSet<RoleModel>();
+ for (RoleModel r : getRoleMappings()) {
+ if (r.getContainer() instanceof ApplicationModel && ((ApplicationModel) r.getContainer()).getId().equals(app.getId())) {
+ roles.add(r);
+ }
+ }
+ return roles;
+ }
+
+ @Override
+ public boolean hasRole(RoleModel role) {
+ for (RoleModel r : getRoleMappings()) {
+ if (r.hasRole(role)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void grantRole(RoleModel role) {
+ user.grantRole(role.getId());
+ }
+
+ @Override
+ public Set<RoleModel> getRoleMappings() {
+ Set<RoleModel> roles = new HashSet<RoleModel>();
+ for (String r : user.getRoleMappings()) {
+ roles.add(realm.getRoleById(r));
+ }
+ return roles;
+ }
+
+ @Override
+ public void deleteRoleMapping(RoleModel role) {
+ user.deleteRoleMapping(role.getId());
+ }
+
+ private boolean isBooleanAttribute(String name) {
+ String v = user.getAttribute(name);
+ return v != null ? v.equals("true") : false;
+ }
+
+ private void setBooleanAttribute(String name, boolean enable) {
+ if (enable) {
+ user.setAttribute(name, "true");
+ } else {
+ user.removeAttribute(name);
+ }
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/UsernameLoginFailureAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/UsernameLoginFailureAdapter.java
new file mode 100755
index 0000000..b496f3f
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/UsernameLoginFailureAdapter.java
@@ -0,0 +1,70 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.sessions.LoginFailure;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel {
+
+ private HybridModelProvider provider;
+
+ private LoginFailure loginFailure;
+
+ UsernameLoginFailureAdapter(HybridModelProvider provider, LoginFailure loginFailure) {
+ this.provider = provider;
+ this.loginFailure = loginFailure;
+ }
+
+ @Override
+ public String getUsername() {
+ return loginFailure.getUsername();
+ }
+
+ @Override
+ public int getFailedLoginNotBefore() {
+ return loginFailure.getFailedLoginNotBefore();
+ }
+
+ @Override
+ public void setFailedLoginNotBefore(int notBefore) {
+ loginFailure.setFailedLoginNotBefore(notBefore);
+ }
+
+ @Override
+ public int getNumFailures() {
+ return loginFailure.getNumFailures();
+ }
+
+ @Override
+ public void incrementFailures() {
+ loginFailure.incrementFailures();
+ }
+
+ @Override
+ public void clearFailures() {
+ loginFailure.clearFailures();
+ }
+
+ @Override
+ public long getLastFailure() {
+ return loginFailure.getLastFailure();
+ }
+
+ @Override
+ public void setLastFailure(long lastFailure) {
+ loginFailure.setLastFailure(lastFailure);
+ }
+
+ @Override
+ public String getLastIPFailure() {
+ return loginFailure.getLastIPFailure();
+ }
+
+ @Override
+ public void setLastIPFailure(String ip) {
+ loginFailure.setLastIPFailure(ip);
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/hybrid/UserSessionAdapter.java b/model/hybrid/src/main/java/org/keycloak/models/hybrid/UserSessionAdapter.java
new file mode 100644
index 0000000..8b5a0d9
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/hybrid/UserSessionAdapter.java
@@ -0,0 +1,100 @@
+package org.keycloak.models.hybrid;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.sessions.Session;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionAdapter implements UserSessionModel {
+
+ private HybridModelProvider provider;
+ private RealmModel realm;
+ private Session session;
+
+ UserSessionAdapter(HybridModelProvider provider, RealmModel realm, Session session) {
+ this.provider = provider;
+ this.realm = realm;
+ this.session = session;
+ }
+
+ Session getSession() {
+ return session;
+ }
+
+ @Override
+ public String getId() {
+ return session.getId();
+ }
+
+ @Override
+ public void setId(String id) {
+ session.setId(id);
+ }
+
+ @Override
+ public UserModel getUser() {
+ return provider.getUserById(session.getUser(), realm);
+ }
+
+ @Override
+ public void setUser(UserModel user) {
+ session.setUser(user.getId());
+ }
+
+ @Override
+ public String getIpAddress() {
+ return session.getIpAddress();
+ }
+
+ @Override
+ public void setIpAddress(String ipAddress) {
+ session.setIpAddress(ipAddress);
+ }
+
+ @Override
+ public int getStarted() {
+ return session.getStarted();
+ }
+
+ @Override
+ public void setStarted(int started) {
+ session.setStarted(started);
+ }
+
+ @Override
+ public int getLastSessionRefresh() {
+ return session.getLastSessionRefresh();
+ }
+
+ @Override
+ public void setLastSessionRefresh(int seconds) {
+ session.setLastSessionRefresh(seconds);
+ }
+
+ @Override
+ public void associateClient(ClientModel client) {
+ session.associateClient(client.getId());
+ }
+
+ @Override
+ public List<ClientModel> getClientAssociations() {
+ List<ClientModel> clients = new LinkedList<ClientModel>();
+ for (String id : session.getClientAssociations()) {
+ clients.add(realm.findClientById(id));
+ }
+ return clients;
+ }
+
+ @Override
+ public void removeAssociatedClient(ClientModel client) {
+ session.removeAssociatedClient(client.getId());
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/Application.java b/model/hybrid/src/main/java/org/keycloak/models/realms/Application.java
new file mode 100755
index 0000000..bf6afb0
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/Application.java
@@ -0,0 +1,40 @@
+package org.keycloak.models.realms;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface Application extends RoleContainer, Client {
+ void updateApplication();
+
+ String getName();
+
+ void setName(String name);
+
+ boolean isSurrogateAuthRequired();
+
+ void setSurrogateAuthRequired(boolean surrogateAuthRequired);
+
+ String getManagementUrl();
+
+ void setManagementUrl(String url);
+
+ String getBaseUrl();
+
+ void setBaseUrl(String url);
+
+ List<String> getDefaultRoles();
+
+ void addDefaultRole(String name);
+
+ void updateDefaultRoles(String[] defaultRoles);
+
+ Set<Role> getApplicationScopeMappings(Client client);
+
+ boolean isBearerOnly();
+ void setBearerOnly(boolean only);
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/Client.java b/model/hybrid/src/main/java/org/keycloak/models/realms/Client.java
new file mode 100755
index 0000000..f7d6a6a
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/Client.java
@@ -0,0 +1,76 @@
+package org.keycloak.models.realms;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface Client {
+ /**
+ * Internal database key
+ *
+ * @return
+ */
+ String getId();
+
+ /**
+ * String exposed to outside world
+ *
+ * @return
+ */
+ String getClientId();
+
+ long getAllowedClaimsMask();
+
+ void setAllowedClaimsMask(long mask);
+
+ Set<String> getWebOrigins();
+
+ void setWebOrigins(Set<String> webOrigins);
+
+ void addWebOrigin(String webOrigin);
+
+ void removeWebOrigin(String webOrigin);
+
+ Set<String> getRedirectUris();
+
+ void setRedirectUris(Set<String> redirectUris);
+
+ void addRedirectUri(String redirectUri);
+
+ void removeRedirectUri(String redirectUri);
+
+
+ boolean isEnabled();
+
+ void setEnabled(boolean enabled);
+
+ boolean validateSecret(String secret);
+ String getSecret();
+ public void setSecret(String secret);
+
+ boolean isPublicClient();
+ void setPublicClient(boolean flag);
+
+ boolean isDirectGrantsOnly();
+ void setDirectGrantsOnly(boolean flag);
+
+ Set<Role> getScopeMappings();
+ void addScopeMapping(Role role);
+ void deleteScopeMapping(Role role);
+ Set<Role> getRealmScopeMappings();
+
+
+ Realm getRealm();
+
+ /**
+ * Time in seconds since epoc
+ *
+ * @return
+ */
+ int getNotBefore();
+
+ void setNotBefore(int notBefore);
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/OAuthClient.java b/model/hybrid/src/main/java/org/keycloak/models/realms/OAuthClient.java
new file mode 100755
index 0000000..74f4cfd
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/OAuthClient.java
@@ -0,0 +1,11 @@
+package org.keycloak.models.realms;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface OAuthClient extends Client {
+
+ void setClientId(String id);
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/Realm.java b/model/hybrid/src/main/java/org/keycloak/models/realms/Realm.java
new file mode 100755
index 0000000..5f1d6e8
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/Realm.java
@@ -0,0 +1,208 @@
+package org.keycloak.models.realms;
+
+import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RequiredCredentialModel;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface Realm extends RoleContainer {
+
+ String getId();
+
+ String getName();
+
+ void setName(String name);
+
+ boolean isEnabled();
+
+ void setEnabled(boolean enabled);
+
+ boolean isSslNotRequired();
+
+ void setSslNotRequired(boolean sslNotRequired);
+
+ boolean isRegistrationAllowed();
+
+ void setRegistrationAllowed(boolean registrationAllowed);
+
+ boolean isPasswordCredentialGrantAllowed();
+
+ void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed);
+
+ boolean isRememberMe();
+
+ void setRememberMe(boolean rememberMe);
+
+ //--- brute force settings
+ boolean isBruteForceProtected();
+ void setBruteForceProtected(boolean value);
+ int getMaxFailureWaitSeconds();
+ void setMaxFailureWaitSeconds(int val);
+ int getWaitIncrementSeconds();
+ void setWaitIncrementSeconds(int val);
+ int getMinimumQuickLoginWaitSeconds();
+ void setMinimumQuickLoginWaitSeconds(int val);
+ long getQuickLoginCheckMilliSeconds();
+ void setQuickLoginCheckMilliSeconds(long val);
+ int getMaxDeltaTimeSeconds();
+ void setMaxDeltaTimeSeconds(int val);
+ int getFailureFactor();
+ void setFailureFactor(int failureFactor);
+ //--- end brute force settings
+
+
+ boolean isVerifyEmail();
+
+ void setVerifyEmail(boolean verifyEmail);
+
+ boolean isResetPasswordAllowed();
+
+ void setResetPasswordAllowed(boolean resetPasswordAllowed);
+
+ int getSsoSessionIdleTimeout();
+ void setSsoSessionIdleTimeout(int seconds);
+
+ int getSsoSessionMaxLifespan();
+ void setSsoSessionMaxLifespan(int seconds);
+
+ int getAccessTokenLifespan();
+
+ void setAccessTokenLifespan(int seconds);
+
+ int getAccessCodeLifespan();
+
+ void setAccessCodeLifespan(int seconds);
+
+ int getAccessCodeLifespanUserAction();
+
+ void setAccessCodeLifespanUserAction(int seconds);
+
+ String getPublicKeyPem();
+
+ void setPublicKeyPem(String publicKeyPem);
+
+ String getPrivateKeyPem();
+
+ void setPrivateKeyPem(String privateKeyPem);
+
+ PublicKey getPublicKey();
+
+ void setPublicKey(PublicKey publicKey);
+
+ PrivateKey getPrivateKey();
+
+ void setPrivateKey(PrivateKey privateKey);
+
+ List<RequiredCredentialModel> getRequiredCredentials();
+
+ void addRequiredCredential(String cred);
+
+ PasswordPolicy getPasswordPolicy();
+
+ void setPasswordPolicy(PasswordPolicy policy);
+
+ List<String> getDefaultRoles();
+
+ void addDefaultRole(String name);
+
+ void updateDefaultRoles(String[] defaultRoles);
+
+ Client findClient(String clientId);
+
+ Map<String, Application> getApplicationNameMap();
+
+ List<Application> getApplications();
+
+ Application addApplication(String id, String name);
+
+ boolean removeApplication(Application application);
+
+ Application getApplicationByName(String name);
+
+ void updateRequiredCredentials(Set<String> creds);
+
+ boolean isSocial();
+
+ void setSocial(boolean social);
+
+ boolean isUpdateProfileOnInitialSocialLogin();
+
+ void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin);
+
+ OAuthClient addOAuthClient(String id, String name);
+
+ OAuthClient getOAuthClient(String name);
+ boolean removeOAuthClient(OAuthClient client);
+
+ List<OAuthClient> getOAuthClients();
+
+ Map<String, String> getSmtpConfig();
+
+ void setSmtpConfig(Map<String, String> smtpConfig);
+
+ Map<String, String> getSocialConfig();
+
+ void setSocialConfig(Map<String, String> socialConfig);
+
+ Map<String, String> getLdapServerConfig();
+
+ void setLdapServerConfig(Map<String, String> ldapServerConfig);
+
+ List<AuthenticationProviderModel> getAuthenticationProviders();
+
+ void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders);
+
+ String getLoginTheme();
+
+ void setLoginTheme(String name);
+
+ String getAccountTheme();
+
+ void setAccountTheme(String name);
+
+ String getAdminTheme();
+
+ void setAdminTheme(String name);
+
+ String getEmailTheme();
+
+ void setEmailTheme(String name);
+
+
+ /**
+ * Time in seconds since epoc
+ *
+ * @return
+ */
+ int getNotBefore();
+
+ void setNotBefore(int notBefore);
+
+ boolean removeRole(Role role);
+
+ boolean isAuditEnabled();
+
+ void setAuditEnabled(boolean enabled);
+
+ long getAuditExpiration();
+
+ void setAuditExpiration(long expiration);
+
+ Set<String> getAuditListeners();
+
+ void setAuditListeners(Set<String> listeners);
+
+ Application getMasterAdminApp();
+
+ void setMasterAdminApp(Application app);
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/RealmProvider.java b/model/hybrid/src/main/java/org/keycloak/models/realms/RealmProvider.java
new file mode 100644
index 0000000..b762d67
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/RealmProvider.java
@@ -0,0 +1,29 @@
+package org.keycloak.models.realms;
+
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface RealmProvider extends Provider {
+
+ Realm createRealm(String name);
+ Realm createRealm(String id, String name);
+
+ Realm getRealm(String id);
+ Realm getRealmByName(String name);
+ List<Realm> getRealms();
+ boolean removeRealm(String id);
+
+ Role getRoleById(String id, String realm);
+ Application getApplicationById(String id, String realm);
+ OAuthClient getOAuthClientById(String id, String realm);
+
+ KeycloakTransaction getTransaction();
+
+ void close();
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/RealmProviderFactory.java b/model/hybrid/src/main/java/org/keycloak/models/realms/RealmProviderFactory.java
new file mode 100755
index 0000000..fb4dea3
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/RealmProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.models.realms;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface RealmProviderFactory extends ProviderFactory<RealmProvider> {
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/RealmSpi.java b/model/hybrid/src/main/java/org/keycloak/models/realms/RealmSpi.java
new file mode 100644
index 0000000..9ec584f
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/RealmSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.realms;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RealmSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "modelRealms";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return RealmProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return RealmProviderFactory.class;
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/Role.java b/model/hybrid/src/main/java/org/keycloak/models/realms/Role.java
new file mode 100755
index 0000000..431d1b6
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/Role.java
@@ -0,0 +1,31 @@
+package org.keycloak.models.realms;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface Role {
+
+ String getId();
+
+ String getName();
+
+ void setName(String name);
+
+ String getDescription();
+
+ void setDescription(String description);
+
+ boolean isComposite();
+
+ void addCompositeRole(Role role);
+
+ void removeCompositeRole(Role role);
+
+ Set<Role> getComposites();
+
+ RoleContainer getContainer();
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/realms/RoleContainer.java b/model/hybrid/src/main/java/org/keycloak/models/realms/RoleContainer.java
new file mode 100755
index 0000000..5d4c02c
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/realms/RoleContainer.java
@@ -0,0 +1,19 @@
+package org.keycloak.models.realms;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface RoleContainer {
+
+ Role getRole(String name);
+
+ Set<Role> getRoles();
+
+ Role addRole(String id, String name);
+
+ boolean removeRole(Role role);
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/sessions/LoginFailure.java b/model/hybrid/src/main/java/org/keycloak/models/sessions/LoginFailure.java
new file mode 100755
index 0000000..f0e7b24
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/sessions/LoginFailure.java
@@ -0,0 +1,29 @@
+package org.keycloak.models.sessions;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface LoginFailure {
+
+ String getUsername();
+
+ int getFailedLoginNotBefore();
+
+ void setFailedLoginNotBefore(int notBefore);
+
+ int getNumFailures();
+
+ void incrementFailures();
+
+ void clearFailures();
+
+ long getLastFailure();
+
+ void setLastFailure(long lastFailure);
+
+ String getLastIPFailure();
+
+ void setLastIPFailure(String ip);
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/sessions/Session.java b/model/hybrid/src/main/java/org/keycloak/models/sessions/Session.java
new file mode 100755
index 0000000..722895d
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/sessions/Session.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.sessions;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface Session {
+
+ String getId();
+
+ void setId(String id);
+
+ String getUser();
+
+ void setUser(String user);
+
+ String getIpAddress();
+
+ void setIpAddress(String ipAddress);
+
+ int getStarted();
+
+ void setStarted(int started);
+
+ int getLastSessionRefresh();
+
+ void setLastSessionRefresh(int seconds);
+
+ void associateClient(String client);
+
+ List<String> getClientAssociations();
+
+ void removeAssociatedClient(String client);
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionProvider.java b/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionProvider.java
new file mode 100644
index 0000000..baf0396
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionProvider.java
@@ -0,0 +1,42 @@
+package org.keycloak.models.sessions;
+
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface SessionProvider extends Provider {
+
+ LoginFailure getUserLoginFailure(String username, String realm);
+
+ LoginFailure addUserLoginFailure(String username, String realm);
+
+ List<LoginFailure> getAllUserLoginFailures(String realm);
+
+ Session createUserSession(String realm, String id, String user, String ipAddress);
+
+ Session getUserSession(String id, String realm);
+
+ List<Session> getUserSessionsByUser(String user, String realm);
+
+ Set<Session> getUserSessionsByClient(String realm, String client);
+
+ int getActiveUserSessions(String realm, String client);
+
+ void removeUserSession(Session session);
+
+ void removeUserSessions(String realm, String user);
+
+ void removeExpiredUserSessions(String realm, long refreshTimeout, long sessionTimeout);
+
+ void removeUserSessions(String realm);
+
+ KeycloakTransaction getTransaction();
+
+ void close();
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionProviderFactory.java b/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionProviderFactory.java
new file mode 100755
index 0000000..08e2051
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.models.sessions;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface SessionProviderFactory extends ProviderFactory<SessionProvider> {
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionSpi.java b/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionSpi.java
new file mode 100644
index 0000000..cf6c7fc
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/sessions/SessionSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.sessions;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class SessionSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "modelSessions";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return SessionProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return SessionProviderFactory.class;
+ }
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/users/Attributes.java b/model/hybrid/src/main/java/org/keycloak/models/users/Attributes.java
new file mode 100644
index 0000000..c341ab0
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/users/Attributes.java
@@ -0,0 +1,12 @@
+package org.keycloak.models.users;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface Attributes {
+
+ String EMAIL_VERIFIED = "keycloak.emailVerified";
+ String TOTP_ENABLED = "keycloak.totpEnabled";
+ String REQUIRED_ACTIONS = "keycloak.requiredActions";
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/users/Credentials.java b/model/hybrid/src/main/java/org/keycloak/models/users/Credentials.java
new file mode 100644
index 0000000..57368bc
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/users/Credentials.java
@@ -0,0 +1,72 @@
+package org.keycloak.models.users;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class Credentials {
+
+ private byte[] salt;
+ private String type;
+ private String value;
+ private String device;
+ private int hashIterations;
+
+ public Credentials(String type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ public Credentials(String type, String value, String device) {
+ this.type = type;
+ this.value = value;
+ this.device = device;
+ }
+
+ public Credentials(String type, byte[] salt, String value, int hashIterations, String device) {
+ this.salt = salt;
+ this.type = type;
+ this.value = value;
+ this.hashIterations = hashIterations;
+ this.device = device;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getDevice() {
+ return device;
+ }
+
+ public void setDevice(String device) {
+ this.device = device;
+ }
+
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public void setSalt(byte[] salt) {
+ this.salt = salt;
+ }
+
+ public int getHashIterations() {
+ return hashIterations;
+ }
+
+ public void setHashIterations(int hashIterations) {
+ this.hashIterations = hashIterations;
+ }
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/users/Feature.java b/model/hybrid/src/main/java/org/keycloak/models/users/Feature.java
new file mode 100644
index 0000000..395fb03
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/users/Feature.java
@@ -0,0 +1,12 @@
+package org.keycloak.models.users;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public enum Feature {
+
+ READ_CREDENTIALS,
+ UPDATE_CREDENTIALS,
+ VERIFY_CREDENTIALS;
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/users/User.java b/model/hybrid/src/main/java/org/keycloak/models/users/User.java
new file mode 100755
index 0000000..90f2880
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/users/User.java
@@ -0,0 +1,58 @@
+package org.keycloak.models.users;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface User {
+
+ public static final String USERNAME = "username";
+ public static final String LAST_NAME = "lastName";
+ public static final String FIRST_NAME = "firstName";
+ public static final String EMAIL = "email";
+
+ String getId();
+
+ boolean isEnabled();
+
+ void setEnabled(boolean enabled);
+
+ String getUsername();
+
+ void setUsername(String username);
+
+ String getFirstName();
+
+ void setFirstName(String firstName);
+
+ String getLastName();
+
+ void setLastName(String lastName);
+
+ String getEmail();
+
+ void setEmail(String email);
+
+ String getAttribute(String name);
+
+ Map<String, String> getAttributes();
+
+ void setAttribute(String name, String value);
+
+ void removeAttribute(String name);
+
+ List<Credentials> getCredentials();
+
+ void updateCredential(Credentials cred);
+
+ Set<String> getRoleMappings();
+
+ void grantRole(String role);
+
+ void deleteRoleMapping(String role);
+
+}
\ No newline at end of file
diff --git a/model/hybrid/src/main/java/org/keycloak/models/users/UserProvider.java b/model/hybrid/src/main/java/org/keycloak/models/users/UserProvider.java
new file mode 100644
index 0000000..de53736
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/users/UserProvider.java
@@ -0,0 +1,46 @@
+package org.keycloak.models.users;
+
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface UserProvider extends Provider {
+
+ KeycloakTransaction getTransaction();
+
+ User addUser(String id, String username, Set<String> roles, String realm);
+
+ boolean removeUser(String name, String realm);
+
+ User getUserById(String id, String realm);
+ User getUserByUsername(String username, String realm);
+ User getUserByEmail(String email, String realm);
+ User getUserByAttribute(String name, String value, String realm);
+
+ List<User> getUsers(String realm);
+ List<User> searchForUser(String search, String realm);
+ List<User> searchForUserByAttributes(Map<String, String> attributes, String realm);
+
+ /**
+ * Returns features supported by the provider. A provider is required to at least support one of verifying credentials
+ * or reading credentials.
+ *
+ * @param feature
+ * @return
+ */
+ boolean supports(Feature feature);
+
+ boolean verifyCredentials(User user, Credentials... credentials);
+
+ void onRealmRemoved(String realm);
+ void onRoleRemoved(String role);
+
+ void close();
+
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/users/UserProviderFactory.java b/model/hybrid/src/main/java/org/keycloak/models/users/UserProviderFactory.java
new file mode 100755
index 0000000..e2f7c46
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/users/UserProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.models.users;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface UserProviderFactory extends ProviderFactory<UserProvider> {
+}
diff --git a/model/hybrid/src/main/java/org/keycloak/models/users/UserSpi.java b/model/hybrid/src/main/java/org/keycloak/models/users/UserSpi.java
new file mode 100644
index 0000000..e3d8e23
--- /dev/null
+++ b/model/hybrid/src/main/java/org/keycloak/models/users/UserSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.users;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "modelUsers";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return UserProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return UserProviderFactory.class;
+ }
+
+}
diff --git a/model/hybrid/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory b/model/hybrid/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory
new file mode 100644
index 0000000..4fdf853
--- /dev/null
+++ b/model/hybrid/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.hybrid.HybridModelProviderFactory
\ No newline at end of file
diff --git a/model/hybrid/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/hybrid/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000..6f3050e
--- /dev/null
+++ b/model/hybrid/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1,3 @@
+org.keycloak.models.realms.RealmSpi
+org.keycloak.models.sessions.SessionSpi
+org.keycloak.models.users.UserSpi
\ No newline at end of file
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ApplicationAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ApplicationAdapter.java
index 31e938e..58fecaf 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ApplicationAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ApplicationAdapter.java
@@ -19,7 +19,7 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
protected ApplicationModel updated;
protected CachedApplication cached;
- public ApplicationAdapter(RealmModel cachedRealm, CachedApplication cached, CacheModelProvider cacheSession, KeycloakCache cache) {
+ public ApplicationAdapter(RealmModel cachedRealm, CachedApplication cached, CacheRealmProvider cacheSession, RealmCache cache) {
super(cachedRealm, cached, cache, cacheSession);
this.cached = cached;
}
@@ -185,6 +185,20 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
}
@Override
+ public boolean hasScope(RoleModel role) {
+ if (super.hasScope(role)) {
+ return true;
+ }
+ Set<RoleModel> roles = getRoles();
+ if (roles.contains(role)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) return true;
+ }
+ return false;
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof ApplicationModel)) return false;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProvider.java
new file mode 100755
index 0000000..8a5f68f
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProvider.java
@@ -0,0 +1,15 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserProvider;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface CacheUserProvider extends UserProvider {
+ UserProvider getDelegate();
+ boolean isEnabled();
+ void setEnabled(boolean enabled);
+ void registerUserInvalidation(RealmModel realm, String id);
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderFactory.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderFactory.java
new file mode 100755
index 0000000..4341ad5
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderFactory.java
@@ -0,0 +1,11 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface CacheUserProviderFactory extends ProviderFactory<CacheUserProvider> {
+
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java
new file mode 100755
index 0000000..6c0fae5
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CacheUserProviderSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "userCache";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return CacheUserProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return CacheUserProviderFactory.class;
+ }
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
index 8be3d82..ae913a1 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
@@ -4,7 +4,6 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserSessionModel;
import org.keycloak.models.cache.entities.CachedClient;
import java.util.HashSet;
@@ -16,12 +15,12 @@ import java.util.Set;
*/
public abstract class ClientAdapter implements ClientModel {
protected CachedClient cachedClient;
- protected CacheModelProvider cacheSession;
+ protected CacheRealmProvider cacheSession;
protected ClientModel updatedClient;
protected RealmModel cachedRealm;
- protected KeycloakCache cache;
+ protected RealmCache cache;
- public ClientAdapter(RealmModel cachedRealm, CachedClient cached, KeycloakCache cache, CacheModelProvider cacheSession) {
+ public ClientAdapter(RealmModel cachedRealm, CachedClient cached, RealmCache cache, CacheRealmProvider cacheSession) {
this.cachedRealm = cachedRealm;
this.cache = cache;
this.cacheSession = cacheSession;
@@ -196,13 +195,4 @@ public abstract class ClientAdapter implements ClientModel {
updatedClient.setNotBefore(notBefore);
}
- public Set<UserSessionModel> getUserSessions() {
- if (updatedClient != null) return updatedClient.getUserSessions();
- return cacheSession.getUserSessions(cachedRealm, this);
- }
-
- public int getActiveUserSessions() {
- if (updatedClient != null) return updatedClient.getActiveUserSessions();
- return cacheSession.getActiveUserSessions(cachedRealm, this);
- }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplication.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplication.java
index 06cc7fe..db0e1c0 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplication.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplication.java
@@ -1,21 +1,15 @@
package org.keycloak.models.cache.entities;
import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.ClientModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ModelProvider;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.cache.KeycloakCache;
+import org.keycloak.models.cache.RealmCache;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -29,7 +23,7 @@ public class CachedApplication extends CachedClient {
private boolean bearerOnly;
private Map<String, String> roles = new HashMap<String, String>();
- public CachedApplication(KeycloakCache cache, ModelProvider delegate, RealmModel realm, ApplicationModel model) {
+ public CachedApplication(RealmCache cache, RealmProvider delegate, RealmModel realm, ApplicationModel model) {
super(cache, delegate, realm, model);
surrogateAuthRequired = model.isSurrogateAuthRequired();
managementUrl = model.getManagementUrl();
@@ -38,7 +32,7 @@ public class CachedApplication extends CachedClient {
bearerOnly = model.isBearerOnly();
for (RoleModel role : model.getRoles()) {
roles.put(role.getName(), role.getId());
- cache.addCachedRole(new CachedApplicationRole(id, role));
+ cache.addCachedRole(new CachedApplicationRole(id, role, realm));
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplicationRole.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplicationRole.java
index 269fba9..da0451c 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplicationRole.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedApplicationRole.java
@@ -1,5 +1,6 @@
package org.keycloak.models.cache.entities;
+import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
/**
@@ -9,8 +10,8 @@ import org.keycloak.models.RoleModel;
public class CachedApplicationRole extends CachedRole {
private final String appId;
- public CachedApplicationRole(String appId, RoleModel model) {
- super(model);
+ public CachedApplicationRole(String appId, RoleModel model, RealmModel realm) {
+ super(model, realm);
this.appId = appId;
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
index 7e62422..8e4b008 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
@@ -1,12 +1,10 @@
package org.keycloak.models.cache.entities;
-import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ModelProvider;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.cache.KeycloakCache;
+import org.keycloak.models.cache.RealmCache;
import java.util.HashSet;
import java.util.Set;
@@ -18,6 +16,7 @@ import java.util.Set;
public class CachedClient {
protected String id;
protected String name;
+ protected String realm;
protected long allowedClaimsMask;
protected Set<String> redirectUris = new HashSet<String>();
protected boolean enabled;
@@ -28,10 +27,11 @@ public class CachedClient {
protected Set<String> scope = new HashSet<String>();
protected Set<String> webOrigins = new HashSet<String>();
- public CachedClient(KeycloakCache cache, ModelProvider delegate, RealmModel realm, ClientModel model) {
+ public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
id = model.getId();
secret = model.getSecret();
name = model.getClientId();
+ this.realm = realm.getId();
enabled = model.isEnabled();
notBefore = model.getNotBefore();
directGrantsOnly = model.isDirectGrantsOnly();
@@ -53,6 +53,10 @@ public class CachedClient {
return name;
}
+ public String getRealm() {
+ return realm;
+ }
+
public long getAllowedClaimsMask() {
return allowedClaimsMask;
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedOAuthClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedOAuthClient.java
index 397010f..0ab872a 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedOAuthClient.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedOAuthClient.java
@@ -1,17 +1,16 @@
package org.keycloak.models.cache.entities;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ModelProvider;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.cache.KeycloakCache;
+import org.keycloak.models.cache.RealmCache;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CachedOAuthClient extends CachedClient {
- public CachedOAuthClient(KeycloakCache cache, ModelProvider delegate, RealmModel realm, OAuthClientModel model) {
+ public CachedOAuthClient(RealmCache cache, RealmProvider delegate, RealmModel realm, OAuthClientModel model) {
super(cache, delegate, realm, model);
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index ffaa6c5..c8748fe 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -2,14 +2,14 @@ package org.keycloak.models.cache.entities;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationProviderModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ModelProvider;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.cache.KeycloakCache;
+import org.keycloak.models.cache.RealmCache;
import java.util.ArrayList;
import java.util.HashMap;
@@ -65,6 +65,7 @@ public class CachedRealm {
private List<RequiredCredentialModel> requiredCredentials = new ArrayList<RequiredCredentialModel>();
private List<AuthenticationProviderModel> authenticationProviders = new ArrayList<AuthenticationProviderModel>();
+ private List<UserFederationProviderModel> federationProviders = new ArrayList<UserFederationProviderModel>();
private Map<String, String> smtpConfig = new HashMap<String, String>();
private Map<String, String> socialConfig = new HashMap<String, String>();
@@ -81,7 +82,7 @@ public class CachedRealm {
public CachedRealm() {
}
- public CachedRealm(KeycloakCache cache, ModelProvider delegate, RealmModel model) {
+ public CachedRealm(RealmCache cache, RealmProvider delegate, RealmModel model) {
id = model.getId();
name = model.getName();
enabled = model.isEnabled();
@@ -121,6 +122,7 @@ public class CachedRealm {
requiredCredentials = model.getRequiredCredentials();
authenticationProviders = model.getAuthenticationProviders();
+ federationProviders = model.getUserFederationProviders();
smtpConfig.putAll(model.getSmtpConfig());
socialConfig.putAll(model.getSocialConfig());
@@ -134,7 +136,7 @@ public class CachedRealm {
for (RoleModel role : model.getRoles()) {
realmRoles.put(role.getName(), role.getId());
- CachedRole cachedRole = new CachedRealmRole(role);
+ CachedRole cachedRole = new CachedRealmRole(role, model);
cache.addCachedRole(cachedRole);
}
@@ -329,4 +331,7 @@ public class CachedRealm {
return auditListeners;
}
+ public List<UserFederationProviderModel> getFederationProviders() {
+ return federationProviders;
+ }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealmRole.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealmRole.java
index b6f6668..e34478e 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealmRole.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealmRole.java
@@ -1,10 +1,8 @@
package org.keycloak.models.cache.entities;
+import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import java.util.HashSet;
-import java.util.Set;
-
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@@ -12,8 +10,8 @@ import java.util.Set;
public class CachedRealmRole extends CachedRole {
- public CachedRealmRole(RoleModel model) {
- super(model);
+ public CachedRealmRole(RoleModel model, RealmModel realm) {
+ super(model, realm);
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java
index ed451e2..d52d0ff 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java
@@ -1,5 +1,6 @@
package org.keycloak.models.cache.entities;
+import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import java.util.HashSet;
@@ -12,15 +13,17 @@ import java.util.Set;
public class CachedRole {
final protected String id;
final protected String name;
+ final protected String realm;
final protected String description;
final protected boolean composite;
final protected Set<String> composites = new HashSet<String>();
- public CachedRole(RoleModel model) {
+ public CachedRole(RoleModel model, RealmModel realm) {
composite = model.isComposite();
description = model.getDescription();
id = model.getId();
name = model.getName();
+ this.realm = realm.getId();
if (composite) {
for (RoleModel child : model.getComposites()) {
composites.add(child.getId());
@@ -37,6 +40,10 @@ public class CachedRole {
return name;
}
+ public String getRealm() {
+ return realm;
+ }
+
public String getDescription() {
return description;
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
index 465f08f..a376012 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
@@ -19,18 +19,16 @@ import java.util.Set;
*/
public class CachedUser {
private String id;
- private String loginName;
- private String usernameKey;
+ private String username;
private String firstName;
private String lastName;
private String email;
- private String emailKey;
private boolean emailVerified;
- private int notBefore;
private List<UserCredentialValueModel> credentials = new LinkedList<UserCredentialValueModel>();
private boolean enabled;
private boolean totp;
private AuthenticationLinkModel authenticationLink;
+ private String federationLink;
private Map<String, String> attributes = new HashMap<String, String>();
private Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
private Set<String> roleMappings = new HashSet<String>();
@@ -38,20 +36,16 @@ public class CachedUser {
public CachedUser(RealmModel realm, UserModel user) {
this.id = user.getId();
- this.loginName = user.getLoginName();
- this.usernameKey = realm.getId() + "." + this.loginName;
+ this.username = user.getUsername();
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.attributes.putAll(user.getAttributes());
this.email = user.getEmail();
- if (this.email != null) {
- this.emailKey = realm.getId() + "." + this.email;
- }
this.emailVerified = user.isEmailVerified();
- this.notBefore = user.getNotBefore();
this.credentials.addAll(user.getCredentialsDirectly());
this.enabled = user.isEnabled();
this.totp = user.isTotp();
+ this.federationLink = user.getFederationLink();
this.requiredActions.addAll(user.getRequiredActions());
this.authenticationLink = user.getAuthenticationLink();
for (RoleModel role : user.getRoleMappings()) {
@@ -63,16 +57,8 @@ public class CachedUser {
return id;
}
- public String getLoginName() {
- return loginName;
- }
-
- public String getUsernameKey() {
- return usernameKey;
- }
-
- public String getEmailKey() {
- return emailKey;
+ public String getUsername() {
+ return username;
}
public String getFirstName() {
@@ -91,10 +77,6 @@ public class CachedUser {
return emailVerified;
}
- public int getNotBefore() {
- return notBefore;
- }
-
public List<UserCredentialValueModel> getCredentials() {
return credentials;
}
@@ -122,4 +104,8 @@ public class CachedUser {
public AuthenticationLinkModel getAuthenticationLink() {
return authenticationLink;
}
+
+ public String getFederationLink() {
+ return federationLink;
+ }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryCacheUserProviderFactory.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryCacheUserProviderFactory.java
new file mode 100755
index 0000000..54974b7
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryCacheUserProviderFactory.java
@@ -0,0 +1,35 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MemoryCacheUserProviderFactory implements CacheUserProviderFactory {
+ protected MemoryUserCache cache;
+
+ @Override
+ public CacheUserProvider create(KeycloakSession session) {
+ return new DefaultCacheUserProvider(cache, session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ config.get("");
+ cache = new MemoryUserCache();
+ int size = config.getInt("maxSize", 10000);
+ cache.setMaxUserCacheSize(size);
+ }
+
+ @Override
+ public void close() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String getId() {
+ return "mem";
+ }
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryUserCache.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryUserCache.java
new file mode 100755
index 0000000..360a02d
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryUserCache.java
@@ -0,0 +1,156 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.models.cache.entities.CachedUser;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MemoryUserCache implements UserCache {
+
+ protected int maxUserCacheSize = 10000;
+ protected volatile boolean enabled = true;
+
+
+ protected class RealmUsers {
+ protected class LRUCache extends LinkedHashMap<String, CachedUser> {
+ public LRUCache() {
+ super(1000, 1.1F, true);
+ }
+
+ @Override
+ public CachedUser put(String key, CachedUser value) {
+ usersByUsername.put(value.getUsername(), value);
+ if (value.getEmail() != null) {
+ usersByEmail.put(value.getEmail(), value);
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public CachedUser remove(Object key) {
+ CachedUser user = super.remove(key);
+ if (user == null) return null;
+ removeUser(user);
+ return user;
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ usersByUsername.clear();
+ usersByEmail.clear();
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<String, CachedUser> eldest) {
+ boolean evict = size() > maxUserCacheSize;
+ if (evict) {
+ removeUser(eldest.getValue());
+ }
+ return evict;
+ }
+
+ private void removeUser(CachedUser value) {
+ usersByUsername.remove(value.getUsername());
+ if (value.getEmail() != null) usersByEmail.remove(value.getEmail());
+ }
+ }
+
+ protected Map<String, CachedUser> usersById = Collections.synchronizedMap(new LRUCache());
+ protected Map<String, CachedUser> usersByUsername = new ConcurrentHashMap<String, CachedUser>();
+ protected Map<String, CachedUser> usersByEmail = new ConcurrentHashMap<String, CachedUser>();
+
+ }
+
+ protected ConcurrentHashMap<String, RealmUsers> realmUsers = new ConcurrentHashMap<String, RealmUsers>();
+
+ public int getMaxUserCacheSize() {
+ return maxUserCacheSize;
+ }
+
+ public void setMaxUserCacheSize(int maxUserCacheSize) {
+ this.maxUserCacheSize = maxUserCacheSize;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ clear();
+ this.enabled = enabled;
+ clear();
+ }
+
+ @Override
+ public CachedUser getCachedUser(String realmId, String id) {
+ if (realmId == null || id == null) return null;
+ RealmUsers users = realmUsers.get(realmId);
+ if (users == null) return null;
+ return users.usersById.get(id);
+ }
+
+ @Override
+ public void invalidateCachedUser(String realmId, CachedUser user) {
+ RealmUsers users = realmUsers.get(realmId);
+ if (users == null) return;
+ users.usersById.remove(user.getId());
+ }
+
+ @Override
+ public void invalidateCachedUserById(String realmId, String id) {
+ RealmUsers users = realmUsers.get(realmId);
+ if (users == null) return;
+ users.usersById.remove(id);
+ }
+
+ @Override
+ public void addCachedUser(String realmId, CachedUser user) {
+ RealmUsers users = realmUsers.get(realmId);
+ if (users == null) {
+ users = new RealmUsers();
+ realmUsers.put(realmId, users);
+ }
+ users.usersById.put(user.getId(), user);
+ }
+
+ @Override
+ public CachedUser getCachedUserByUsername(String realmId, String name) {
+ if (realmId == null || name == null) return null;
+ RealmUsers users = realmUsers.get(realmId);
+ if (users == null) return null;
+ CachedUser user = users.usersByUsername.get(name);
+ if (user == null) return null;
+ users.usersById.get(user.getId()); // refresh cache entry age
+ return user;
+ }
+
+ @Override
+ public CachedUser getCachedUserByEmail(String realmId, String email) {
+ if (realmId == null || email == null) return null;
+ RealmUsers users = realmUsers.get(realmId);
+ if (users == null) return null;
+ CachedUser user = users.usersByEmail.get(email);
+ if (user == null) return null;
+ users.usersById.get(user.getId()); // refresh cache entry age
+ return user;
+ }
+
+ @Override
+ public void invalidateRealmUsers(String realmId) {
+ realmUsers.remove(realmId);
+ }
+
+ @Override
+ public void clear() {
+ realmUsers.clear();
+ }
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
new file mode 100755
index 0000000..e704042
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
@@ -0,0 +1,112 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class NoCacheRealmProvider implements CacheRealmProvider {
+ protected KeycloakSession session;
+ protected RealmProvider delegate;
+
+ public NoCacheRealmProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public RealmProvider getDelegate() {
+ if (delegate != null) return delegate;
+ delegate = session.getProvider(RealmProvider.class);
+ return delegate;
+ }
+
+ @Override
+ public void registerRealmInvalidation(String id) {
+ }
+
+ @Override
+ public void registerApplicationInvalidation(String id) {
+ }
+
+ @Override
+ public void registerRoleInvalidation(String id) {
+ }
+
+ @Override
+ public void registerOAuthClientInvalidation(String id) {
+ }
+
+ @Override
+ public RealmModel createRealm(String name) {
+ return getDelegate().createRealm(name);
+ }
+
+ @Override
+ public RealmModel createRealm(String id, String name) {
+ return getDelegate().createRealm(id, name);
+ }
+
+ @Override
+ public RealmModel getRealm(String id) {
+ return getDelegate().getRealm(id);
+ }
+
+ @Override
+ public RealmModel getRealmByName(String name) {
+ return getDelegate().getRealmByName(name);
+ }
+
+ @Override
+ public List<RealmModel> getRealms() {
+ // we don't cache this for now
+ return getDelegate().getRealms();
+ }
+
+ @Override
+ public boolean removeRealm(String id) {
+ return getDelegate().removeRealm(id);
+ }
+
+ @Override
+ public void close() {
+ if (delegate != null) delegate.close();
+ }
+
+ @Override
+ public RoleModel getRoleById(String id, RealmModel realm) {
+ return getDelegate().getRoleById(id, realm);
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id, RealmModel realm) {
+ return getDelegate().getApplicationById(id, realm);
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
+ return getDelegate().getOAuthClientById(id, realm);
+ }
+
+ @Override
+ public void registerUserInvalidation(String id) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
new file mode 100755
index 0000000..6831a43
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
@@ -0,0 +1,161 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserProvider;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class NoCacheUserProvider implements CacheUserProvider {
+ protected KeycloakSession session;
+ protected UserProvider delegate;
+
+ public NoCacheUserProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ }
+
+ @Override
+ public UserProvider getDelegate() {
+ if (delegate != null) return delegate;
+ delegate = session.getProvider(UserProvider.class);
+ return delegate;
+ }
+
+ @Override
+ public void registerUserInvalidation(RealmModel realm, String id) {
+ }
+
+ @Override
+ public UserModel getUserById(String id, RealmModel realm) {
+ return getDelegate().getUserById(id, realm);
+ }
+
+ @Override
+ public UserModel getUserByUsername(String username, RealmModel realm) {
+ return getDelegate().getUserByUsername(username, realm);
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email, RealmModel realm) {
+ return getDelegate().getUserByEmail(email, realm);
+ }
+
+ @Override
+ public void close() {
+ if (delegate != null) delegate.close();
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+ return getDelegate().getUserBySocialLink(socialLink, realm);
+ }
+
+ @Override
+ public List<UserModel> getUsers(RealmModel realm) {
+ return getDelegate().getUsers(realm);
+ }
+
+ @Override
+ public int getUsersCount(RealmModel realm) {
+ return getDelegate().getUsersCount(realm);
+ }
+
+ @Override
+ public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
+ return getDelegate().getUsers(realm, firstResult, maxResults);
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search, RealmModel realm) {
+ return getDelegate().searchForUser(search, realm);
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
+ return getDelegate().searchForUser(search, realm, firstResult, maxResults);
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+ return getDelegate().searchForUserByAttributes(attributes, realm);
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
+ return getDelegate().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
+ }
+
+ @Override
+ public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+ return getDelegate().getSocialLinks(user, realm);
+ }
+
+ @Override
+ public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
+ return getDelegate().getSocialLink(user, socialProvider, realm);
+ }
+
+ @Override
+ public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles) {
+ return getDelegate().addUser(realm, id, username, addDefaultRoles);
+ }
+
+ @Override
+ public UserModel addUser(RealmModel realm, String username) {
+ return getDelegate().addUser(realm, username);
+ }
+
+ @Override
+ public boolean removeUser(RealmModel realm, UserModel user) {
+ return getDelegate().removeUser(realm, user);
+ }
+
+ @Override
+ public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
+ getDelegate().addSocialLink(realm, user, socialLink);
+ }
+
+ @Override
+ public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
+ return getDelegate().removeSocialLink(realm, user, socialProvider);
+ }
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+ return getDelegate().validCredentials(realm, user, input);
+ }
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
+ return getDelegate().validCredentials(realm, user, input);
+ }
+
+ @Override
+ public void preRemove(RealmModel realm) {
+ getDelegate().preRemove(realm);
+ }
+
+ @Override
+ public void preRemove(RealmModel realm, RoleModel role) {
+ getDelegate().preRemove(realm, role);
+ }
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProviderFactory.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProviderFactory.java
new file mode 100755
index 0000000..1008a00
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProviderFactory.java
@@ -0,0 +1,30 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class NoCacheUserProviderFactory implements CacheUserProviderFactory {
+ @Override
+ public CacheUserProvider create(KeycloakSession session) {
+ return new NoCacheUserProvider(session);
+ }
+
+ @Override
+ public void close() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String getId() {
+ return "none";
+ }
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/OAuthClientAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/OAuthClientAdapter.java
index 2831ea0..50708b9 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/OAuthClientAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/OAuthClientAdapter.java
@@ -12,7 +12,7 @@ public class OAuthClientAdapter extends ClientAdapter implements OAuthClientMode
protected OAuthClientModel updated;
protected CachedOAuthClient cached;
- public OAuthClientAdapter(RealmModel cachedRealm, CachedOAuthClient cached, CacheModelProvider cacheSession, KeycloakCache cache) {
+ public OAuthClientAdapter(RealmModel cachedRealm, CachedOAuthClient cached, CacheRealmProvider cacheSession, RealmCache cache) {
super(cachedRealm, cached, cache, cacheSession);
this.cached = cached;
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index 3b5158c..4d9bf61 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -1,23 +1,17 @@
package org.keycloak.models.cache;
+import org.keycloak.Config;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
-import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.UserCredentialValueModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.models.cache.entities.CachedRealm;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
-import org.keycloak.models.utils.TimeBasedOTP;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -34,13 +28,13 @@ import java.util.Set;
*/
public class RealmAdapter implements RealmModel {
protected CachedRealm cached;
- protected CacheModelProvider cacheSession;
+ protected CacheRealmProvider cacheSession;
protected RealmModel updated;
- protected KeycloakCache cache;
+ protected RealmCache cache;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
- public RealmAdapter(CachedRealm cached, CacheModelProvider cacheSession) {
+ public RealmAdapter(CachedRealm cached, CacheRealmProvider cacheSession) {
this.cached = cached;
this.cacheSession = cacheSession;
}
@@ -379,61 +373,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public boolean validatePassword(UserModel user, String password) {
- for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
- if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
- return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue());
-
- }
- }
- return false;
- }
-
- @Override
- public boolean validateTOTP(UserModel user, String password, String token) {
- if (!validatePassword(user, password)) return false;
- for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
- if (cred.getType().equals(UserCredentialModel.TOTP)) {
- return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
- }
- }
- return false;
- }
-
- @Override
- public UserModel getUser(String name) {
- return cacheSession.getUserByUsername(name, this);
- }
-
- @Override
- public UserModel getUserByEmail(String email) {
- return cacheSession.getUserByEmail(email, this);
- }
-
- @Override
- public UserModel getUserById(String id) {
- return cacheSession.getUserById(id, this);
- }
-
- @Override
- public UserModel addUser(String id, String username) {
- getDelegateForUpdate();
- return updated.addUser(id, username);
- }
-
- @Override
- public UserModel addUser(String username) {
- getDelegateForUpdate();
- return updated.addUser(username);
- }
-
- @Override
- public boolean removeUser(String name) {
- getDelegateForUpdate();
- return updated.removeUser(name);
- }
-
- @Override
public RoleModel getRoleById(String id) {
if (updated != null) return updated.getRoleById(id);
return cacheSession.getRoleById(id, this);
@@ -544,36 +483,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
- if (updated != null) return updated.getUserBySocialLink(socialLink);
- return cacheSession.getUserBySocialLink(socialLink, this);
- }
-
- @Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user) {
- if (updated != null) return updated.getSocialLinks(user);
- return cacheSession.getSocialLinks(user, this);
- }
-
- @Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider) {
- if (updated != null) return updated.getSocialLink(user, socialProvider);
- return cacheSession.getSocialLink(user, socialProvider, this);
- }
-
- @Override
- public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
- getDelegateForUpdate();
- updated.addSocialLink(user, socialLink);
- }
-
- @Override
- public boolean removeSocialLink(UserModel user, String socialProvider) {
- getDelegateForUpdate();
- return updated.removeSocialLink(user, socialProvider);
- }
-
- @Override
public boolean isSocial() {
if (updated != null) return updated.isSocial();
return cached.isSocial();
@@ -598,24 +507,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public List<UserModel> getUsers() {
- if (updated != null) return updated.getUsers();
- return cacheSession.getUsers(this);
- }
-
- @Override
- public List<UserModel> searchForUser(String search) {
- if (updated != null) return updated.searchForUser(search);
- return cacheSession.searchForUser(search, this);
- }
-
- @Override
- public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
- if (updated != null) return updated.searchForUserByAttributes(attributes);
- return cacheSession.searchForUserByAttributes(attributes, this);
- }
-
- @Override
public OAuthClientModel addOAuthClient(String name) {
getDelegateForUpdate();
OAuthClientModel client = updated.addOAuthClient(name);
@@ -715,6 +606,18 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public List<UserFederationProviderModel> getUserFederationProviders() {
+ if (updated != null) return updated.getUserFederationProviders();
+ return cached.getFederationProviders();
+ }
+
+ @Override
+ public void setUserFederationProviders(List<UserFederationProviderModel> providers) {
+ getDelegateForUpdate();
+ updated.setUserFederationProviders(providers);
+ }
+
+ @Override
public String getLoginTheme() {
if (updated != null) return updated.getLoginTheme();
return cached.getLoginTheme();
@@ -819,8 +722,7 @@ public class RealmAdapter implements RealmModel {
@Override
public ApplicationModel getMasterAdminApp() {
- if (updated != null) return updated.getMasterAdminApp();
- return getApplicationById(cached.getMasterAdminApp());
+ return cacheSession.getRealm(Config.getAdminRealm()).getApplicationById(cached.getMasterAdminApp());
}
@Override
@@ -880,83 +782,6 @@ public class RealmAdapter implements RealmModel {
return getOAuthClientById(id);
}
-
- @Override
- public UsernameLoginFailureModel getUserLoginFailure(String username) {
- if (updated != null) return updated.getUserLoginFailure(username);
- return cacheSession.getUserLoginFailure(username, this);
- }
-
- @Override
- public UsernameLoginFailureModel addUserLoginFailure(String username) {
- if (updated != null) return updated.addUserLoginFailure(username);
- return cacheSession.addUserLoginFailure(username, this);
- }
-
- @Override
- public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
- if (updated != null) return updated.getAllUserLoginFailures();
- return cacheSession.getAllUserLoginFailures(this);
- }
-
- @Override
- public UserSessionModel createUserSession(UserModel user, String ipAddress) {
- if (updated != null) return updated.createUserSession(user, ipAddress);
- return cacheSession.createUserSession(this, user, ipAddress);
- }
-
- @Override
- public UserSessionModel getUserSession(String id) {
- if (updated != null) return updated.getUserSession(id);
- return cacheSession.getUserSession(id, this);
- }
-
- @Override
- public List<UserSessionModel> getUserSessions(UserModel user) {
- if (updated != null) return updated.getUserSessions(user);
- return cacheSession.getUserSessions(user, this);
- }
-
- @Override
- public void removeUserSession(UserSessionModel session) {
- if (updated != null) {
- updated.removeUserSession(session);
- } else {
- cacheSession.removeUserSession(session);
-
- }
- }
-
- @Override
- public void removeUserSessions(UserModel user) {
- if (updated != null) {
- updated.removeUserSessions(user);
- } else {
- cacheSession.removeUserSessions(this, user);
-
- }
- }
-
- @Override
- public void removeExpiredUserSessions() {
- if (updated != null) {
- updated.removeExpiredUserSessions();
- } else {
- cacheSession.removeExpiredUserSessions(this);
-
- }
- }
-
- @Override
- public void removeUserSessions() {
- if (updated != null) {
- updated.removeUserSessions();
- } else {
- cacheSession.removeUserSessions(this);
-
- }
- }
-
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java
index bbf2766..18f6422 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java
@@ -19,11 +19,11 @@ public class RoleAdapter implements RoleModel {
protected RoleModel updated;
protected CachedRole cached;
- protected KeycloakCache cache;
- protected CacheModelProvider cacheSession;
+ protected RealmCache cache;
+ protected CacheRealmProvider cacheSession;
protected RealmModel realm;
- public RoleAdapter(CachedRole cached, KeycloakCache cache, CacheModelProvider session, RealmModel realm) {
+ public RoleAdapter(CachedRole cached, RealmCache cache, CacheRealmProvider session, RealmModel realm) {
this.cached = cached;
this.cache = cache;
this.cacheSession = session;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
index 727244c..9f4ffe3 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
@@ -2,6 +2,7 @@ package org.keycloak.models.cache;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
@@ -22,21 +23,21 @@ import java.util.Set;
public class UserAdapter implements UserModel {
protected UserModel updated;
protected CachedUser cached;
- protected KeycloakCache cache;
- protected CacheModelProvider cacheSession;
+ protected CacheUserProvider userProviderCache;
+ protected KeycloakSession keycloakSession;
protected RealmModel realm;
- public UserAdapter(CachedUser cached, KeycloakCache cache, CacheModelProvider session, RealmModel realm) {
+ public UserAdapter(CachedUser cached, CacheUserProvider userProvider, KeycloakSession keycloakSession, RealmModel realm) {
this.cached = cached;
- this.cache = cache;
- this.cacheSession = session;
+ this.userProviderCache = userProvider;
+ this.keycloakSession = keycloakSession;
this.realm = realm;
}
protected void getDelegateForUpdate() {
if (updated == null) {
- cacheSession.registerUserInvalidation(getId());
- updated = cacheSession.getDelegate().getUserById(getId(), realm);
+ userProviderCache.registerUserInvalidation(realm, getId());
+ updated = userProviderCache.getDelegate().getUserById(getId(), realm);
if (updated == null) throw new IllegalStateException("Not found in database");
}
}
@@ -47,15 +48,15 @@ public class UserAdapter implements UserModel {
}
@Override
- public String getLoginName() {
- if (updated != null) return updated.getLoginName();
- return cached.getLoginName();
+ public String getUsername() {
+ if (updated != null) return updated.getUsername();
+ return cached.getUsername();
}
@Override
- public void setLoginName(String loginName) {
+ public void setUsername(String username) {
getDelegateForUpdate();
- updated.setLoginName(loginName);
+ updated.setUsername(username);
}
@Override
@@ -173,18 +174,6 @@ public class UserAdapter implements UserModel {
}
@Override
- public int getNotBefore() {
- if (updated != null) return updated.getNotBefore();
- return cached.getNotBefore();
- }
-
- @Override
- public void setNotBefore(int notBefore) {
- getDelegateForUpdate();
- updated.setNotBefore(notBefore);
- }
-
- @Override
public void updateCredential(UserCredentialModel cred) {
getDelegateForUpdate();
updated.updateCredential(cred);
@@ -215,6 +204,18 @@ public class UserAdapter implements UserModel {
}
@Override
+ public String getFederationLink() {
+ if (updated != null) return updated.getFederationLink();
+ return cached.getFederationLink();
+ }
+
+ @Override
+ public void setFederationLink(String link) {
+ getDelegateForUpdate();
+ updated.setFederationLink(link);
+ }
+
+ @Override
public Set<RoleModel> getRealmRoleMappings() {
if (updated != null) return updated.getRealmRoleMappings();
Set<RoleModel> roleMappings = getRoleMappings();
@@ -269,7 +270,13 @@ public class UserAdapter implements UserModel {
if (updated != null) return updated.getRoleMappings();
Set<RoleModel> roles = new HashSet<RoleModel>();
for (String id : cached.getRoleMappings()) {
- roles.add(cacheSession.getRoleById(id, realm));
+ RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
+ if (roleById == null) {
+ // chance that role was removed, so just delete to persistence and get user invalidated
+ getDelegateForUpdate();
+ return updated.getRoleMappings();
+ }
+ roles.add(roleById);
}
return roles;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserCache.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserCache.java
new file mode 100755
index 0000000..61f1188
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserCache.java
@@ -0,0 +1,29 @@
+package org.keycloak.models.cache;
+
+import org.keycloak.models.cache.entities.CachedUser;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserCache {
+ void clear();
+
+ CachedUser getCachedUser(String realmId, String id);
+
+ void invalidateCachedUser(String realmId, CachedUser user);
+
+ void addCachedUser(String realmId, CachedUser user);
+
+ CachedUser getCachedUserByUsername(String realmId, String name);
+
+ CachedUser getCachedUserByEmail(String realmId, String name);
+
+ void invalidateCachedUserById(String realmId, String id);
+
+ void invalidateRealmUsers(String realmId);
+
+ boolean isEnabled();
+
+ void setEnabled(boolean enabled);
+}
diff --git a/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory b/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory
new file mode 100755
index 0000000..82ddea9
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory
@@ -0,0 +1,2 @@
+org.keycloak.models.cache.MemoryCacheRealmProviderFactory
+org.keycloak.models.cache.NoCacheRealmProviderFactory
\ No newline at end of file
diff --git a/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheUserProviderFactory b/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheUserProviderFactory
new file mode 100755
index 0000000..438f9e1
--- /dev/null
+++ b/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheUserProviderFactory
@@ -0,0 +1,2 @@
+org.keycloak.models.cache.MemoryCacheUserProviderFactory
+org.keycloak.models.cache.NoCacheUserProviderFactory
diff --git a/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index eb9e116..d793d8a 100755
--- a/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/model/invalidation-cache/model-adapters/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1 +1,2 @@
-org.keycloak.models.cache.CacheModelProviderSpi
\ No newline at end of file
+org.keycloak.models.cache.CacheRealmProviderSpi
+org.keycloak.models.cache.CacheUserProviderSpi
\ No newline at end of file
model/jpa/pom.xml 63(+12 -51)
diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
index 987abd2..52c7db5 100755
--- a/model/jpa/pom.xml
+++ b/model/jpa/pom.xml
@@ -37,6 +37,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-invalidation-cache-model</artifactId>
<version>${project.version}</version>
<scope>test</scope>
@@ -53,6 +58,13 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-sessions-mem</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+
+ </dependency>
+ <dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
@@ -71,26 +83,6 @@
</exclusion>
</exclusions>
</dependency>
-
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-tests</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-tests</artifactId>
- <version>${project.version}</version>
- <classifier>tests</classifier>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <scope>test</scope>
- </dependency>
-
</dependencies>
<build>
<plugins>
@@ -102,37 +94,6 @@
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
-
- <!-- Test jar used in export-import -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <executions>
- <execution>
- <id>package-tests-jar</id>
- <phase>package</phase>
- <goals>
- <goal>test-jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <executions>
- <execution>
- <id>default-test</id>
- <configuration>
- <dependenciesToScan>
- <dependency>org.keycloak:keycloak-model-tests</dependency>
- </dependenciesToScan>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
</plugins>
</build>
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
index 0ebe017..f83599c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
@@ -2,12 +2,13 @@ package org.keycloak.models.jpa;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.jpa.entities.*;
+import org.keycloak.models.jpa.entities.ApplicationEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.ScopeMappingEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
@@ -25,10 +26,12 @@ import java.util.Set;
public class ApplicationAdapter extends ClientAdapter implements ApplicationModel {
protected EntityManager em;
+ protected KeycloakSession session;
protected ApplicationEntity applicationEntity;
- public ApplicationAdapter(RealmModel realm, EntityManager em, ApplicationEntity applicationEntity) {
+ public ApplicationAdapter(RealmModel realm, EntityManager em, KeycloakSession session, ApplicationEntity applicationEntity) {
super(realm, applicationEntity, em);
+ this.session = session;
this.realm = realm;
this.em = em;
this.applicationEntity = applicationEntity;
@@ -135,15 +138,15 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
}
if (!roleModel.getContainer().equals(this)) return false;
+ session.users().preRemove(getRealm(), roleModel);
RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
if (!role.isApplicationRole()) return false;
applicationEntity.getRoles().remove(role);
applicationEntity.getDefaultRoles().remove(role);
- em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", role).executeUpdate();
- em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
- em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
+ em.createNativeQuery("delete from COMPOSITE_ROLE where CHILD_ROLE = :role").setParameter("role", role).executeUpdate();
+ em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", role).executeUpdate();
role.setApplication(null);
em.flush();
em.remove(role);
@@ -164,6 +167,20 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
}
@Override
+ public boolean hasScope(RoleModel role) {
+ if (super.hasScope(role)) {
+ return true;
+ }
+ Set<RoleModel> roles = getRoles();
+ if (roles.contains(role)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) return true;
+ }
+ return false;
+ }
+
+ @Override
public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
Set<RoleModel> roleMappings = client.getScopeMappings();
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 2e99658..6a07b5e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -4,16 +4,12 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserSessionModel;
import org.keycloak.models.jpa.entities.ClientEntity;
-import org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.jpa.entities.ScopeMappingEntity;
import javax.persistence.EntityManager;
-import javax.persistence.Query;
import javax.persistence.TypedQuery;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -154,31 +150,6 @@ public abstract class ClientAdapter implements ClientModel {
}
@Override
- public int getActiveUserSessions() {
- Query query = em.createNamedQuery("getActiveClientSessions");
- query.setParameter("clientId", getId());
- Object count = query.getSingleResult();
- return ((Number)count).intValue();
- }
-
- @Override
- public Set<UserSessionModel> getUserSessions() {
- Set<UserSessionModel> list = new HashSet<UserSessionModel>();
- TypedQuery<ClientUserSessionAssociationEntity> query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
- String id = getId();
- query.setParameter("clientId", id);
- List<ClientUserSessionAssociationEntity> results = query.getResultList();
- for (ClientUserSessionAssociationEntity entity : results) {
- list.add(new UserSessionAdapter(em, realm, entity.getSession()));
- }
- return list;
- }
-
- public void deleteUserSessionAssociation() {
- em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", getId()).executeUpdate();
- }
-
- @Override
public Set<RoleModel> getRealmScopeMappings() {
Set<RoleModel> roleMappings = getScopeMappings();
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
index 8327bd0..c34761f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
@@ -1,25 +1,15 @@
package org.keycloak.models.jpa.entities;
import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
-import javax.persistence.ElementCollection;
+import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
-import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
-import javax.persistence.OneToOne;
import javax.persistence.Table;
-import javax.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.hibernate.annotations.GenericGenerator;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -28,16 +18,23 @@ import org.hibernate.annotations.GenericGenerator;
@Entity
public class ApplicationEntity extends ClientEntity {
+ @Column(name="SURROGATE_AUTH_REQUIRED")
private boolean surrogateAuthRequired;
+
+ @Column(name="BASE_URL")
private String baseUrl;
+
+ @Column(name="MANAGEMENT_URL")
private String managementUrl;
+
+ @Column(name="BEARER_ONLY")
private boolean bearerOnly;
@OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "application")
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
- @JoinTable(name="ApplicationDefaultRoles")
+ @JoinTable(name="APPLICATION_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="APPLICATION_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
public boolean isSurrogateAuthRequired() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
old mode 100644
new mode 100755
index e6ae8d4..e21b4fc
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
@@ -1,36 +1,39 @@
package org.keycloak.models.jpa.entities;
+import javax.persistence.Column;
import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
+import javax.persistence.FetchType;
import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
-import javax.persistence.OneToOne;
-
-import org.hibernate.annotations.GenericGenerator;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.io.Serializable;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
+@NamedQueries({
+ @NamedQuery(name="deleteAuthenticationLinksByRealm", query="delete from AuthenticationLinkEntity authLink where authLink.user IN (select u from UserEntity u where u.realmId=:realmId)")
+})
+@Table(name="AUTHENTICATION_LINK")
@Entity
+@IdClass(AuthenticationLinkEntity.Key.class)
public class AuthenticationLinkEntity {
@Id
- @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
- @GeneratedValue(generator = "keycloak_generator")
- private String id;
-
+ @Column(name="AUTH_PROVIDER")
protected String authProvider;
+ @Column(name="AUTH_USER_ID")
protected String authUserId;
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
+ @Id
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name="USER_ID")
+ protected UserEntity user;
public String getAuthProvider() {
return authProvider;
@@ -47,4 +50,56 @@ public class AuthenticationLinkEntity {
public void setAuthUserId(String authUserId) {
this.authUserId = authUserId;
}
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public static class Key implements Serializable {
+
+ protected UserEntity user;
+
+ protected String authProvider;
+
+ public Key() {
+ }
+
+ public Key(UserEntity user, String authProvider) {
+ this.user = user;
+ this.authProvider = authProvider;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public String getAuthProvider() {
+ return authProvider;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (authProvider != null ? !authProvider.equals(key.authProvider) : key.authProvider != null) return false;
+ if (user != null ? !user.getId().equals(key.user != null ? key.user.getId() : null) : key.user != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = user != null ? user.getId().hashCode() : 0;
+ result = 31 * result + (authProvider != null ? authProvider.hashCode() : 0);
+ return result;
+ }
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java
old mode 100644
new mode 100755
index 2ce4c12..89cfe49
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java
@@ -1,49 +1,54 @@
package org.keycloak.models.jpa.entities;
-import java.util.Map;
-
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
+import javax.persistence.FetchType;
import javax.persistence.Id;
+import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
-
-import org.hibernate.annotations.GenericGenerator;
+import java.io.Serializable;
+import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@Entity
-@Table(name="AuthProviderEntity")
+@Table(name="AUTH_PROVIDER")
+@IdClass(AuthenticationProviderEntity.Key.class)
public class AuthenticationProviderEntity {
@Id
- @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
- @GeneratedValue(generator = "keycloak_generator")
- protected String id;
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "REALM_ID")
+ protected RealmEntity realm;
+ @Id
+ @Column(name="PROVIDER_NAME")
private String providerName;
+ @Column(name="PASSWORD_UPDATE_SUPPORTED")
private boolean passwordUpdateSupported;
+ @Column(name="PRIORITY")
private int priority;
@ElementCollection
- @MapKeyColumn(name="name")
- @Column(name="value")
- @CollectionTable(name="AuthProviderEntity_cfg", joinColumns = {
- @JoinColumn(name = "AuthProviderEntity_id")
- })
+ @MapKeyColumn(name="NAME")
+ @Column(name="VALUE")
+ @CollectionTable(name="AUTH_PROVIDER_CONFIG", joinColumns = {
+ @JoinColumn(name="REALM_ID", referencedColumnName = "REALM_ID"),
+ @JoinColumn(name="AUTH_PROVIDER_NAME", referencedColumnName = "PROVIDER_NAME")})
private Map<String, String> config;
- public String getId() {
- return id;
+ public RealmEntity getRealm() {
+ return realm;
}
- public void setId(String id) {
- this.id = id;
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
}
public String getProviderName() {
@@ -77,4 +82,48 @@ public class AuthenticationProviderEntity {
public void setConfig(Map<String, String> config) {
this.config = config;
}
+
+ public static class Key implements Serializable {
+
+ protected RealmEntity realm;
+
+ protected String providerName;
+
+ public Key() {
+ }
+
+ public Key(RealmEntity realm, String providerName) {
+ this.realm = realm;
+ this.providerName = providerName;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (providerName != null ? !providerName.equals(key.providerName) : key.providerName != null) return false;
+ if (realm != null ? !realm.getId().equals(key.realm != null ? key.realm.getId() : null) : key.realm != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = realm != null ? realm.getId().hashCode() : 0;
+ result = 31 * result + (providerName != null ? providerName.hashCode() : 0);
+ return result;
+ }
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index 9367d25..2f8625b 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -1,20 +1,15 @@
package org.keycloak.models.jpa.entities;
-import org.hibernate.annotations.GenericGenerator;
-
-import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
-import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import java.util.HashSet;
@@ -26,27 +21,36 @@ import java.util.Set;
*/
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
-@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"realm", "name"})})
+@Table(name="CLIENT", uniqueConstraints = {@UniqueConstraint(columnNames = {"REALM_ID", "NAME"})})
public abstract class ClientEntity {
@Id
+ @Column(name="ID", length = 36)
private String id;
- @Column(name = "name")
+ @Column(name = "NAME")
private String name;
+ @Column(name="ENABLED")
private boolean enabled;
+ @Column(name="SECRET")
private String secret;
+ @Column(name="ALLOWED_CLAIMS_MASK")
private long allowedClaimsMask;
+ @Column(name="NOT_BEFORE")
private int notBefore;
+ @Column(name="PUBLIC_CLIENT")
private boolean publicClient;
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "realm")
+ @JoinColumn(name = "REALM_ID")
protected RealmEntity realm;
@ElementCollection
- @CollectionTable
+ @Column(name="VALUE")
+ @CollectionTable(name = "WEB_ORIGINS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
protected Set<String> webOrigins = new HashSet<String>();
+
@ElementCollection
- @CollectionTable
+ @Column(name="VALUE")
+ @CollectionTable(name = "REDIRECT_URIS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
protected Set<String> redirectUris = new HashSet<String>();
public RealmEntity getRealm() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
index e18c2f8..d67402e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
@@ -1,37 +1,56 @@
package org.keycloak.models.jpa.entities;
+import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
-
-import org.hibernate.annotations.GenericGenerator;
+import javax.persistence.Table;
+import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
- @NamedQuery(name="credentialByUserAndType", query="select cred from CredentialEntity cred where cred.user = :user and cred.type = :type")
+ @NamedQuery(name="credentialByUserAndType", query="select cred from CredentialEntity cred where cred.user = :user and cred.type = :type"),
+ @NamedQuery(name="deleteCredentialsByRealm", query="delete from CredentialEntity cred where cred.user IN (select u from UserEntity u where u.realmId=:realmId)")
+
})
+@Table(name="CREDENTIAL")
@Entity
public class CredentialEntity {
@Id
- @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
- @GeneratedValue(generator = "keycloak_generator")
+ @Column(name="ID", length = 36)
protected String id;
+ @Column(name="TYPE")
protected String type;
+ @Column(name="VALUE")
protected String value;
+ @Column(name="DEVICE")
protected String device;
+ @Column(name="SALT")
protected byte[] salt;
+ @Column(name="HASH_ITERATIONS")
+ protected int hashIterations;
@ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name="USER_ID")
protected UserEntity user;
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
public String getValue() {
return value;
}
@@ -48,14 +67,6 @@ public class CredentialEntity {
this.type = type;
}
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
public String getDevice() {
return device;
}
@@ -80,5 +91,11 @@ public class CredentialEntity {
this.salt = salt;
}
+ public int getHashIterations() {
+ return hashIterations;
+ }
+ public void setHashIterations(int hashIterations) {
+ this.hashIterations = hashIterations;
+ }
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/FederationProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/FederationProviderEntity.java
new file mode 100755
index 0000000..830065c
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/FederationProviderEntity.java
@@ -0,0 +1,85 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.Table;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+@Entity
+@Table(name="FEDERATION_PROVIDER")
+public class FederationProviderEntity {
+
+ @Id
+ @Column(name="ID", length = 36)
+ protected String id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "REALM_ID")
+ protected RealmEntity realm;
+
+ @Column(name="PROVIDER_NAME")
+ private String providerName;
+ @Column(name="PRIORITY")
+ private int priority;
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable(name="FEDERATION_PROVIDER_CONFIG")
+ private Map<String, String> config;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public Map<String, String> getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
index dbdf080..7fac887 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
@@ -1,6 +1,8 @@
package org.keycloak.models.jpa.entities;
+import javax.persistence.Column;
import javax.persistence.Entity;
+import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
@@ -15,6 +17,7 @@ import javax.persistence.NamedQuery;
})
@Entity
public class OAuthClientEntity extends ClientEntity {
+ @Column(name="DIRECT_GRANTS_ONLY")
protected boolean directGrantsOnly;
public boolean isDirectGrantsOnly() {
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 8fd8a56..b7af403 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
@@ -8,12 +8,14 @@ import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
+import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
+import javax.persistence.Table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -26,6 +28,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
+@Table(name="REALM")
@Entity
@NamedQueries({
@NamedQuery(name="getAllRealms", query="select realm from RealmEntity realm"),
@@ -33,95 +36,130 @@ import java.util.Set;
})
public class RealmEntity {
@Id
+ @Column(name="ID", length = 36)
protected String id;
- @Column(unique = true)
+ @Column(name="NAME", unique = true)
protected String name;
+ @Column(name="ENABLED")
protected boolean enabled;
+ @Column(name="SSL_NOT_REQUIRED")
protected boolean sslNotRequired;
+ @Column(name="REGISTRATION_ALLOWED")
protected boolean registrationAllowed;
+ @Column(name="PASSWORD_CRED_GRANT_ALLOWED")
protected boolean passwordCredentialGrantAllowed;
+ @Column(name="VERIFY_EMAIL")
protected boolean verifyEmail;
+ @Column(name="RESET_PASSWORD_ALLOWED")
protected boolean resetPasswordAllowed;
+ @Column(name="SOCIAL")
protected boolean social;
+ @Column(name="REMEMBER_ME")
protected boolean rememberMe;
//--- brute force settings
+ @Column(name="BRUTE_FORCE_PROTECTED")
protected boolean bruteForceProtected;
+ @Column(name="MAX_FAILURE_WAIT")
protected int maxFailureWaitSeconds;
+ @Column(name="MINIMUM_QUICK_LOGIN_WAIT")
protected int minimumQuickLoginWaitSeconds;
+ @Column(name="WAIT_INCREMENT_SECONDS")
protected int waitIncrementSeconds;
+ @Column(name="QUICK_LOGIN_CHECK")
protected long quickLoginCheckMilliSeconds;
+ @Column(name="MAX_DELTA_TIME")
protected int maxDeltaTimeSeconds;
+ @Column(name="FAILURE_FACTOR")
protected int failureFactor;
//--- end brute force settings
- @Column(name="updateProfileOnInitSocLogin")
+ @Column(name="UPDATE_PROFILE_ON_SOC_LOGIN")
protected boolean updateProfileOnInitialSocialLogin;
+ @Column(name="PASSWORD_POLICY")
protected String passwordPolicy;
+ @Column(name="SSO_IDLE_TIMEOUT")
private int ssoSessionIdleTimeout;
+ @Column(name="SSO_MAX_LIFESPAN")
private int ssoSessionMaxLifespan;
+ @Column(name="ACCESS_TOKEN_LIFESPAN")
protected int accessTokenLifespan;
+ @Column(name="ACCESS_CODE_LIFESPAN")
protected int accessCodeLifespan;
+ @Column(name="USER_ACTION_LIFESPAN")
protected int accessCodeLifespanUserAction;
+ @Column(name="NOT_BEFORE")
protected int notBefore;
- @Column(length = 2048)
+ @Column(name="PUBLIC_KEY", length = 2048)
protected String publicKeyPem;
- @Column(length = 2048)
+ @Column(name="PRIVATE_KEY", length = 2048)
protected String privateKeyPem;
+ @Column(name="LOGIN_THEME")
protected String loginTheme;
+ @Column(name="ACCOUNT_THEME")
protected String accountTheme;
+ @Column(name="ADMIN_THEME")
protected String adminTheme;
+ @Column(name="EMAIL_THEME")
protected String emailTheme;
- @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
- @JoinTable(name="User_RequiredCreds")
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
- @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
- @JoinTable(name="AuthProviders")
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="FED_PROVIDERS")
+ List<FederationProviderEntity> federationProviders = new ArrayList<FederationProviderEntity>();
+
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="REALM_APPLICATION", joinColumns={ @JoinColumn(name="APPLICATION_ID") }, inverseJoinColumns={ @JoinColumn(name="REALM_ID") })
Collection<ApplicationEntity> applications = new ArrayList<ApplicationEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@ElementCollection
- @MapKeyColumn(name="name")
- @Column(name="value")
- @CollectionTable
+ @MapKeyColumn(name="NAME")
+ @Column(name="VALUE")
+ @CollectionTable(name="REALM_SMTP_CONFIG", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Map<String, String> smtpConfig = new HashMap<String, String>();
@ElementCollection
- @MapKeyColumn(name="name")
- @Column(name="value")
- @CollectionTable
+ @MapKeyColumn(name="NAME")
+ @Column(name="VALUE")
+ @CollectionTable(name="REALM_SOCIAL_CONFIG", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Map<String, String> socialConfig = new HashMap<String, String>();
@ElementCollection
- @MapKeyColumn(name="name")
- @Column(name="value")
- @CollectionTable
+ @MapKeyColumn(name="NAME")
+ @Column(name="VALUE")
+ @CollectionTable(name="REALM_LDAP_CONFIG", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Map<String, String> ldapServerConfig = new HashMap<String, String>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
- @JoinTable(name="RealmDefaultRoles")
+ @JoinTable(name="REALM_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="REALM_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
protected Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
+ @Column(name="AUDIT_ENABLED")
protected boolean auditEnabled;
+ @Column(name="AUDIT_EXPIRATION")
protected long auditExpiration;
@ElementCollection
+ @Column(name="VALUE")
+ @CollectionTable(name="REALM_AUDIT_LISTENERS", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Set<String> auditListeners= new HashSet<String>();
@OneToOne
+ @JoinColumn(name="MASTER_ADMIN_APP")
protected ApplicationEntity masterAdminApp;
public String getId() {
@@ -475,5 +513,12 @@ public class RealmEntity {
this.masterAdminApp = masterAdminApp;
}
+ public List<FederationProviderEntity> getFederationProviders() {
+ return federationProviders;
+ }
+
+ public void setFederationProviders(List<FederationProviderEntity> federationProviders) {
+ this.federationProviders = federationProviders;
+ }
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java
index 9487022..7e4245c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java
@@ -1,33 +1,45 @@
package org.keycloak.models.jpa.entities;
+import javax.persistence.Column;
import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
+import javax.persistence.FetchType;
import javax.persistence.Id;
-
-import org.hibernate.annotations.GenericGenerator;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
+@Table(name="REALM_REQUIRED_CREDENTIAL")
@Entity
+@IdClass(RequiredCredentialEntity.Key.class)
public class RequiredCredentialEntity {
+
@Id
- @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
- @GeneratedValue(generator = "keycloak_generator")
- protected String id;
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "REALM_ID")
+ protected RealmEntity realm;
+ @Id
+ @Column(name = "TYPE")
protected String type;
+ @Column(name = "INPUT")
protected boolean input;
+ @Column(name = "SECRET")
protected boolean secret;
+ @Column(name = "FORM_LABEL")
protected String formLabel;
- public String getId() {
- return id;
+ public RealmEntity getRealm() {
+ return realm;
}
- public void setId(String id) {
- this.id = id;
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
}
public String getType() {
@@ -61,4 +73,48 @@ public class RequiredCredentialEntity {
public void setFormLabel(String formLabel) {
this.formLabel = formLabel;
}
+
+ public static class Key implements Serializable {
+
+ protected RealmEntity realm;
+
+ protected String type;
+
+ public Key() {
+ }
+
+ public Key(RealmEntity realm, String type) {
+ this.realm = realm;
+ this.type = type;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (realm != null ? !realm.getId().equals(key.realm != null ? key.realm.getId() : null) : key.realm != null) return false;
+ if (type != null ? !type.equals(key.type) : key.type != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = realm != null ? realm.getId().hashCode() : 0;
+ result = 31 * result + (type != null ? type.hashCode() : 0);
+ return result;
+ }
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
index d3fc6df..a17f8e5 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
@@ -3,10 +3,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@@ -18,15 +15,13 @@ import javax.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.Collection;
-import org.hibernate.annotations.GenericGenerator;
-
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
-@Table(uniqueConstraints = {
- @UniqueConstraint(columnNames = { "name", "appRealmConstraint" })
+@Table(name="KEYCLOAK_ROLE", uniqueConstraints = {
+ @UniqueConstraint(columnNames = { "NAME", "APP_REALM_CONSTRAINT" })
})
@NamedQueries({
@NamedQuery(name="getAppRoleByName", query="select role from RoleEntity role where role.name = :name and role.application = :application"),
@@ -35,31 +30,35 @@ import org.hibernate.annotations.GenericGenerator;
public class RoleEntity {
@Id
- @Column(name="id")
+ @Column(name="id", length = 36)
private String id;
+ @Column(name = "NAME")
private String name;
+ @Column(name = "DESCRIPTION")
private String description;
// hax! couldn't get constraint to work properly
+ @Column(name = "REALM_ID")
private String realmId;
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "realm")
+ @JoinColumn(name = "REALM")
private RealmEntity realm;
- @Column(name="applicationRole")
+ @Column(name="APPLICATION_ROLE")
private boolean applicationRole;
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "application")
+ @JoinColumn(name = "APPLICATION")
private ApplicationEntity application;
// Hack to ensure that either name+application or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
+ @Column(name="APP_REALM_CONSTRAINT", length = 36)
private String appRealmConstraint;
@ManyToMany(fetch = FetchType.LAZY, cascade = {})
- @JoinTable(name = "CompositeRole", joinColumns = @JoinColumn(name = "composite"), inverseJoinColumns = @JoinColumn(name = "childRole"))
+ @JoinTable(name = "COMPOSITE_ROLE", joinColumns = @JoinColumn(name = "COMPOSITE"), inverseJoinColumns = @JoinColumn(name = "CHILD_ROLE"))
private Collection<RoleEntity> compositeRoles = new ArrayList<RoleEntity>();
public String getId() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java
index b45b69b..93d375d 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java
@@ -1,15 +1,16 @@
package org.keycloak.models.jpa.entities;
-import org.hibernate.annotations.GenericGenerator;
-
+import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
+import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -18,29 +19,25 @@ import javax.persistence.NamedQuery;
@NamedQueries({
@NamedQuery(name="hasScope", query="select m from ScopeMappingEntity m where m.client = :client and m.role = :role"),
@NamedQuery(name="clientScopeMappings", query="select m from ScopeMappingEntity m where m.client = :client"),
- @NamedQuery(name="clientScopeMappingIds", query="select m.role.id from ScopeMappingEntity m where m.client = :client")
+ @NamedQuery(name="clientScopeMappingIds", query="select m.role.id from ScopeMappingEntity m where m.client = :client"),
+ @NamedQuery(name="deleteScopeMappingByRole", query="delete from ScopeMappingEntity where role = :role"),
+ @NamedQuery(name="deleteScopeMappingByClient", query="delete from ScopeMappingEntity where client = :client")
})
+@Table(name="SCOPE_MAPPING")
@Entity
+@IdClass(ScopeMappingEntity.Key.class)
public class ScopeMappingEntity {
+
@Id
- @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
- @GeneratedValue(generator = "keycloak_generator")
- protected String id;
@ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "CLIENT_ID")
protected ClientEntity client;
+ @Id
@ManyToOne(fetch= FetchType.LAZY)
- @JoinColumn(name="roleId")
+ @JoinColumn(name="ROLE_ID")
protected RoleEntity role;
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
public ClientEntity getClient() {
return client;
}
@@ -57,4 +54,47 @@ public class ScopeMappingEntity {
this.role = role;
}
+ public static class Key implements Serializable {
+
+ protected ClientEntity client;
+
+ protected RoleEntity role;
+
+ public Key() {
+ }
+
+ public Key(ClientEntity client, RoleEntity role) {
+ this.client = client;
+ this.role = role;
+ }
+
+ public ClientEntity getClient() {
+ return client;
+ }
+
+ public RoleEntity getRole() {
+ return role;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
+ if (role != null ? !role.getId().equals(key.role != null ? key.role.getId() : null) : key.role != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = client != null ? client.getId().hashCode() : 0;
+ result = 31 * result + (role != null ? role.getId().hashCode() : 0);
+ return result;
+ }
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java
index a1de3af..de33d6c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java
@@ -1,14 +1,16 @@
package org.keycloak.models.jpa.entities;
+import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
-
-import org.hibernate.annotations.GenericGenerator;
+import javax.persistence.Table;
+import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -17,33 +19,31 @@ import org.hibernate.annotations.GenericGenerator;
@NamedQueries({
@NamedQuery(name="findSocialLinkByUser", query="select link from SocialLinkEntity link where link.user = :user"),
@NamedQuery(name="findSocialLinkByUserAndProvider", query="select link from SocialLinkEntity link where link.user = :user and link.socialProvider = :socialProvider"),
- @NamedQuery(name="findUserByLinkAndRealm", query="select link.user from SocialLinkEntity link where link.realm = :realm and link.socialProvider = :socialProvider and link.socialUserId = :socialUserId")
+ @NamedQuery(name="findUserByLinkAndRealm", query="select link.user from SocialLinkEntity link where link.realmId = :realmId and link.socialProvider = :socialProvider and link.socialUserId = :socialUserId"),
+ @NamedQuery(name="deleteSocialLinkByRealm", query="delete from SocialLinkEntity social where social.user IN (select u from UserEntity u where realmId=:realmId)"),
+ @NamedQuery(name="deleteSocialLinkByUser", query="delete from SocialLinkEntity social where social.user = :user")
})
+@Table(name="USER_SOCIAL_LINK")
@Entity
+@IdClass(SocialLinkEntity.Key.class)
public class SocialLinkEntity {
- @Id
- @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
- @GeneratedValue(generator = "keycloak_generator")
- private String id;
+ @Id
@ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "USER_ID")
private UserEntity user;
- @ManyToOne(fetch = FetchType.LAZY)
- protected RealmEntity realm;
+ @Column(name = "REALM_ID")
+ protected String realmId;
+ @Id
+ @Column(name = "SOCIAL_PROVIDER")
protected String socialProvider;
+ @Column(name = "SOCIAL_USER_ID")
protected String socialUserId;
+ @Column(name = "SOCIAL_USERNAME")
protected String socialUsername;
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
public UserEntity getUser() {
return user;
}
@@ -76,11 +76,56 @@ public class SocialLinkEntity {
this.socialUsername = socialUsername;
}
- public RealmEntity getRealm() {
- return realm;
+ public String getRealmId() {
+ return realmId;
}
- public void setRealm(RealmEntity realm) {
- this.realm = realm;
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
}
+
+ public static class Key implements Serializable {
+
+ protected UserEntity user;
+
+ protected String socialProvider;
+
+ public Key() {
+ }
+
+ public Key(UserEntity user, String socialProvider) {
+ this.user = user;
+ this.socialProvider = socialProvider;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public String getSocialProvider() {
+ return socialProvider;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (socialProvider != null ? !socialProvider.equals(key.socialProvider) : key.socialProvider != null)
+ return false;
+ if (user != null ? !user.getId().equals(key.user != null ? key.user.getId() : null) : key.user != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = user != null ? user.getId().hashCode() : 0;
+ result = 31 * result + (socialProvider != null ? socialProvider.hashCode() : 0);
+ return result;
+ }
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java
new file mode 100755
index 0000000..a861276
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java
@@ -0,0 +1,105 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="deleteUserAttributesByRealm", query="delete from UserAttributeEntity attr where attr.user IN (select u from UserEntity u where u.realmId=:realmId)")
+})
+@Table(name="USER_ATTRIBUTE")
+@Entity
+@IdClass(UserAttributeEntity.Key.class)
+public class UserAttributeEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "USER_ID")
+ protected UserEntity user;
+
+ @Id
+ @Column(name = "NAME")
+ protected String name;
+ @Column(name = "VALUE")
+ protected String value;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public static class Key implements Serializable {
+
+ protected UserEntity user;
+
+ protected String name;
+
+ public Key() {
+ }
+
+ public Key(UserEntity user, String name) {
+ this.user = user;
+ this.name = name;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (name != null ? !name.equals(key.name) : key.name != null) return false;
+ if (user != null ? !user.getId().equals(key.user != null ? key.user.getId() : null) : key.user != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = user != null ? user.getId().hashCode() : 0;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+ }
+
+}
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 3b765b4..31e139a 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
@@ -1,6 +1,5 @@
package org.keycloak.models.jpa.entities;
-import org.hibernate.annotations.GenericGenerator;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -10,7 +9,6 @@ import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@@ -21,7 +19,6 @@ import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -34,52 +31,62 @@ import java.util.Set;
* @version $Revision: 1 $
*/
@NamedQueries({
- @NamedQuery(name="getRealmUserById", query="select u from UserEntity u where u.id = :id and u.realm = :realm"),
- @NamedQuery(name="getRealmUserByLoginName", query="select u from UserEntity u where u.loginName = :loginName and u.realm = :realm"),
- @NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realm = :realm"),
- @NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realm = :realm"),
- @NamedQuery(name="getRealmUserByFirstLastName", query="select u from UserEntity u where u.firstName = :first and u.lastName = :last and u.realm = :realm")
+ @NamedQuery(name="getAllUsersByRealm", query="select u from UserEntity u where u.realmId = :realmId order by u.username"),
+ @NamedQuery(name="searchForUser", query="select u from UserEntity u where u.realmId = :realmId and ( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search ) order by u.username"),
+ @NamedQuery(name="getRealmUserById", query="select u from UserEntity u where u.id = :id and u.realmId = :realmId"),
+ @NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realmId = :realmId"),
+ @NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realmId = :realmId"),
+ @NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realmId = :realmId"),
+ @NamedQuery(name="getRealmUserByFirstLastName", query="select u from UserEntity u where u.firstName = :first and u.lastName = :last and u.realmId = :realmId"),
+ @NamedQuery(name="getRealmUserCount", query="select count(u) from UserEntity u where u.realmId = :realmId"),
+ @NamedQuery(name="deleteUsersByRealm", query="delete from UserEntity u where u.realmId = :realmId")
})
@Entity
-@Table(uniqueConstraints = {
- @UniqueConstraint(columnNames = { "realm", "loginName" }),
- @UniqueConstraint(columnNames = { "realm", "emailConstraint" })
+@Table(name="USER_ENTITY", uniqueConstraints = {
+ @UniqueConstraint(columnNames = { "REALM_ID", "USERNAME" }),
+ @UniqueConstraint(columnNames = { "REALM_ID", "EMAIL_CONSTRAINT" })
})
public class UserEntity {
@Id
+ @Column(name="ID", length = 36)
protected String id;
- protected String loginName;
+ @Column(name = "USERNAME")
+ protected String username;
+ @Column(name = "FIRST_NAME")
protected String firstName;
+ @Column(name = "LAST_NAME")
protected String lastName;
+ @Column(name = "EMAIL")
protected String email;
+ @Column(name = "ENABLED")
protected boolean enabled;
+ @Column(name = "TOTP")
protected boolean totp;
+ @Column(name = "EMAIL_VERIFIED")
protected boolean emailVerified;
- protected int notBefore;
// Hack just to workaround the fact that on MS-SQL you can't have unique constraint with multiple NULL values TODO: Find better solution (like unique index with 'where' but that's proprietary)
+ @Column(name = "EMAIL_CONSTRAINT")
protected String emailConstraint = KeycloakModelUtils.generateId();
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "realm")
- protected RealmEntity realm;
+ @Column(name = "REALM_ID")
+ protected String realmId;
- @ElementCollection
- @MapKeyColumn(name="name")
- @Column(name="value")
- @CollectionTable
- protected Map<String, String> attributes = new HashMap<String, String>();
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
+ protected Collection<UserAttributeEntity> attributes = new ArrayList<UserAttributeEntity>();
- @ElementCollection
- @CollectionTable
- protected Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
+ protected Collection<UserRequiredActionEntity> requiredActions = new ArrayList<UserRequiredActionEntity>();
- @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
- @OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
- protected AuthenticationLinkEntity authenticationLink;
+ @Column(name="federation_link")
+ protected String federationLink;
+
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
+ protected Collection<AuthenticationLinkEntity> authenticationLink;
public String getId() {
return id;
@@ -89,12 +96,12 @@ public class UserEntity {
this.id = id;
}
- public String getLoginName() {
- return loginName;
+ public String getUsername() {
+ return username;
}
- public void setLoginName(String loginName) {
- this.loginName = loginName;
+ public void setUsername(String username) {
+ this.username = username;
}
public String getFirstName() {
@@ -154,28 +161,28 @@ public class UserEntity {
this.emailVerified = emailVerified;
}
- public Map<String, String> getAttributes() {
+ public Collection<UserAttributeEntity> getAttributes() {
return attributes;
}
- public void setAttributes(Map<String, String> attributes) {
+ public void setAttributes(Collection<UserAttributeEntity> attributes) {
this.attributes = attributes;
}
- public Set<UserModel.RequiredAction> getRequiredActions() {
+ public Collection<UserRequiredActionEntity> getRequiredActions() {
return requiredActions;
}
- public void setRequiredActions(Set<UserModel.RequiredAction> requiredActions) {
+ public void setRequiredActions(Collection<UserRequiredActionEntity> requiredActions) {
this.requiredActions = requiredActions;
}
- public RealmEntity getRealm() {
- return realm;
+ public String getRealmId() {
+ return realmId;
}
- public void setRealm(RealmEntity realm) {
- this.realm = realm;
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
}
public Collection<CredentialEntity> getCredentials() {
@@ -186,19 +193,19 @@ public class UserEntity {
this.credentials = credentials;
}
- public AuthenticationLinkEntity getAuthenticationLink() {
+ public Collection<AuthenticationLinkEntity> getAuthenticationLink() {
return authenticationLink;
}
- public void setAuthenticationLink(AuthenticationLinkEntity authenticationLink) {
+ public void setAuthenticationLink(Collection<AuthenticationLinkEntity> authenticationLink) {
this.authenticationLink = authenticationLink;
}
- public int getNotBefore() {
- return notBefore;
+ public String getFederationLink() {
+ return federationLink;
}
- public void setNotBefore(int notBefore) {
- this.notBefore = notBefore;
+ public void setFederationLink(String federationLink) {
+ this.federationLink = federationLink;
}
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRequiredActionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRequiredActionEntity.java
new file mode 100755
index 0000000..d2161e9
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRequiredActionEntity.java
@@ -0,0 +1,98 @@
+package org.keycloak.models.jpa.entities;
+
+import org.keycloak.models.UserModel;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="deleteUserRequiredActionsByRealm", query="delete from UserRequiredActionEntity action where action.user IN (select u from UserEntity u where u.realmId=:realmId)")
+})
+@Entity
+@Table(name="USER_REQUIRED_ACTION")
+@IdClass(UserRequiredActionEntity.Key.class)
+public class UserRequiredActionEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name="USER_ID")
+ protected UserEntity user;
+
+ @Id
+ @Column(name="ACTION")
+ protected UserModel.RequiredAction action;
+
+ public UserModel.RequiredAction getAction() {
+ return action;
+ }
+
+ public void setAction(UserModel.RequiredAction action) {
+ this.action = action;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public static class Key implements Serializable {
+
+ protected UserEntity user;
+
+ protected UserModel.RequiredAction action;
+
+ public Key() {
+ }
+
+ public Key(UserEntity user, UserModel.RequiredAction action) {
+ this.user = user;
+ this.action = action;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public UserModel.RequiredAction getAction() {
+ return action;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (action != key.action) return false;
+ if (user != null ? !user.getId().equals(key.user != null ? key.user.getId() : null) : key.user != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = user != null ? user.getId().hashCode() : 0;
+ result = 31 * result + (action != null ? action.hashCode() : 0);
+ return result;
+ }
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java
index 647acea..608847b 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java
@@ -1,19 +1,101 @@
package org.keycloak.models.jpa.entities;
+import javax.persistence.Column;
import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
- @NamedQuery(name="userHasRole", query="select m from UserRoleMappingEntity m where m.user = :user and m.role = :role"),
+ @NamedQuery(name="userHasRole", query="select m from UserRoleMappingEntity m where m.user = :user and m.roleId = :roleId"),
@NamedQuery(name="userRoleMappings", query="select m from UserRoleMappingEntity m where m.user = :user"),
- @NamedQuery(name="userRoleMappingIds", query="select m.role.id from UserRoleMappingEntity m where m.user = :user")
+ @NamedQuery(name="userRoleMappingIds", query="select m.roleId from UserRoleMappingEntity m where m.user = :user"),
+ @NamedQuery(name="deleteUserRoleMappingsByRealm", query="delete from UserRoleMappingEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId)"),
+ @NamedQuery(name="deleteUserRoleMappingsByRole", query="delete from UserRoleMappingEntity m where m.roleId = :roleId"),
+ @NamedQuery(name="deleteUserRoleMappingsByUser", query="delete from UserRoleMappingEntity m where m.user = :user")
+
})
+@Table(name="USER_ROLE_MAPPING")
@Entity
-public class UserRoleMappingEntity extends AbstractRoleMappingEntity {
+@IdClass(UserRoleMappingEntity.Key.class)
+public class UserRoleMappingEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name="USER_ID")
+ protected UserEntity user;
+
+ @Id
+ @Column(name = "ROLE_ID")
+ protected String roleId;
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public String getRoleId() {
+ return roleId;
+ }
+
+ public void setRoleId(String roleId) {
+ this.roleId = roleId;
+ }
+
+
+ public static class Key implements Serializable {
+
+ protected UserEntity user;
+
+ protected String roleId;
+
+ public Key() {
+ }
+
+ public Key(UserEntity user, String roleId) {
+ this.user = user;
+ this.roleId = roleId;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public String getRoleId() {
+ return roleId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (!roleId.equals(key.roleId)) return false;
+ if (!user.equals(key.user)) return false;
+
+ return true;
+ }
+ @Override
+ public int hashCode() {
+ int result = user.hashCode();
+ result = 31 * result + roleId.hashCode();
+ return result;
+ }
+ }
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
new file mode 100755
index 0000000..a80485b
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -0,0 +1,130 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.jpa.entities.ApplicationEntity;
+import org.keycloak.models.jpa.entities.OAuthClientEntity;
+import org.keycloak.models.jpa.entities.RealmEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaRealmProvider implements RealmProvider {
+ private final KeycloakSession session;
+ protected EntityManager em;
+
+ public JpaRealmProvider(KeycloakSession session, EntityManager em) {
+ this.session = session;
+ this.em = em;
+ }
+
+ @Override
+ public RealmModel createRealm(String name) {
+ return createRealm(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RealmModel createRealm(String id, String name) {
+ RealmEntity realm = new RealmEntity();
+ realm.setName(name);
+ realm.setId(id);
+ em.persist(realm);
+ em.flush();
+ return new RealmAdapter(session, em, realm);
+ }
+
+ @Override
+ public RealmModel getRealm(String id) {
+ RealmEntity realm = em.find(RealmEntity.class, id);
+ if (realm == null) return null;
+ return new RealmAdapter(session, em, realm);
+ }
+
+ @Override
+ public List<RealmModel> getRealms() {
+ TypedQuery<RealmEntity> query = em.createNamedQuery("getAllRealms", RealmEntity.class);
+ List<RealmEntity> entities = query.getResultList();
+ List<RealmModel> realms = new ArrayList<RealmModel>();
+ for (RealmEntity entity : entities) {
+ realms.add(new RealmAdapter(session, em, entity));
+ }
+ return realms;
+ }
+
+ @Override
+ public RealmModel getRealmByName(String name) {
+ TypedQuery<RealmEntity> query = em.createNamedQuery("getRealmByName", RealmEntity.class);
+ query.setParameter("name", name);
+ List<RealmEntity> entities = query.getResultList();
+ if (entities.size() == 0) return null;
+ if (entities.size() > 1) throw new IllegalStateException("Should not be more than one realm with same name");
+ RealmEntity realm = query.getResultList().get(0);
+ if (realm == null) return null;
+ return new RealmAdapter(session, em, realm);
+ }
+
+ @Override
+ public boolean removeRealm(String id) {
+ RealmEntity realm = em.find(RealmEntity.class, id);
+ if (realm == null) {
+ return false;
+ }
+
+ RealmAdapter adapter = new RealmAdapter(session, em, realm);
+ session.users().preRemove(adapter);
+ for (ApplicationEntity a : new LinkedList<ApplicationEntity>(realm.getApplications())) {
+ adapter.removeApplication(a.getId());
+ }
+
+ for (OAuthClientModel oauth : adapter.getOAuthClients()) {
+ adapter.removeOAuthClient(oauth.getId());
+ }
+
+ em.remove(realm);
+ return true;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public RoleModel getRoleById(String id, RealmModel realm) {
+ RoleEntity entity = em.find(RoleEntity.class, id);
+ if (entity == null) return null;
+ if (!realm.getId().equals(entity.getRealmId())) return null;
+ return new RoleAdapter(realm, em, entity);
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id, RealmModel realm) {
+ ApplicationEntity app = em.find(ApplicationEntity.class, id);
+
+ // Check if application belongs to this realm
+ if (app == null || !realm.getId().equals(app.getRealm().getId())) return null;
+ return new ApplicationAdapter(realm, em, session, app);
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
+ OAuthClientEntity client = em.find(OAuthClientEntity.class, id);
+
+ // Check if client belongs to this realm
+ if (client == null || !realm.getId().equals(client.getRealm().getId())) return null;
+ return new OAuthClientAdapter(realm, client, em);
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProviderFactory.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProviderFactory.java
new file mode 100755
index 0000000..7e090c2
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProviderFactory.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.RealmProviderFactory;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaRealmProviderFactory implements RealmProviderFactory {
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public String getId() {
+ return "jpa";
+ }
+
+ @Override
+ public RealmProvider create(KeycloakSession session) {
+ EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
+ return new JpaRealmProvider(session, em);
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
new file mode 100755
index 0000000..b3e5bf7
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -0,0 +1,327 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserProvider;
+import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
+import org.keycloak.models.jpa.entities.SocialLinkEntity;
+import org.keycloak.models.jpa.entities.UserEntity;
+import org.keycloak.models.utils.CredentialValidation;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaUserProvider implements UserProvider {
+ private final KeycloakSession session;
+ protected EntityManager em;
+
+ public JpaUserProvider(KeycloakSession session, EntityManager em) {
+ this.session = session;
+ this.em = em;
+ }
+
+ @Override
+ public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles) {
+ if (id == null) {
+ id = KeycloakModelUtils.generateId();
+ }
+
+ UserEntity entity = new UserEntity();
+ entity.setId(id);
+ entity.setUsername(username);
+ entity.setRealmId(realm.getId());
+ em.persist(entity);
+ em.flush();
+ UserModel userModel = new UserAdapter(realm, em, entity);
+
+ if (addDefaultRoles) {
+ for (String r : realm.getDefaultRoles()) {
+ userModel.grantRole(realm.getRole(r));
+ }
+
+ for (ApplicationModel application : realm.getApplications()) {
+ for (String r : application.getDefaultRoles()) {
+ userModel.grantRole(application.getRole(r));
+ }
+ }
+ }
+
+ return userModel;
+ }
+
+ @Override
+ public UserModel addUser(RealmModel realm, String username) {
+ return addUser(realm, KeycloakModelUtils.generateId(), username, true);
+ }
+
+ @Override
+ public boolean removeUser(RealmModel realm, UserModel user) {
+ UserEntity userEntity = em.find(UserEntity.class, user.getId());
+ if (userEntity == null) return false;
+ removeUser(userEntity);
+ return true;
+ }
+
+ private void removeUser(UserEntity user) {
+ em.createNamedQuery("deleteUserRoleMappingsByUser").setParameter("user", user).executeUpdate();
+ em.createNamedQuery("deleteSocialLinkByUser").setParameter("user", user).executeUpdate();
+ if (user.getAuthenticationLink() != null) {
+ for (AuthenticationLinkEntity l : user.getAuthenticationLink()) {
+ em.remove(l);
+ }
+ }
+ em.remove(user);
+ }
+
+ @Override
+ public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
+ SocialLinkEntity entity = new SocialLinkEntity();
+ entity.setRealmId(realm.getId());
+ entity.setSocialProvider(socialLink.getSocialProvider());
+ entity.setSocialUserId(socialLink.getSocialUserId());
+ entity.setSocialUsername(socialLink.getSocialUsername());
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ entity.setUser(userEntity);
+ em.persist(entity);
+ em.flush();
+ }
+
+ @Override
+ public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
+ SocialLinkEntity entity = findSocialLink(user, socialProvider);
+ if (entity != null) {
+ em.remove(entity);
+ em.flush();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+
+ @Override
+ public void preRemove(RealmModel realm) {
+ int num = em.createNamedQuery("deleteUserRoleMappingsByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteUserRequiredActionsByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteSocialLinkByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteCredentialsByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteUserAttributesByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteAuthenticationLinksByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteUsersByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ }
+
+ @Override
+ public void preRemove(RealmModel realm, RoleModel role) {
+ em.createNamedQuery("deleteUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate();
+ }
+
+ @Override
+ public UserModel getUserById(String id, RealmModel realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserById", UserEntity.class);
+ query.setParameter("id", id);
+ query.setParameter("realmId", realm.getId());
+ List<UserEntity> entities = query.getResultList();
+ if (entities.size() == 0) return null;
+ return new UserAdapter(realm, em, entities.get(0));
+ }
+
+ @Override
+ public UserModel getUserByUsername(String username, RealmModel realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+ query.setParameter("username", username);
+ query.setParameter("realmId", realm.getId());
+ List<UserEntity> results = query.getResultList();
+ if (results.size() == 0) return null;
+ return new UserAdapter(realm, em, results.get(0));
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email, RealmModel realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
+ query.setParameter("email", email);
+ query.setParameter("realmId", realm.getId());
+ List<UserEntity> results = query.getResultList();
+ return results.isEmpty() ? null : new UserAdapter(realm, em, results.get(0));
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("findUserByLinkAndRealm", UserEntity.class);
+ query.setParameter("realmId", realm.getId());
+ query.setParameter("socialProvider", socialLink.getSocialProvider());
+ query.setParameter("socialUserId", socialLink.getSocialUserId());
+ List<UserEntity> results = query.getResultList();
+ if (results.isEmpty()) {
+ return null;
+ } else if (results.size() > 1) {
+ throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() +
+ ", socialUserId=" + socialLink.getSocialUserId() + ", results=" + results);
+ } else {
+ UserEntity user = results.get(0);
+ return new UserAdapter(realm, em, user);
+ }
+ }
+
+ @Override
+ public List<UserModel> getUsers(RealmModel realm) {
+ return getUsers(realm, -1, -1);
+ }
+
+ @Override
+ public int getUsersCount(RealmModel realm) {
+ // TODO: named query?
+ Object count = em.createNamedQuery("getRealmUserCount")
+ .setParameter("realmId", realm.getId())
+ .getSingleResult();
+ return ((Number)count).intValue();
+ }
+
+ @Override
+ public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("getAllUsersByRealm", UserEntity.class);
+ query.setParameter("realmId", realm.getId());
+ if (firstResult != -1) {
+ query.setFirstResult(firstResult);
+ }
+ if (maxResults != -1) {
+ query.setMaxResults(maxResults);
+ }
+ List<UserEntity> results = query.getResultList();
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search, RealmModel realm) {
+ return searchForUser(search, realm, -1, -1);
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("searchForUser", UserEntity.class);
+ query.setParameter("realmId", realm.getId());
+ query.setParameter("search", "%" + search.toLowerCase() + "%");
+ if (firstResult != -1) {
+ query.setFirstResult(firstResult);
+ }
+ if (maxResults != -1) {
+ query.setMaxResults(maxResults);
+ }
+ List<UserEntity> results = query.getResultList();
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+ return searchForUserByAttributes(attributes, realm, -1, -1);
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
+ StringBuilder builder = new StringBuilder("select u from UserEntity u");
+ boolean first = true;
+ for (Map.Entry<String, String> entry : attributes.entrySet()) {
+ String attribute = null;
+ if (entry.getKey().equals(UserModel.USERNAME)) {
+ attribute = "lower(username)";
+ } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
+ attribute = "lower(firstName)";
+ } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
+ attribute = "lower(lastName)";
+ } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
+ attribute = "lower(email)";
+ }
+ if (attribute == null) continue;
+ if (first) {
+ first = false;
+ builder.append(" where realm = :realm");
+ } else {
+ builder.append(" and ");
+ }
+ builder.append(attribute).append(" like '%").append(entry.getValue().toLowerCase()).append("%'");
+ }
+ builder.append(" order by u.username");
+ String q = builder.toString();
+ TypedQuery<UserEntity> query = em.createQuery(q, UserEntity.class);
+ query.setParameter("realmId", realm.getId());
+ if (firstResult != -1) {
+ query.setFirstResult(firstResult);
+ }
+ if (maxResults != -1) {
+ query.setMaxResults(maxResults);
+ }
+ List<UserEntity> results = query.getResultList();
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
+ TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ query.setParameter("socialProvider", socialProvider);
+ List<SocialLinkEntity> results = query.getResultList();
+ return results.size() > 0 ? results.get(0) : null;
+ }
+
+
+ @Override
+ public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+ TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUser", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ List<SocialLinkEntity> results = query.getResultList();
+ Set<SocialLinkModel> set = new HashSet<SocialLinkModel>();
+ for (SocialLinkEntity entity : results) {
+ set.add(new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()));
+ }
+ return set;
+ }
+
+ @Override
+ public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
+ SocialLinkEntity entity = findSocialLink(user, socialProvider);
+ return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()) : null;
+ }
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+ return CredentialValidation.validCredentials(realm, user, input);
+ }
+
+ @Override
+ public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
+ return CredentialValidation.validCredentials(realm, user, input);
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProviderFactory.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProviderFactory.java
new file mode 100755
index 0000000..3a52f12
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProviderFactory.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.UserProvider;
+import org.keycloak.models.UserProviderFactory;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaUserProviderFactory implements UserProviderFactory {
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public String getId() {
+ return "jpa";
+ }
+
+ @Override
+ public UserProvider create(KeycloakSession session) {
+ EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
+ return new JpaUserProvider(session, em);
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
index c890c9f..2279163 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
@@ -1,14 +1,10 @@
package org.keycloak.models.jpa;
-import org.keycloak.models.ApplicationModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.OAuthClientEntity;
import javax.persistence.EntityManager;
-import java.util.HashSet;
-import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java
old mode 100644
new mode 100755
index dfa5053..62a9426
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java
@@ -1,12 +1,11 @@
package org.keycloak.models.jpa;
import org.hibernate.exception.ConstraintViolationException;
-import org.keycloak.models.ModelException;
import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
-import javax.persistence.PersistenceException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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 a985f3d..e65e377 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
@@ -1,44 +1,26 @@
package org.keycloak.models.jpa;
-import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.jpa.entities.FederationProviderEntity;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ModelDuplicateException;
-import org.keycloak.models.RoleContainerModel;
-import org.keycloak.models.UserCredentialValueModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.models.jpa.entities.ApplicationEntity;
-import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
import org.keycloak.models.jpa.entities.AuthenticationProviderEntity;
import org.keycloak.models.jpa.entities.OAuthClientEntity;
import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
-import org.keycloak.models.jpa.entities.ScopeMappingEntity;
-import org.keycloak.models.jpa.entities.SocialLinkEntity;
-import org.keycloak.models.jpa.entities.UserEntity;
-import org.keycloak.models.jpa.entities.UserSessionEntity;
-import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
-import org.keycloak.models.jpa.entities.UsernameLoginFailureEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
-import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.OAuthClientModel;
-import org.keycloak.models.PasswordPolicy;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.RequiredCredentialModel;
-import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
-import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.utils.TimeBasedOTP;
-import org.keycloak.util.Time;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
-
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
@@ -48,13 +30,10 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.*;
-
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@@ -370,6 +349,7 @@ public class RealmAdapter implements RealmModel {
public void addRequiredCredential(RequiredCredentialModel model) {
RequiredCredentialEntity entity = new RequiredCredentialEntity();
+ entity.setRealm(realm);
entity.setInput(model.isInput());
entity.setSecret(model.isSecret());
entity.setType(model.getType());
@@ -424,87 +404,6 @@ public class RealmAdapter implements RealmModel {
@Override
- public UserModel getUser(String name) {
- return session.getUserByUsername(name, this);
- }
-
- @Override
- public UsernameLoginFailureModel getUserLoginFailure(String username) {
- return session.getUserLoginFailure(username, this);
- }
-
- @Override
- public UsernameLoginFailureModel addUserLoginFailure(String username) {
- return session.addUserLoginFailure(username, this);
- }
-
- @Override
- public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
- return session.getAllUserLoginFailures(this);
- }
-
- @Override
- public UserModel getUserByEmail(String email) {
- return session.getUserByEmail(email, this);
- }
-
- @Override
- public UserModel getUserById(String id) {
- return session.getUserById(id, this);
- }
-
- @Override
- public UserModel addUser(String username) {
- return this.addUser(KeycloakModelUtils.generateId(), username);
- }
-
- @Override
- public UserModel addUser(String id, String username) {
- UserEntity entity = new UserEntity();
- entity.setId(id);
- entity.setLoginName(username);
- entity.setRealm(realm);
- em.persist(entity);
- em.flush();
- UserModel userModel = new UserAdapter(this, em, entity);
-
- for (String r : getDefaultRoles()) {
- userModel.grantRole(getRole(r));
- }
-
- for (ApplicationModel application : getApplications()) {
- for (String r : application.getDefaultRoles()) {
- userModel.grantRole(application.getRole(r));
- }
- }
-
- return userModel;
- }
-
- @Override
- public boolean removeUser(String name) {
- TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByLoginName", UserEntity.class);
- query.setParameter("loginName", name);
- query.setParameter("realm", realm);
- List<UserEntity> results = query.getResultList();
- if (results.size() == 0) return false;
- removeUser(results.get(0));
- return true;
- }
-
- private void removeUser(UserEntity user) {
- em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
- em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
-
- em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
- em.createQuery("delete from " + SocialLinkEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
- if (user.getAuthenticationLink() != null) {
- em.remove(user.getAuthenticationLink());
- }
- em.remove(user);
- }
-
- @Override
public List<String> getDefaultRoles() {
Collection<RoleEntity> entities = realm.getDefaultRoles();
List<String> roles = new ArrayList<String>();
@@ -591,7 +490,7 @@ public class RealmAdapter implements RealmModel {
List<ApplicationModel> list = new ArrayList<ApplicationModel>();
if (realm.getApplications() == null) return list;
for (ApplicationEntity entity : realm.getApplications()) {
- list.add(new ApplicationAdapter(this, em, entity));
+ list.add(new ApplicationAdapter(this, em, session, entity));
}
return list;
}
@@ -611,7 +510,7 @@ public class RealmAdapter implements RealmModel {
realm.getApplications().add(applicationData);
em.persist(applicationData);
em.flush();
- ApplicationModel resource = new ApplicationAdapter(this, em, applicationData);
+ ApplicationModel resource = new ApplicationAdapter(this, em, session, applicationData);
em.flush();
return resource;
}
@@ -622,7 +521,6 @@ public class RealmAdapter implements RealmModel {
ApplicationModel application = getApplicationById(id);
if (application == null) return false;
- ((ApplicationAdapter)application).deleteUserSessionAssociation();
for (RoleModel role : application.getRoles()) {
application.removeRole(role);
}
@@ -646,14 +544,15 @@ public class RealmAdapter implements RealmModel {
return false;
}
em.remove(applicationEntity);
- em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", applicationEntity).executeUpdate();
+ em.createNamedQuery("deleteScopeMappingByClient").setParameter("client", applicationEntity).executeUpdate();
+ em.flush();
return true;
}
@Override
public ApplicationModel getApplicationById(String id) {
- return session.getApplicationById(id, this);
+ return session.realms().getApplicationById(id, this);
}
@Override
@@ -662,57 +561,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
- return session.getUserBySocialLink(socialLink, this);
- }
-
- @Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user) {
- return session.getSocialLinks(user, this);
- }
-
- @Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider) {
- return session.getSocialLink(user, socialProvider, this);
- }
-
- @Override
- public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
- SocialLinkEntity entity = new SocialLinkEntity();
- entity.setRealm(realm);
- entity.setSocialProvider(socialLink.getSocialProvider());
- entity.setSocialUserId(socialLink.getSocialUserId());
- entity.setSocialUsername(socialLink.getSocialUsername());
- UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
- entity.setUser(userEntity);
- em.persist(entity);
- em.flush();
- }
-
- private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
- TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
- UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
- query.setParameter("user", userEntity);
- query.setParameter("socialProvider", socialProvider);
- List<SocialLinkEntity> results = query.getResultList();
- return results.size() > 0 ? results.get(0) : null;
- }
-
-
- @Override
- public boolean removeSocialLink(UserModel user, String socialProvider) {
- SocialLinkEntity entity = findSocialLink(user, socialProvider);
- if (entity != null) {
- em.remove(entity);
- em.flush();
- return true;
- } else {
- return false;
- }
- }
-
-
- @Override
public boolean isSocial() {
return realm.isSocial();
}
@@ -735,21 +583,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public List<UserModel> getUsers() {
- return session.getUsers(this);
- }
-
- @Override
- public List<UserModel> searchForUser(String search) {
- return session.searchForUser(search, this);
- }
-
- @Override
- public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
- return session.searchForUserByAttributes(attributes, this);
- }
-
- @Override
public OAuthClientModel addOAuthClient(String name) {
return this.addOAuthClient(KeycloakModelUtils.generateId(), name);
}
@@ -770,9 +603,8 @@ public class RealmAdapter implements RealmModel {
public boolean removeOAuthClient(String id) {
OAuthClientModel oauth = getOAuthClientById(id);
if (oauth == null) return false;
- ((OAuthClientAdapter)oauth).deleteUserSessionAssociation();
- OAuthClientEntity client = (OAuthClientEntity) ((OAuthClientAdapter) oauth).getEntity();
- em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", client).executeUpdate();
+ OAuthClientEntity client = em.getReference(OAuthClientEntity.class, oauth.getId());
+ em.createNamedQuery("deleteScopeMappingByClient").setParameter("client", client).executeUpdate();
em.remove(client);
return true;
}
@@ -790,7 +622,7 @@ public class RealmAdapter implements RealmModel {
@Override
public OAuthClientModel getOAuthClientById(String id) {
- return session.getOAuthClientById(id, this);
+ return session.realms().getOAuthClientById(id, this);
}
@@ -867,6 +699,7 @@ public class RealmAdapter implements RealmModel {
int counter = 1;
for (AuthenticationProviderModel model : authenticationProviders) {
AuthenticationProviderEntity entity = new AuthenticationProviderEntity();
+ entity.setRealm(realm);
entity.setProviderName(model.getProviderName());
entity.setPasswordUpdateSupported(model.isPasswordUpdateSupported());
entity.setConfig(model.getConfig());
@@ -882,6 +715,8 @@ public class RealmAdapter implements RealmModel {
em.remove(apToRemove);
}
+ em.flush();
+
// Now create all new providers
for (AuthenticationProviderEntity apToAdd : newEntities) {
existing.add(apToAdd);
@@ -892,6 +727,64 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public List<UserFederationProviderModel> getUserFederationProviders() {
+ List<FederationProviderEntity> entities = realm.getFederationProviders();
+ List<FederationProviderEntity> copy = new ArrayList<FederationProviderEntity>();
+ for (FederationProviderEntity entity : entities) {
+ copy.add(entity);
+
+ }
+ Collections.sort(copy, new Comparator<FederationProviderEntity>() {
+
+ @Override
+ public int compare(FederationProviderEntity o1, FederationProviderEntity o2) {
+ return o1.getPriority() - o2.getPriority();
+ }
+
+ });
+ List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
+ for (FederationProviderEntity entity : copy) {
+ result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig()));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void setUserFederationProviders(List<UserFederationProviderModel> providers) {
+ List<FederationProviderEntity> newEntities = new ArrayList<FederationProviderEntity>();
+ int counter = 1;
+ for (UserFederationProviderModel model : providers) {
+ FederationProviderEntity entity = new FederationProviderEntity();
+ entity.setId(KeycloakModelUtils.generateId());
+ entity.setRealm(realm);
+ entity.setProviderName(model.getProviderName());
+ entity.setConfig(model.getConfig());
+ entity.setPriority(counter++);
+ newEntities.add(entity);
+ }
+
+ // Remove all existing first
+ Collection<FederationProviderEntity> existing = realm.getFederationProviders();
+ Collection<FederationProviderEntity> copy = new ArrayList<FederationProviderEntity>(existing);
+ for (FederationProviderEntity apToRemove : copy) {
+ existing.remove(apToRemove);
+ em.remove(apToRemove);
+ }
+
+ em.flush();
+
+ // Now create all new providers
+ for (FederationProviderEntity apToAdd : newEntities) {
+ existing.add(apToAdd);
+ em.persist(apToAdd);
+ }
+
+ em.flush();
+ }
+
+
+ @Override
public RoleModel getRole(String name) {
TypedQuery<RoleEntity> query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
query.setParameter("name", name);
@@ -925,14 +818,13 @@ public class RealmAdapter implements RealmModel {
return false;
}
if (!role.getContainer().equals(this)) return false;
-
+ session.users().preRemove(this, role);
RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
realm.getRoles().remove(role);
realm.getDefaultRoles().remove(role);
- em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", roleEntity).executeUpdate();
- em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
- em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
+ em.createNativeQuery("delete from COMPOSITE_ROLE where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate();
+ em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
em.remove(roleEntity);
@@ -952,7 +844,7 @@ public class RealmAdapter implements RealmModel {
@Override
public RoleModel getRoleById(String id) {
- return session.getRoleById(id, this);
+ return session.realms().getRoleById(id, this);
}
@Override
@@ -962,32 +854,6 @@ public class RealmAdapter implements RealmModel {
return role.getContainer().removeRole(role);
}
-
-
-
-
- @Override
- public boolean validatePassword(UserModel user, String password) {
- for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
- if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
- return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue());
-
- }
- }
- return false;
- }
-
- @Override
- public boolean validateTOTP(UserModel user, String password, String token) {
- if (!validatePassword(user, password)) return false;
- for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
- if (cred.getType().equals(UserCredentialModel.TOTP)) {
- return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
- }
- }
- return false;
- }
-
@Override
public PasswordPolicy getPasswordPolicy() {
if (passwordPolicy == null) {
@@ -1096,49 +962,14 @@ public class RealmAdapter implements RealmModel {
@Override
public ApplicationModel getMasterAdminApp() {
- return new ApplicationAdapter(this, em, realm.getMasterAdminApp());
+ return new ApplicationAdapter(this, em, session, realm.getMasterAdminApp());
}
@Override
public void setMasterAdminApp(ApplicationModel app) {
- realm.setMasterAdminApp(((ApplicationAdapter) app).getJpaEntity());
+ ApplicationEntity appEntity = app!=null ? em.getReference(ApplicationEntity.class, app.getId()) : null;
+ realm.setMasterAdminApp(appEntity);
em.flush();
}
- @Override
- public UserSessionModel createUserSession(UserModel user, String ipAddress) {
- return session.createUserSession(this, user, ipAddress);
- }
-
- @Override
- public UserSessionModel getUserSession(String id) {
- return session.getUserSession(id, this);
- }
-
- @Override
- public List<UserSessionModel> getUserSessions(UserModel user) {
- return session.getUserSessions(user, this);
- }
-
- @Override
- public void removeUserSession(UserSessionModel session) {
- this.session.removeUserSession(session);
- }
-
- @Override
- public void removeUserSessions() {
- session.removeUserSessions(this);
-
- }
-
- @Override
- public void removeUserSessions(UserModel user) {
- session.removeUserSessions(this, user);
- }
-
- @Override
- public void removeExpiredUserSessions() {
- session.removeExpiredUserSessions(this);
- }
-
}
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 e86b42f..949efba 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
@@ -2,7 +2,7 @@ package org.keycloak.models.jpa;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationLinkModel;
-import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
@@ -11,16 +11,21 @@ import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
import org.keycloak.models.jpa.entities.CredentialEntity;
-import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.UserAttributeEntity;
import org.keycloak.models.jpa.entities.UserEntity;
+import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -53,13 +58,13 @@ public class UserAdapter implements UserModel {
}
@Override
- public String getLoginName() {
- return user.getLoginName();
+ public String getUsername() {
+ return user.getUsername();
}
@Override
- public void setLoginName(String loginName) {
- user.setLoginName(loginName);
+ public void setUsername(String username) {
+ user.setUsername(username);
}
@Override
@@ -79,52 +84,84 @@ public class UserAdapter implements UserModel {
@Override
public void setAttribute(String name, String value) {
- Map<String, String> attributes = user.getAttributes();
- if (attributes == null) {
- attributes = new HashMap<String, String>();
+ for (UserAttributeEntity attr : user.getAttributes()) {
+ if (attr.getName().equals(name)) {
+ attr.setValue(value);
+ return;
+ }
}
- attributes.put(name, value);
- user.setAttributes(attributes);
+ UserAttributeEntity attr = new UserAttributeEntity();
+ attr.setName(name);
+ attr.setValue(value);
+ attr.setUser(user);
+ em.persist(attr);
+ user.getAttributes().add(attr);
}
@Override
public void removeAttribute(String name) {
- Map<String, String> attributes = user.getAttributes();
- if (attributes == null) {
- attributes = new HashMap<String, String>();
+ Iterator<UserAttributeEntity> it = user.getAttributes().iterator();
+ while (it.hasNext()) {
+ UserAttributeEntity attr = it.next();
+ if (attr.getName().equals(name)) {
+ it.remove();
+ em.remove(attr);
+ }
}
- attributes.remove(name);
- user.setAttributes(attributes);
}
@Override
public String getAttribute(String name) {
- if (user.getAttributes() == null) return null;
- return user.getAttributes().get(name);
+ for (UserAttributeEntity attr : user.getAttributes()) {
+ if (attr.getName().equals(name)) {
+ return attr.getValue();
+ }
+ }
+ return null;
}
@Override
public Map<String, String> getAttributes() {
Map<String, String> result = new HashMap<String, String>();
- result.putAll(user.getAttributes());
+ for (UserAttributeEntity attr : user.getAttributes()) {
+ result.put(attr.getName(), attr.getValue());
+ }
return result;
}
@Override
public Set<RequiredAction> getRequiredActions() {
Set<RequiredAction> result = new HashSet<RequiredAction>();
- result.addAll(user.getRequiredActions());
+ for (UserRequiredActionEntity attr : user.getRequiredActions()) {
+ result.add(attr.getAction());
+ }
return result;
}
@Override
public void addRequiredAction(RequiredAction action) {
- user.getRequiredActions().add(action);
+ for (UserRequiredActionEntity attr : user.getRequiredActions()) {
+ if (attr.getAction().equals(action)) {
+ return;
+ }
+ }
+ UserRequiredActionEntity attr = new UserRequiredActionEntity();
+ attr.setAction(action);
+ attr.setUser(user);
+ em.persist(attr);
+ user.getRequiredActions().add(attr);
}
@Override
public void removeRequiredAction(RequiredAction action) {
- user.getRequiredActions().remove(action);
+ Iterator<UserRequiredActionEntity> it = user.getRequiredActions().iterator();
+ while (it.hasNext()) {
+ UserRequiredActionEntity attr = it.next();
+ if (attr.getAction().equals(action)) {
+ it.remove();
+ em.remove(attr);
+ }
+ }
}
@@ -174,21 +211,12 @@ public class UserAdapter implements UserModel {
}
@Override
- public int getNotBefore() {
- return user.getNotBefore();
- }
-
- @Override
- public void setNotBefore(int notBefore) {
- user.setNotBefore(notBefore);
- }
-
- @Override
public void updateCredential(UserCredentialModel cred) {
CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
if (credentialEntity == null) {
credentialEntity = new CredentialEntity();
+ credentialEntity.setId(KeycloakModelUtils.generateId());
credentialEntity.setType(cred.getType());
credentialEntity.setDevice(cred.getDevice());
credentialEntity.setUser(user);
@@ -197,8 +225,15 @@ public class UserAdapter implements UserModel {
}
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
byte[] salt = getSalt();
- credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue()));
+ int hashIterations = 1;
+ PasswordPolicy policy = realm.getPasswordPolicy();
+ if (policy != null) {
+ hashIterations = policy.getHashIterations();
+ if (hashIterations == -1) hashIterations = 1;
+ }
+ credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
credentialEntity.setSalt(salt);
+ credentialEntity.setHashIterations(hashIterations);
} else {
credentialEntity.setValue(cred.getValue());
}
@@ -228,6 +263,7 @@ public class UserAdapter implements UserModel {
credModel.setDevice(credEntity.getDevice());
credModel.setValue(credEntity.getValue());
credModel.setSalt(credEntity.getSalt());
+ credModel.setHashIterations(credEntity.getHashIterations());
result.add(credModel);
}
@@ -242,6 +278,7 @@ public class UserAdapter implements UserModel {
if (credentialEntity == null) {
credentialEntity = new CredentialEntity();
+ credentialEntity.setId(KeycloakModelUtils.generateId());
credentialEntity.setType(credModel.getType());
credentialEntity.setUser(user);
em.persist(credentialEntity);
@@ -251,6 +288,7 @@ public class UserAdapter implements UserModel {
credentialEntity.setValue(credModel.getValue());
credentialEntity.setSalt(credModel.getSalt());
credentialEntity.setDevice(credModel.getDevice());
+ credentialEntity.setHashIterations(credModel.getHashIterations());
em.flush();
}
@@ -269,8 +307,7 @@ public class UserAdapter implements UserModel {
protected TypedQuery<UserRoleMappingEntity> getUserRoleMappingEntityTypedQuery(RoleModel role) {
TypedQuery<UserRoleMappingEntity> query = em.createNamedQuery("userHasRole", UserRoleMappingEntity.class);
query.setParameter("user", getUser());
- RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
- query.setParameter("role", roleEntity);
+ query.setParameter("roleId", role.getId());
return query;
}
@@ -279,8 +316,7 @@ public class UserAdapter implements UserModel {
if (hasRole(role)) return;
UserRoleMappingEntity entity = new UserRoleMappingEntity();
entity.setUser(getUser());
- RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
- entity.setRole(roleEntity);
+ entity.setRoleId(role.getId());
em.persist(entity);
em.flush();
em.detach(entity);
@@ -349,8 +385,12 @@ public class UserAdapter implements UserModel {
@Override
public AuthenticationLinkModel getAuthenticationLink() {
- AuthenticationLinkEntity authLinkEntity = user.getAuthenticationLink();
- return authLinkEntity == null ? null : new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId());
+ Collection<AuthenticationLinkEntity> col = user.getAuthenticationLink();
+ if (col == null || col.isEmpty()) {
+ return null;
+ }
+ AuthenticationLinkEntity authLinkEntity = col.iterator().next();
+ return new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId());
}
@Override
@@ -358,14 +398,32 @@ public class UserAdapter implements UserModel {
AuthenticationLinkEntity entity = new AuthenticationLinkEntity();
entity.setAuthProvider(authenticationLink.getAuthProvider());
entity.setAuthUserId(authenticationLink.getAuthUserId());
+ entity.setUser(user);
+
+ if (user.getAuthenticationLink() == null) {
+ user.setAuthenticationLink(new LinkedList<AuthenticationLinkEntity>());
+ } else if (!user.getAuthenticationLink().isEmpty()) {
+ AuthenticationLinkEntity old = user.getAuthenticationLink().iterator().next();
+ user.getAuthenticationLink().clear();
+ em.remove(old);
+ }
- user.setAuthenticationLink(entity);
+ user.getAuthenticationLink().add(entity);
em.persist(entity);
- em.persist(user);
em.flush();
}
@Override
+ public String getFederationLink() {
+ return user.getFederationLink();
+ }
+
+ @Override
+ public void setFederationLink(String link) {
+ user.setFederationLink(link);
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof UserModel)) return false;
diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory
new file mode 100755
index 0000000..8c7cf39
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.jpa.JpaRealmProviderFactory
\ No newline at end of file
diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory
new file mode 100755
index 0000000..6c80343
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.jpa.JpaUserProviderFactory
\ No newline at end of file
model/mongo/pom.xml 121(+2 -119)
diff --git a/model/mongo/pom.xml b/model/mongo/pom.xml
index 31d513d..c239f07 100755
--- a/model/mongo/pom.xml
+++ b/model/mongo/pom.xml
@@ -39,9 +39,9 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-invalidation-cache-model</artifactId>
+ <artifactId>keycloak-connections-mongo</artifactId>
<version>${project.version}</version>
- <scope>test</scope>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
@@ -53,122 +53,5 @@
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
-
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-tests</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs</artifactId>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-tests</artifactId>
- <version>${project.version}</version>
- <classifier>tests</classifier>
- <scope>test</scope>
- </dependency>
</dependencies>
-
- <properties>
- <keycloak.model.mongo.host>localhost</keycloak.model.mongo.host>
- <keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
- <keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
- <keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
- <keycloak.model.mongo.bindIp>127.0.0.1</keycloak.model.mongo.bindIp>
- </properties>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>${maven.compiler.source}</source>
- <target>${maven.compiler.target}</target>
- </configuration>
- </plugin>
-
- <!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <executions>
- <execution>
- <id>test</id>
- <phase>integration-test</phase>
- <goals>
- <goal>test</goal>
- </goals>
- <configuration>
- <systemPropertyVariables>
- <keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
- <keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
- <keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
- <keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
- <keycloak.model.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.model.mongo.bindIp>
- </systemPropertyVariables>
- <dependenciesToScan>
- <dependency>org.keycloak:keycloak-model-tests</dependency>
- </dependenciesToScan>
- </configuration>
- </execution>
- <execution>
- <id>default-test</id>
- <configuration>
- <skip>true</skip>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <!-- Embedded mongo -->
- <plugin>
- <groupId>com.github.joelittlejohn.embedmongo</groupId>
- <artifactId>embedmongo-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>start-mongodb</id>
- <phase>pre-integration-test</phase>
- <goals>
- <goal>start</goal>
- </goals>
- <configuration>
- <port>${keycloak.model.mongo.port}</port>
- <logging>file</logging>
- <logFile>${project.build.directory}/mongodb.log</logFile>
- <bindIp>${keycloak.model.mongo.bindIp}</bindIp>
- </configuration>
- </execution>
- <execution>
- <id>stop-mongodb</id>
- <phase>post-integration-test</phase>
- <goals>
- <goal>stop</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- </plugins>
- </build>
</project>
\ No newline at end of file
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractMongoAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractMongoAdapter.java
index 42b10cd..f922e76 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractMongoAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractMongoAdapter.java
@@ -1,8 +1,8 @@
package org.keycloak.models.mongo.keycloak.adapters;
-import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.models.mongo.api.MongoStore;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
index d2ab912..2b6ef4c 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
@@ -2,14 +2,12 @@ package org.keycloak.models.mongo.keycloak.adapters;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.mongo.utils.MongoModelUtils;
@@ -143,6 +141,7 @@ public class ApplicationAdapter extends ClientAdapter<MongoApplicationEntity> im
@Override
public boolean removeRole(RoleModel role) {
+ session.users().preRemove(getRealm(), role);
return getMongoStore().removeEntity(MongoRoleEntity.class, role.getId(), invocationContext);
}
@@ -162,6 +161,20 @@ public class ApplicationAdapter extends ClientAdapter<MongoApplicationEntity> im
}
@Override
+ public boolean hasScope(RoleModel role) {
+ if (super.hasScope(role)) {
+ return true;
+ }
+ Set<RoleModel> roles = getRoles();
+ if (roles.contains(role)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) return true;
+ }
+ return false;
+ }
+
+ @Override
public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
Set<RoleModel> result = new HashSet<RoleModel>();
List<MongoRoleEntity> roles = MongoModelUtils.getAllScopesOfClient(client, invocationContext);
@@ -205,6 +218,7 @@ public class ApplicationAdapter extends ClientAdapter<MongoApplicationEntity> im
updateMongoEntity();
}
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index 614204b..1e468bc 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -1,24 +1,21 @@
package org.keycloak.models.mongo.keycloak.adapters;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import com.mongodb.DBObject;
-import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserSessionModel;
import org.keycloak.models.entities.ClientEntity;
-import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
-import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity;
import org.keycloak.models.mongo.utils.MongoModelUtils;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@@ -27,12 +24,14 @@ public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends A
protected final T clientEntity;
private final RealmModel realm;
protected KeycloakSession session;
+ private final RealmProvider model;
public ClientAdapter(KeycloakSession session, RealmModel realm, T clientEntity, MongoStoreInvocationContext invContext) {
super(invContext);
this.clientEntity = clientEntity;
this.realm = realm;
this.session = session;
+ this.model = session.realms();
}
@Override
@@ -175,16 +174,6 @@ public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends A
}
@Override
- public Set<UserSessionModel> getUserSessions() {
- return session.getUserSessions(realm, this);
- }
-
- @Override
- public int getActiveUserSessions() {
- return session.getActiveUserSessions(realm, this);
- }
-
- @Override
public Set<RoleModel> getScopeMappings() {
Set<RoleModel> result = new HashSet<RoleModel>();
List<MongoRoleEntity> roles = MongoModelUtils.getAllScopesOfClient(this, invocationContext);
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
new file mode 100755
index 0000000..a025e89
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
@@ -0,0 +1,131 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoRealmProvider implements RealmProvider {
+
+ private final MongoStoreInvocationContext invocationContext;
+ private final KeycloakSession session;
+ private final MongoStore mongoStore;
+
+ public MongoRealmProvider(KeycloakSession session, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
+ this.session = session;
+ this.mongoStore = mongoStore;
+ this.invocationContext = invocationContext;
+ }
+
+ @Override
+ public void close() {
+ // TODO
+ }
+
+ @Override
+ public RealmModel createRealm(String name) {
+ return createRealm(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RealmModel createRealm(String id, String name) {
+ MongoRealmEntity newRealm = new MongoRealmEntity();
+ newRealm.setId(id);
+ newRealm.setName(name);
+
+ getMongoStore().insertEntity(newRealm, invocationContext);
+
+ return new RealmAdapter(session, newRealm, invocationContext);
+ }
+
+ @Override
+ public RealmModel getRealm(String id) {
+ MongoRealmEntity realmEntity = getMongoStore().loadEntity(MongoRealmEntity.class, id, invocationContext);
+ return realmEntity != null ? new RealmAdapter(session, realmEntity, invocationContext) : null;
+ }
+
+ @Override
+ public List<RealmModel> getRealms() {
+ DBObject query = new BasicDBObject();
+ List<MongoRealmEntity> realms = getMongoStore().loadEntities(MongoRealmEntity.class, query, invocationContext);
+
+ List<RealmModel> results = new ArrayList<RealmModel>();
+ for (MongoRealmEntity realmEntity : realms) {
+ results.add(new RealmAdapter(session, realmEntity, invocationContext));
+ }
+ return results;
+ }
+
+ @Override
+ public RealmModel getRealmByName(String name) {
+ DBObject query = new QueryBuilder()
+ .and("name").is(name)
+ .get();
+ MongoRealmEntity realm = getMongoStore().loadSingleEntity(MongoRealmEntity.class, query, invocationContext);
+
+ if (realm == null) return null;
+ return new RealmAdapter(session, realm, invocationContext);
+ }
+
+ @Override
+ public boolean removeRealm(String id) {
+ RealmModel realm = getRealm(id);
+ if (realm == null) return false;
+ session.users().preRemove(realm);
+ return getMongoStore().removeEntity(MongoRealmEntity.class, id, invocationContext);
+ }
+
+ protected MongoStore getMongoStore() {
+ return invocationContext.getMongoStore();
+ }
+
+ @Override
+ public RoleModel getRoleById(String id, RealmModel realm) {
+ MongoRoleEntity role = getMongoStore().loadEntity(MongoRoleEntity.class, id, invocationContext);
+ if (role == null) return null;
+ if (role.getRealmId() != null && !role.getRealmId().equals(realm.getId())) return null;
+ if (role.getApplicationId() != null && realm.getApplicationById(role.getApplicationId()) == null) return null;
+ return new RoleAdapter(session, realm, role, null, invocationContext);
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id, RealmModel realm) {
+ MongoApplicationEntity appData = getMongoStore().loadEntity(MongoApplicationEntity.class, id, invocationContext);
+
+ // Check if application belongs to this realm
+ if (appData == null || !realm.getId().equals(appData.getRealmId())) {
+ return null;
+ }
+
+ return new ApplicationAdapter(session, realm, appData, invocationContext);
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
+ MongoOAuthClientEntity clientEntity = getMongoStore().loadEntity(MongoOAuthClientEntity.class, id, invocationContext);
+
+ // Check if client belongs to this realm
+ if (clientEntity == null || !realm.getId().equals(clientEntity.getRealmId())) return null;
+
+ return new OAuthClientAdapter(session, realm, clientEntity, invocationContext);
+ }
+
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java
new file mode 100755
index 0000000..fceeb82
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java
@@ -0,0 +1,38 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.connections.mongo.MongoConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.RealmProviderFactory;
+
+/**
+ * KeycloakSessionFactory implementation based on MongoDB
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoRealmProviderFactory implements RealmProviderFactory {
+ protected static final Logger logger = Logger.getLogger(MongoRealmProviderFactory.class);
+
+ @Override
+ public String getId() {
+ return "mongo";
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public RealmProvider create(KeycloakSession session) {
+ MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
+ return new MongoRealmProvider(session, connection.getMongoStore(), connection.getInvocationContext());
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
+
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProviderFactory.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProviderFactory.java
new file mode 100755
index 0000000..083d0c0
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProviderFactory.java
@@ -0,0 +1,38 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.connections.mongo.MongoConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.UserProvider;
+import org.keycloak.models.UserProviderFactory;
+
+/**
+ * KeycloakSessionFactory implementation based on MongoDB
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoUserProviderFactory implements UserProviderFactory {
+ protected static final Logger logger = Logger.getLogger(MongoUserProviderFactory.class);
+
+ @Override
+ public String getId() {
+ return "mongo";
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public UserProvider create(KeycloakSession session) {
+ MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
+ return new MongoUserProvider(session, connection.getMongoStore(), connection.getInvocationContext());
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
+
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
index 0ebd713..71b73ce 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
@@ -1,10 +1,9 @@
package org.keycloak.models.mongo.keycloak.adapters;
-import org.keycloak.models.ApplicationModel;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
/**
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 2602222..2e563f5 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
@@ -1,43 +1,28 @@
package org.keycloak.models.mongo.keycloak.adapters;
-import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.jboss.logging.Logger;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.AuthenticationLinkModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.entities.FederationProviderEntity;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
-import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.UserCredentialValueModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.UsernameLoginFailureModel;
-import org.keycloak.models.entities.AuthenticationLinkEntity;
import org.keycloak.models.entities.AuthenticationProviderEntity;
-import org.keycloak.models.entities.CredentialEntity;
import org.keycloak.models.entities.RequiredCredentialEntity;
-import org.keycloak.models.entities.SocialLinkEntity;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
-import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
-import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity;
-import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity;
-import org.keycloak.models.mongo.utils.MongoModelUtils;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
-import org.keycloak.models.utils.TimeBasedOTP;
-import org.keycloak.util.Time;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -46,11 +31,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.regex.Pattern;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -60,6 +43,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
private static final Logger logger = Logger.getLogger(RealmAdapter.class);
private final MongoRealmEntity realm;
+ private final RealmProvider model;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
@@ -71,6 +55,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
super(invocationContext);
this.realm = realmEntity;
this.session = session;
+ this.model = session.realms();
}
@Override
@@ -448,80 +433,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
- public UserModel getUser(String name) {
- return session.getUserByUsername(name, this);
- }
-
- @Override
- public UsernameLoginFailureModel getUserLoginFailure(String name) {
- return session.getUserLoginFailure(name, this);
- }
-
- @Override
- public UsernameLoginFailureModel addUserLoginFailure(String username) {
- return session.addUserLoginFailure(username, this);
- }
-
-
- @Override
- public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
- return session.getAllUserLoginFailures(this);
- }
-
- @Override
- public UserModel getUserByEmail(String email) {
- return session.getUserByEmail(email, this);
- }
-
- @Override
- public UserModel getUserById(String id) {
- return session.getUserById(id, this);
- }
-
- @Override
- public UserAdapter addUser(String username) {
- return this.addUser(null, username);
- }
-
- @Override
- public UserAdapter addUser(String id, String username) {
- UserAdapter userModel = addUserEntity(id, username);
-
- for (String r : getDefaultRoles()) {
- userModel.grantRole(getRole(r));
- }
-
- for (ApplicationModel application : getApplications()) {
- for (String r : application.getDefaultRoles()) {
- userModel.grantRole(application.getRole(r));
- }
- }
-
- return userModel;
- }
-
- protected UserAdapter addUserEntity(String id, String username) {
- MongoUserEntity userEntity = new MongoUserEntity();
- userEntity.setId(id);
- userEntity.setLoginName(username);
- // Compatibility with JPA model, which has user disabled by default
- // userEntity.setEnabled(true);
- userEntity.setRealmId(getId());
-
- getMongoStore().insertEntity(userEntity, invocationContext);
- return new UserAdapter(session, this, userEntity, invocationContext);
- }
-
- @Override
- public boolean removeUser(String name) {
- DBObject query = new QueryBuilder()
- .and("loginName").is(name)
- .and("realmId").is(getId())
- .get();
- return getMongoStore().removeEntities(MongoUserEntity.class, query, invocationContext);
- }
-
- @Override
public RoleAdapter getRole(String name) {
DBObject query = new QueryBuilder()
.and("name").is(name)
@@ -559,6 +470,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public boolean removeRoleById(String id) {
+ RoleModel role = getRoleById(id);
+ if (role == null) return false;
+ session.users().preRemove(this, role);
return getMongoStore().removeEntity(MongoRoleEntity.class, id, invocationContext);
}
@@ -581,7 +495,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public RoleModel getRoleById(String id) {
- return session.getRoleById(id, this);
+ return model.getRoleById(id, this);
}
@Override
@@ -633,7 +547,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public ApplicationModel getApplicationById(String id) {
- return session.getApplicationById(id, this);
+ return model.getApplicationById(id, this);
}
@Override
@@ -724,7 +638,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public OAuthClientModel getOAuthClientById(String id) {
- return session.getOAuthClientById(id, this);
+ return model.getOAuthClientById(id, this);
}
@Override
@@ -806,81 +720,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
return result;
}
- @Override
- public boolean validatePassword(UserModel user, String password) {
- for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
- if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
- return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue());
- }
- }
- return false;
- }
-
- @Override
- public boolean validateTOTP(UserModel user, String password, String token) {
- if (!validatePassword(user, password)) return false;
- for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
- if (cred.getType().equals(UserCredentialModel.TOTP)) {
- return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
- }
- }
- return false;
- }
-
-
-
-
- @Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
- return session.getUserBySocialLink(socialLink, this);
- }
-
- @Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user) {
- return session.getSocialLinks(user, this);
- }
-
- @Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider) {
- return session.getSocialLink(user, socialProvider, this);
- }
-
- @Override
- public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
- MongoUserEntity userEntity = ((UserAdapter) user).getUser();
- SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
- socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
- socialLinkEntity.setSocialUserId(socialLink.getSocialUserId());
- socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
-
- getMongoStore().pushItemToList(userEntity, "socialLinks", socialLinkEntity, true, invocationContext);
- }
-
- @Override
- public boolean removeSocialLink(UserModel userModel, String socialProvider) {
- UserModel user = getUserById(userModel.getId());
- MongoUserEntity userEntity = ((UserAdapter) user).getUser();
- SocialLinkEntity socialLinkEntity = findSocialLink(userEntity, socialProvider);
- if (socialLinkEntity == null) {
- return false;
- }
- return getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
- }
-
- private SocialLinkEntity findSocialLink(MongoUserEntity userEntity, String socialProvider) {
- List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
- if (linkEntities == null) {
- return null;
- }
-
- for (SocialLinkEntity socialLinkEntity : linkEntities) {
- if (socialLinkEntity.getSocialProvider().equals(socialProvider)) {
- return socialLinkEntity;
- }
- }
- return null;
- }
-
protected void updateRealm() {
super.updateMongoEntity();
}
@@ -894,23 +733,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
- public List<UserModel> getUsers() {
- return session.getUsers(this);
- }
-
- @Override
- public List<UserModel> searchForUser(String search) {
- return session.searchForUser(search, this);
-
- }
-
- @Override
- public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
- return session.searchForUserByAttributes(attributes, this);
- }
-
-
- @Override
public Map<String, String> getSmtpConfig() {
return realm.getSmtpConfig();
}
@@ -968,6 +790,31 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
realm.setAuthenticationProviders(entities);
updateRealm();
}
+ @Override
+ public List<UserFederationProviderModel> getUserFederationProviders() {
+ List<FederationProviderEntity> entities = realm.getFederationProviders();
+ List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
+ for (FederationProviderEntity entity : entities) {
+ result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig()));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void setUserFederationProviders(List<UserFederationProviderModel> providers) {
+ List<FederationProviderEntity> entities = new ArrayList<FederationProviderEntity>();
+ for (UserFederationProviderModel model : providers) {
+ FederationProviderEntity entity = new FederationProviderEntity();
+ entity.setId(KeycloakModelUtils.generateId());
+ entity.setProviderName(model.getProviderName());
+ entity.setConfig(model.getConfig());
+ entities.add(entity);
+ }
+
+ realm.setFederationProviders(entities);
+ updateRealm();
+ }
@Override
public boolean isAuditEnabled() {
@@ -1014,7 +861,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public void setMasterAdminApp(ApplicationModel app) {
- realm.setAdminAppId(app.getId());
+ String adminAppId = app != null ? app.getId() : null;
+ realm.setAdminAppId(adminAppId);
updateRealm();
}
@@ -1024,41 +872,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
- public UserSessionModel createUserSession(UserModel user, String ipAddress) {
- return session.createUserSession(this, user, ipAddress);
- }
-
- @Override
- public UserSessionModel getUserSession(String id) {
- return session.getUserSession(id, this);
- }
-
- @Override
- public List<UserSessionModel> getUserSessions(UserModel user) {
- return session.getUserSessions(user, this);
- }
-
- @Override
- public void removeUserSession(UserSessionModel session) {
- this.session.removeUserSession(session);
- }
-
- @Override
- public void removeUserSessions(UserModel user) {
- this.session.removeUserSessions(this, user);
- }
-
- @Override
- public void removeUserSessions() {
- this.session.removeUserSessions(this);
- }
-
- @Override
- public void removeExpiredUserSessions() {
- this.session.removeExpiredUserSessions(this);
- }
-
- @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof RealmModel)) return false;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
index 3eb9f0c..c2a8317 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
@@ -1,22 +1,22 @@
package org.keycloak.models.mongo.keycloak.adapters;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* Wrapper around RoleData object, which will persist wrapped object after each set operation (compatibility with picketlink based idm)
*
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 e582b93..1d30a5b 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
@@ -1,9 +1,10 @@
package org.keycloak.models.mongo.keycloak.adapters;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationLinkModel;
-import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
@@ -11,7 +12,6 @@ import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.entities.AuthenticationLinkEntity;
import org.keycloak.models.entities.CredentialEntity;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
import org.keycloak.models.mongo.utils.MongoModelUtils;
@@ -49,13 +49,13 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
}
@Override
- public String getLoginName() {
- return user.getLoginName();
+ public String getUsername() {
+ return user.getUsername();
}
@Override
- public void setLoginName(String loginName) {
- user.setLoginName(loginName);
+ public void setUsername(String username) {
+ user.setUsername(username);
updateUser();
}
@@ -71,16 +71,6 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
}
@Override
- public int getNotBefore() {
- return user.getNotBefore();
- }
-
- @Override
- public void setNotBefore(int notBefore) {
- user.setNotBefore(notBefore);
- }
-
- @Override
public String getFirstName() {
return user.getFirstName();
}
@@ -199,8 +189,15 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
}
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
byte[] salt = Pbkdf2PasswordEncoder.getSalt();
- credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue()));
+ int hashIterations = 1;
+ PasswordPolicy policy = realm.getPasswordPolicy();
+ if (policy != null) {
+ hashIterations = policy.getHashIterations();
+ if (hashIterations == -1) hashIterations = 1;
+ }
+ credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
credentialEntity.setSalt(salt);
+ credentialEntity.setHashIterations(hashIterations);
} else {
credentialEntity.setValue(cred.getValue());
}
@@ -229,6 +226,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
credModel.setDevice(credEntity.getDevice());
credModel.setValue(credEntity.getValue());
credModel.setSalt(credEntity.getSalt());
+ credModel.setHashIterations(credEntity.getHashIterations());
result.add(credModel);
}
@@ -249,6 +247,8 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
credentialEntity.setValue(credModel.getValue());
credentialEntity.setSalt(credModel.getSalt());
credentialEntity.setDevice(credModel.getDevice());
+ credentialEntity.setHashIterations(credModel.getHashIterations());
+
getMongoStore().updateEntity(user, invocationContext);
}
@@ -331,6 +331,17 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
}
@Override
+ public String getFederationLink() {
+ return user.getFederationLink();
+ }
+
+ @Override
+ public void setFederationLink(String link) {
+ user.setFederationLink(link);
+
+ }
+
+ @Override
public AuthenticationLinkModel getAuthenticationLink() {
AuthenticationLinkEntity authLinkEntity = user.getAuthenticationLink();
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
index c8dd6da..9000d37 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
@@ -1,15 +1,12 @@
package org.keycloak.models.mongo.keycloak.entities;
-import java.util.List;
-
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
-import org.jboss.logging.Logger;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoIndex;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.entities.ApplicationEntity;
-import org.keycloak.models.mongo.api.MongoCollection;
-import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.models.mongo.api.MongoIndex;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -25,14 +22,5 @@ public class MongoApplicationEntity extends ApplicationEntity implements MongoId
.and("applicationId").is(getId())
.get();
context.getMongoStore().removeEntities(MongoRoleEntity.class, query, context);
-
- // Remove all session associations
- query = new QueryBuilder()
- .and("associatedClientIds").is(getId())
- .get();
- List<MongoUserSessionEntity> sessions = context.getMongoStore().loadEntities(MongoUserSessionEntity.class, query, context);
- for (MongoUserSessionEntity session : sessions) {
- context.getMongoStore().pullItemFromList(session, "associatedClientIds", getId(), context);
- }
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
index 8950292..bf07f91 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
@@ -1,14 +1,12 @@
package org.keycloak.models.mongo.keycloak.entities;
-import java.util.List;
-
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoIndex;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.entities.OAuthClientEntity;
-import org.keycloak.models.mongo.api.MongoCollection;
-import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.models.mongo.api.MongoIndex;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -23,9 +21,5 @@ public class MongoOAuthClientEntity extends OAuthClientEntity implements MongoId
DBObject query = new QueryBuilder()
.and("associatedClientIds").is(getId())
.get();
- List<MongoUserSessionEntity> sessions = context.getMongoStore().loadEntities(MongoUserSessionEntity.class, query, context);
- for (MongoUserSessionEntity session : sessions) {
- context.getMongoStore().pullItemFromList(session, "associatedClientIds", getId(), context);
- }
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
index d091619..c1a4267 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
@@ -2,11 +2,11 @@ package org.keycloak.models.mongo.keycloak.entities;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoIndex;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.entities.RealmEntity;
-import org.keycloak.models.mongo.api.MongoCollection;
-import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.models.mongo.api.MongoIndex;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -32,8 +32,5 @@ public class MongoRealmEntity extends RealmEntity implements MongoIdentifiableEn
// Remove all clients of this realm
context.getMongoStore().removeEntities(MongoOAuthClientEntity.class, query, context);
-
- // Remove all sessions of this realm
- context.getMongoStore().removeEntities(MongoUserSessionEntity.class, query, context);
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
index f931a91..be034da 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
@@ -3,13 +3,13 @@ package org.keycloak.models.mongo.keycloak.entities;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.jboss.logging.Logger;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoField;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoIndex;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.entities.RoleEntity;
-import org.keycloak.models.mongo.api.MongoCollection;
-import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.models.mongo.api.MongoIndex;
-import org.keycloak.models.mongo.api.MongoStore;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import java.util.List;
@@ -50,7 +50,7 @@ public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEnti
List<MongoUserEntity> users = mongoStore.loadEntities(MongoUserEntity.class, query, invContext);
for (MongoUserEntity user : users) {
- logger.info("Removing role " + getName() + " from user " + user.getLoginName());
+ logger.info("Removing role " + getName() + " from user " + user.getUsername());
mongoStore.pullItemFromList(user, "roleIds", getId(), invContext);
}
@@ -61,7 +61,7 @@ public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEnti
users = mongoStore.loadEntities(MongoUserEntity.class, query, invContext);
for (MongoUserEntity user : users) {
- logger.info("Removing scope " + getName() + " from user " + user.getLoginName());
+ logger.info("Removing scope " + getName() + " from user " + user.getUsername());
mongoStore.pullItemFromList(user, "scopeIds", getId(), invContext);
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
index 084a153..a0c16bf 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
@@ -1,20 +1,18 @@
package org.keycloak.models.mongo.keycloak.entities;
-import com.mongodb.DBObject;
-import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoIndex;
+import org.keycloak.connections.mongo.api.MongoIndexes;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.entities.UserEntity;
-import org.keycloak.models.mongo.api.MongoCollection;
-import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.models.mongo.api.MongoIndex;
-import org.keycloak.models.mongo.api.MongoIndexes;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@MongoCollection(collectionName = "users")
@MongoIndexes({
- @MongoIndex(fields = { "realmId", "loginName" }, unique = true),
+ @MongoIndex(fields = { "realmId", "username" }, unique = true),
@MongoIndex(fields = { "emailIndex" }, unique = true, sparse = true),
})
public class MongoUserEntity extends UserEntity implements MongoIdentifiableEntity {
@@ -29,10 +27,5 @@ public class MongoUserEntity extends UserEntity implements MongoIdentifiableEnti
@Override
public void afterRemove(MongoStoreInvocationContext invocationContext) {
- DBObject query = new QueryBuilder()
- .and("userId").is(getId())
- .get();
-
- invocationContext.getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
index a6d40b9..63f0bfd 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
@@ -1,19 +1,19 @@
package org.keycloak.models.mongo.utils;
-import java.util.Collections;
-import java.util.List;
-
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ClientModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.entities.ClientEntity;
-import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.adapters.ClientAdapter;
import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
+import java.util.Collections;
+import java.util.List;
+
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
diff --git a/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory b/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory
new file mode 100755
index 0000000..deffafa
--- /dev/null
+++ b/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.mongo.keycloak.adapters.MongoRealmProviderFactory
\ No newline at end of file
diff --git a/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory b/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory
new file mode 100755
index 0000000..ddc04d8
--- /dev/null
+++ b/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.mongo.keycloak.adapters.MongoUserProviderFactory
\ No newline at end of file
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
index 9fa72d0..9b02e14 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
@@ -16,7 +16,12 @@ import org.picketlink.idm.model.sample.SampleModel;
import org.picketlink.idm.query.IdentityQuery;
import org.picketlink.idm.query.RelationshipQuery;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
index 36d7098..bb1b564 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
@@ -1,7 +1,6 @@
package org.keycloak.models.picketlink;
import org.bouncycastle.openssl.PEMWriter;
-import org.keycloak.util.PemUtils;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.IdGenerator;
import org.keycloak.models.KeycloakSession;
@@ -22,6 +21,7 @@ import org.keycloak.models.picketlink.relationships.RequiredApplicationCredentia
import org.keycloak.models.picketlink.relationships.RequiredCredentialRelationship;
import org.keycloak.models.picketlink.relationships.ScopeRelationship;
import org.keycloak.models.picketlink.relationships.SocialLinkRelationship;
+import org.keycloak.util.PemUtils;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
@@ -46,7 +46,14 @@ import java.io.StringWriter;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Meant to be a per-request object
model/pom.xml 5(+3 -2)
diff --git a/model/pom.xml b/model/pom.xml
index 850a21f..bfb162d 100755
--- a/model/pom.xml
+++ b/model/pom.xml
@@ -27,9 +27,10 @@
<modules>
<module>api</module>
<module>invalidation-cache</module>
- <!-- <module>picketlink</module> -->
<module>jpa</module>
<module>mongo</module>
- <module>tests</module>
+ <module>sessions-jpa</module>
+ <module>sessions-mem</module>
+ <module>sessions-mongo</module>
</modules>
</project>
model/realms-jpa/pom.xml 45(+45 -0)
diff --git a/model/realms-jpa/pom.xml b/model/realms-jpa/pom.xml
new file mode 100755
index 0000000..09413e8
--- /dev/null
+++ b/model/realms-jpa/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-model-realms-jpa</artifactId>
+ <name>Keycloak Model Realms JPA</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-hybrid</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.javax.persistence</groupId>
+ <artifactId>hibernate-jpa-2.0-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>${hibernate.entitymanager.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/ApplicationAdapter.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/ApplicationAdapter.java
new file mode 100755
index 0000000..16c2500
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/ApplicationAdapter.java
@@ -0,0 +1,258 @@
+package org.keycloak.models.realms.jpa;
+
+import org.keycloak.models.realms.Application;
+import org.keycloak.models.realms.Client;
+import org.keycloak.models.realms.Realm;
+import org.keycloak.models.realms.RealmProvider;
+import org.keycloak.models.realms.Role;
+import org.keycloak.models.realms.RoleContainer;
+import org.keycloak.models.realms.jpa.entities.ApplicationEntity;
+import org.keycloak.models.realms.jpa.entities.RoleEntity;
+import org.keycloak.models.realms.jpa.entities.ScopeMappingEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ApplicationAdapter extends ClientAdapter implements Application {
+
+ protected EntityManager em;
+ protected ApplicationEntity applicationEntity;
+
+ public ApplicationAdapter(RealmProvider provider, EntityManager em, ApplicationEntity applicationEntity) {
+ super(provider, applicationEntity, em);
+ this.em = em;
+ this.applicationEntity = applicationEntity;
+ }
+
+ @Override
+ public void updateApplication() {
+ em.flush();
+ }
+
+ @Override
+ public String getName() {
+ return entity.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ entity.setName(name);
+ }
+
+ @Override
+ public boolean isSurrogateAuthRequired() {
+ return applicationEntity.isSurrogateAuthRequired();
+ }
+
+ @Override
+ public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
+ applicationEntity.setSurrogateAuthRequired(surrogateAuthRequired);
+ }
+
+ @Override
+ public String getManagementUrl() {
+ return applicationEntity.getManagementUrl();
+ }
+
+ @Override
+ public void setManagementUrl(String url) {
+ applicationEntity.setManagementUrl(url);
+ }
+
+ @Override
+ public String getBaseUrl() {
+ return applicationEntity.getBaseUrl();
+ }
+
+ @Override
+ public void setBaseUrl(String url) {
+ applicationEntity.setBaseUrl(url);
+ }
+
+ @Override
+ public boolean isBearerOnly() {
+ return applicationEntity.isBearerOnly();
+ }
+
+ @Override
+ public void setBearerOnly(boolean only) {
+ applicationEntity.setBearerOnly(only);
+ }
+
+ @Override
+ public boolean isDirectGrantsOnly() {
+ return false; // applications can't be grant only
+ }
+
+ @Override
+ public void setDirectGrantsOnly(boolean flag) {
+ // applications can't be grant only
+ }
+
+ @Override
+ public Role getRole(String name) {
+ TypedQuery<RoleEntity> query = em.createNamedQuery("getAppRoleByName", RoleEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("application", entity);
+ List<RoleEntity> roles = query.getResultList();
+ if (roles.size() == 0) return null;
+ return new RoleAdapter(provider, em, roles.get(0));
+ }
+
+ @Override
+ public Role addRole(String id, String name) {
+ RoleEntity roleEntity = new RoleEntity();
+ roleEntity.setId(id);
+ roleEntity.setName(name);
+ roleEntity.setApplication(applicationEntity);
+ roleEntity.setApplicationRole(true);
+ roleEntity.setRealmId(entity.getRealm().getId());
+ em.persist(roleEntity);
+ applicationEntity.getRoles().add(roleEntity);
+ em.flush();
+ return new RoleAdapter(provider, em, roleEntity);
+ }
+
+ @Override
+ public boolean removeRole(Role Role) {
+ RoleAdapter roleAdapter = (RoleAdapter) Role;
+ if (Role == null) {
+ return false;
+ }
+ if (!roleAdapter.getContainer().equals(this)) return false;
+
+ if (!roleAdapter.getRole().isApplicationRole()) return false;
+
+ RoleEntity role = roleAdapter.getRole();
+
+ applicationEntity.getRoles().remove(role);
+ applicationEntity.getDefaultRoles().remove(role);
+ em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", role).executeUpdate();
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
+ role.setApplication(null);
+ em.flush();
+ em.remove(role);
+ em.flush();
+
+ return true;
+ }
+
+ @Override
+ public Set<Role> getRoles() {
+ Set<Role> list = new HashSet<Role>();
+ Collection<RoleEntity> roles = applicationEntity.getRoles();
+ if (roles == null) return list;
+ for (RoleEntity entity : roles) {
+ list.add(new RoleAdapter(provider, em, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public Set<Role> getApplicationScopeMappings(Client client) {
+ Set<Role> roleMappings = client.getScopeMappings();
+
+ Set<Role> appRoles = new HashSet<Role>();
+ for (Role role : roleMappings) {
+ RoleContainer container = role.getContainer();
+ if (container instanceof Realm) {
+ } else {
+ Application app = (Application)container;
+ if (app.getId().equals(getId())) {
+ appRoles.add(role);
+ }
+ }
+ }
+
+ return appRoles;
+ }
+
+
+
+
+ @Override
+ public List<String> getDefaultRoles() {
+ Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
+ List<String> roles = new ArrayList<String>();
+ if (entities == null) return roles;
+ for (RoleEntity entity : entities) {
+ roles.add(entity.getName());
+ }
+ return roles;
+ }
+
+ @Override
+ public void addDefaultRole(String name) {
+ Role role = getRole(name);
+ Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
+ for (RoleEntity entity : entities) {
+ if (entity.getId().equals(role.getId())) {
+ return;
+ }
+ }
+ entities.add(((RoleAdapter) role).getRole());
+ em.flush();
+ }
+
+ public static boolean contains(String str, String[] array) {
+ for (String s : array) {
+ if (str.equals(s)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void updateDefaultRoles(String[] defaultRoles) {
+ Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
+ Set<String> already = new HashSet<String>();
+ List<RoleEntity> remove = new ArrayList<RoleEntity>();
+ for (RoleEntity rel : entities) {
+ if (!contains(rel.getName(), defaultRoles)) {
+ remove.add(rel);
+ } else {
+ already.add(rel.getName());
+ }
+ }
+ for (RoleEntity entity : remove) {
+ entities.remove(entity);
+ }
+ em.flush();
+ for (String roleName : defaultRoles) {
+ if (!already.contains(roleName)) {
+ addDefaultRole(roleName);
+ }
+ }
+ em.flush();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof Application)) return false;
+
+ Application that = (Application) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ ApplicationEntity getJpaEntity() {
+ return applicationEntity;
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/ClientAdapter.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/ClientAdapter.java
new file mode 100755
index 0000000..147a4aa
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/ClientAdapter.java
@@ -0,0 +1,225 @@
+package org.keycloak.models.realms.jpa;
+
+import org.keycloak.models.realms.Client;
+import org.keycloak.models.realms.Realm;
+import org.keycloak.models.realms.RealmProvider;
+import org.keycloak.models.realms.Role;
+import org.keycloak.models.realms.RoleContainer;
+import org.keycloak.models.realms.jpa.entities.ClientEntity;
+import org.keycloak.models.realms.jpa.entities.ScopeMappingEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class ClientAdapter implements Client {
+ protected RealmProvider provider;
+ protected ClientEntity entity;
+ protected EntityManager em;
+
+ public ClientAdapter(RealmProvider provider, ClientEntity entity, EntityManager em) {
+ this.provider = provider;
+ this.entity = entity;
+ this.em = em;
+ }
+
+ public ClientEntity getEntity() {
+ return entity;
+ }
+
+ @Override
+ public String getId() {
+ return entity.getId();
+ }
+
+ @Override
+ public Realm getRealm() {
+ return new RealmAdapter(provider, em, entity.getRealm());
+ }
+
+ @Override
+ public String getClientId() {
+ return entity.getName();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return entity.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ entity.setEnabled(enabled);
+ }
+
+ @Override
+ public long getAllowedClaimsMask() {
+ return entity.getAllowedClaimsMask();
+ }
+
+ @Override
+ public void setAllowedClaimsMask(long mask) {
+ entity.setAllowedClaimsMask(mask);
+ }
+
+ @Override
+ public boolean isPublicClient() {
+ return entity.isPublicClient();
+ }
+
+ @Override
+ public void setPublicClient(boolean flag) {
+ entity.setPublicClient(flag);
+ }
+
+ @Override
+ public Set<String> getWebOrigins() {
+ Set<String> result = new HashSet<String>();
+ result.addAll(entity.getWebOrigins());
+ return result;
+ }
+
+
+
+ @Override
+ public void setWebOrigins(Set<String> webOrigins) {
+ entity.setWebOrigins(webOrigins);
+ }
+
+ @Override
+ public void addWebOrigin(String webOrigin) {
+ entity.getWebOrigins().add(webOrigin);
+ }
+
+ @Override
+ public void removeWebOrigin(String webOrigin) {
+ entity.getWebOrigins().remove(webOrigin);
+ }
+
+ @Override
+ public Set<String> getRedirectUris() {
+ Set<String> result = new HashSet<String>();
+ result.addAll(entity.getRedirectUris());
+ return result;
+ }
+
+ @Override
+ public void setRedirectUris(Set<String> redirectUris) {
+ entity.setRedirectUris(redirectUris);
+ }
+
+ @Override
+ public void addRedirectUri(String redirectUri) {
+ entity.getRedirectUris().add(redirectUri);
+ }
+
+ @Override
+ public void removeRedirectUri(String redirectUri) {
+ entity.getRedirectUris().remove(redirectUri);
+ }
+
+ @Override
+ public String getSecret() {
+ return entity.getSecret();
+ }
+
+ @Override
+ public void setSecret(String secret) {
+ entity.setSecret(secret);
+ }
+
+ @Override
+ public boolean validateSecret(String secret) {
+ return secret.equals(entity.getSecret());
+ }
+
+ @Override
+ public int getNotBefore() {
+ return entity.getNotBefore();
+ }
+
+ @Override
+ public void setNotBefore(int notBefore) {
+ entity.setNotBefore(notBefore);
+ }
+
+ @Override
+ public Set<Role> getRealmScopeMappings() {
+ Set<Role> roleMappings = getScopeMappings();
+
+ Set<Role> appRoles = new HashSet<Role>();
+ for (Role role : roleMappings) {
+ RoleContainer container = role.getContainer();
+ if (container instanceof Realm) {
+ if (((Realm) container).getId().equals(entity.getRealm().getId())) {
+ appRoles.add(role);
+ }
+ }
+ }
+
+ return appRoles;
+ }
+
+
+
+ @Override
+ public Set<Role> getScopeMappings() {
+ TypedQuery<String> query = em.createNamedQuery("clientScopeMappingIds", String.class);
+ query.setParameter("client", getEntity());
+ List<String> ids = query.getResultList();
+ Set<Role> roles = new HashSet<Role>();
+ for (String roleId : ids) {
+ Role role = provider.getRoleById(roleId, entity.getRealm().getId());
+ if (role == null) continue;
+ roles.add(role);
+ }
+ return roles;
+ }
+
+ @Override
+ public void addScopeMapping(Role role) {
+ ScopeMappingEntity entity = new ScopeMappingEntity();
+ entity.setClient(getEntity());
+ entity.setRole(((RoleAdapter) role).getRole());
+ em.persist(entity);
+ em.flush();
+ em.detach(entity);
+ }
+
+ @Override
+ public void deleteScopeMapping(Role role) {
+ TypedQuery<ScopeMappingEntity> query = getRealmScopeMappingQuery((RoleAdapter) role);
+ List<ScopeMappingEntity> results = query.getResultList();
+ if (results.size() == 0) return;
+ for (ScopeMappingEntity entity : results) {
+ em.remove(entity);
+ }
+ }
+
+ protected TypedQuery<ScopeMappingEntity> getRealmScopeMappingQuery(RoleAdapter role) {
+ TypedQuery<ScopeMappingEntity> query = em.createNamedQuery("hasScope", ScopeMappingEntity.class);
+ query.setParameter("client", getEntity());
+ query.setParameter("role", role.getRole());
+ return query;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!this.getClass().equals(o.getClass())) return false;
+
+ ClientAdapter that = (ClientAdapter) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return entity.getId().hashCode();
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ApplicationEntity.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ApplicationEntity.java
new file mode 100755
index 0000000..6093fbc
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ApplicationEntity.java
@@ -0,0 +1,77 @@
+package org.keycloak.models.realms.jpa.entities;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinTable;
+import javax.persistence.OneToMany;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+public class ApplicationEntity extends ClientEntity {
+
+ private boolean surrogateAuthRequired;
+ private String baseUrl;
+ private String managementUrl;
+ private boolean bearerOnly;
+
+ @OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "application")
+ Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="ApplicationDefaultRoles")
+ Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
+
+ public boolean isSurrogateAuthRequired() {
+ return surrogateAuthRequired;
+ }
+
+ public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
+ this.surrogateAuthRequired = surrogateAuthRequired;
+ }
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ public String getManagementUrl() {
+ return managementUrl;
+ }
+
+ public void setManagementUrl(String managementUrl) {
+ this.managementUrl = managementUrl;
+ }
+
+ public Collection<RoleEntity> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Collection<RoleEntity> roles) {
+ this.roles = roles;
+ }
+
+ public Collection<RoleEntity> getDefaultRoles() {
+ return defaultRoles;
+ }
+
+ public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
+ this.defaultRoles = defaultRoles;
+ }
+
+ public boolean isBearerOnly() {
+ return bearerOnly;
+ }
+
+ public void setBearerOnly(boolean bearerOnly) {
+ this.bearerOnly = bearerOnly;
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/AuthenticationProviderEntity.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/AuthenticationProviderEntity.java
new file mode 100644
index 0000000..261a10b
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/AuthenticationProviderEntity.java
@@ -0,0 +1,79 @@
+package org.keycloak.models.realms.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.Table;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@Entity
+@Table(name="AuthProviderEntity")
+public class AuthenticationProviderEntity {
+
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.realms.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ protected String id;
+
+ private String providerName;
+ private boolean passwordUpdateSupported;
+ private int priority;
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable(name="AuthProviderEntity_cfg", joinColumns = {
+ @JoinColumn(name = "AuthProviderEntity_id")
+ })
+ private Map<String, String> config;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ public boolean isPasswordUpdateSupported() {
+ return passwordUpdateSupported;
+ }
+
+ public void setPasswordUpdateSupported(boolean passwordUpdateSupported) {
+ this.passwordUpdateSupported = passwordUpdateSupported;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public Map<String, String> getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ClientEntity.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ClientEntity.java
new file mode 100755
index 0000000..05eb5e0
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/ClientEntity.java
@@ -0,0 +1,126 @@
+package org.keycloak.models.realms.jpa.entities;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"realm", "name"})})
+public abstract class ClientEntity {
+ @Id
+ private String id;
+ @Column(name = "name")
+ private String name;
+ private boolean enabled;
+ private String secret;
+ private long allowedClaimsMask;
+ private int notBefore;
+ private boolean publicClient;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "realm")
+ protected RealmEntity realm;
+
+ @ElementCollection
+ @CollectionTable
+ protected Set<String> webOrigins = new HashSet<String>();
+ @ElementCollection
+ @CollectionTable
+ protected Set<String> redirectUris = new HashSet<String>();
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getAllowedClaimsMask() {
+ return allowedClaimsMask;
+ }
+
+ public void setAllowedClaimsMask(long allowedClaimsMask) {
+ this.allowedClaimsMask = allowedClaimsMask;
+ }
+
+ public Set<String> getWebOrigins() {
+ return webOrigins;
+ }
+
+ public void setWebOrigins(Set<String> webOrigins) {
+ this.webOrigins = webOrigins;
+ }
+
+ public Set<String> getRedirectUris() {
+ return redirectUris;
+ }
+
+ public void setRedirectUris(Set<String> redirectUris) {
+ this.redirectUris = redirectUris;
+ }
+
+ public String getSecret() {
+ return secret;
+ }
+
+ public void setSecret(String secret) {
+ this.secret = secret;
+ }
+
+ public int getNotBefore() {
+ return notBefore;
+ }
+
+ public void setNotBefore(int notBefore) {
+ this.notBefore = notBefore;
+ }
+
+ public boolean isPublicClient() {
+ return publicClient;
+ }
+
+ public void setPublicClient(boolean publicClient) {
+ this.publicClient = publicClient;
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/OAuthClientEntity.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/OAuthClientEntity.java
new file mode 100755
index 0000000..55e351a
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/OAuthClientEntity.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.realms.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="findOAuthClientByName", query="select o from OAuthClientEntity o where o.name=:name and o.realm = :realm"),
+ @NamedQuery(name="findOAuthClientByRealm", query="select o from OAuthClientEntity o where o.realm = :realm")
+
+})
+@Entity
+public class OAuthClientEntity extends ClientEntity {
+ protected boolean directGrantsOnly;
+
+ public boolean isDirectGrantsOnly() {
+ return directGrantsOnly;
+ }
+
+ public void setDirectGrantsOnly(boolean directGrantsOnly) {
+ this.directGrantsOnly = directGrantsOnly;
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RealmEntity.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RealmEntity.java
new file mode 100755
index 0000000..78c36d4
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RealmEntity.java
@@ -0,0 +1,479 @@
+package org.keycloak.models.realms.jpa.entities;
+
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinTable;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@NamedQueries({
+ @NamedQuery(name="getAllRealms", query="select realm from RealmEntity realm"),
+ @NamedQuery(name="getRealmByName", query="select realm from RealmEntity realm where realm.name = :name"),
+})
+public class RealmEntity {
+ @Id
+ protected String id;
+
+ @Column(unique = true)
+ protected String name;
+
+ protected boolean enabled;
+ protected boolean sslNotRequired;
+ protected boolean registrationAllowed;
+ protected boolean passwordCredentialGrantAllowed;
+ protected boolean verifyEmail;
+ protected boolean resetPasswordAllowed;
+ protected boolean social;
+ protected boolean rememberMe;
+ //--- brute force settings
+ protected boolean bruteForceProtected;
+ protected int maxFailureWaitSeconds;
+ protected int minimumQuickLoginWaitSeconds;
+ protected int waitIncrementSeconds;
+ protected long quickLoginCheckMilliSeconds;
+ protected int maxDeltaTimeSeconds;
+ protected int failureFactor;
+ //--- end brute force settings
+
+
+ @Column(name="updateProfileOnInitSocLogin")
+ protected boolean updateProfileOnInitialSocialLogin;
+ protected String passwordPolicy;
+
+ private int ssoSessionIdleTimeout;
+ private int ssoSessionMaxLifespan;
+ protected int accessTokenLifespan;
+ protected int accessCodeLifespan;
+ protected int accessCodeLifespanUserAction;
+ protected int notBefore;
+
+ @Column(length = 2048)
+ protected String publicKeyPem;
+ @Column(length = 2048)
+ protected String privateKeyPem;
+
+ protected String loginTheme;
+ protected String accountTheme;
+ protected String adminTheme;
+ protected String emailTheme;
+
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="User_RequiredCreds")
+ Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
+
+
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="AuthProviders")
+ List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ Collection<ApplicationEntity> applications = new ArrayList<ApplicationEntity>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+ Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map<String, String> smtpConfig = new HashMap<String, String>();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map<String, String> socialConfig = new HashMap<String, String>();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map<String, String> ldapServerConfig = new HashMap<String, String>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="RealmDefaultRoles")
+ protected Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
+
+ protected boolean auditEnabled;
+ protected long auditExpiration;
+
+ @ElementCollection
+ protected Set<String> auditListeners= new HashSet<String>();
+
+ @OneToOne
+ protected ApplicationEntity masterAdminApp;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isSslNotRequired() {
+ return sslNotRequired;
+ }
+
+ public void setSslNotRequired(boolean sslNotRequired) {
+ this.sslNotRequired = sslNotRequired;
+ }
+
+ public boolean isPasswordCredentialGrantAllowed() {
+ return passwordCredentialGrantAllowed;
+ }
+
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
+ }
+
+ public boolean isRegistrationAllowed() {
+ return registrationAllowed;
+ }
+
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ this.registrationAllowed = registrationAllowed;
+ }
+
+ public boolean isRememberMe() {
+ return rememberMe;
+ }
+
+ public void setRememberMe(boolean rememberMe) {
+ this.rememberMe = rememberMe;
+ }
+
+ public boolean isVerifyEmail() {
+ return verifyEmail;
+ }
+
+ public void setVerifyEmail(boolean verifyEmail) {
+ this.verifyEmail = verifyEmail;
+ }
+
+ public boolean isResetPasswordAllowed() {
+ return resetPasswordAllowed;
+ }
+
+ public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
+ this.resetPasswordAllowed = resetPasswordAllowed;
+ }
+
+ public boolean isSocial() {
+ return social;
+ }
+
+ public void setSocial(boolean social) {
+ this.social = social;
+ }
+
+ public boolean isUpdateProfileOnInitialSocialLogin() {
+ return updateProfileOnInitialSocialLogin;
+ }
+
+ public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+ this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
+ }
+
+ public int getSsoSessionIdleTimeout() {
+ return ssoSessionIdleTimeout;
+ }
+
+ public void setSsoSessionIdleTimeout(int ssoSessionIdleTimeout) {
+ this.ssoSessionIdleTimeout = ssoSessionIdleTimeout;
+ }
+
+ public int getSsoSessionMaxLifespan() {
+ return ssoSessionMaxLifespan;
+ }
+
+ public void setSsoSessionMaxLifespan(int ssoSessionMaxLifespan) {
+ this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
+ }
+
+ public int getAccessTokenLifespan() {
+ return accessTokenLifespan;
+ }
+
+ public void setAccessTokenLifespan(int accessTokenLifespan) {
+ this.accessTokenLifespan = accessTokenLifespan;
+ }
+
+ public int getAccessCodeLifespan() {
+ return accessCodeLifespan;
+ }
+
+ public void setAccessCodeLifespan(int accessCodeLifespan) {
+ this.accessCodeLifespan = accessCodeLifespan;
+ }
+
+ public int getAccessCodeLifespanUserAction() {
+ return accessCodeLifespanUserAction;
+ }
+
+ public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
+ this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
+ }
+
+ public String getPublicKeyPem() {
+ return publicKeyPem;
+ }
+
+ public void setPublicKeyPem(String publicKeyPem) {
+ this.publicKeyPem = publicKeyPem;
+ }
+
+ public String getPrivateKeyPem() {
+ return privateKeyPem;
+ }
+
+ public void setPrivateKeyPem(String privateKeyPem) {
+ this.privateKeyPem = privateKeyPem;
+ }
+
+ public Collection<RequiredCredentialEntity> getRequiredCredentials() {
+ return requiredCredentials;
+ }
+
+ public void setRequiredCredentials(Collection<RequiredCredentialEntity> requiredCredentials) {
+ this.requiredCredentials = requiredCredentials;
+ }
+
+ public List<AuthenticationProviderEntity> getAuthenticationProviders() {
+ return authenticationProviders;
+ }
+
+ public void setAuthenticationProviders(List<AuthenticationProviderEntity> authenticationProviders) {
+ this.authenticationProviders = authenticationProviders;
+ }
+
+ public Collection<ApplicationEntity> getApplications() {
+ return applications;
+ }
+
+ public void setApplications(Collection<ApplicationEntity> applications) {
+ this.applications = applications;
+ }
+
+ public Collection<RoleEntity> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Collection<RoleEntity> roles) {
+ this.roles = roles;
+ }
+
+ public void addRole(RoleEntity role) {
+ if (roles == null) {
+ roles = new ArrayList<RoleEntity>();
+ }
+ roles.add(role);
+ }
+
+ public Map<String, String> getSmtpConfig() {
+ return smtpConfig;
+ }
+
+ public void setSmtpConfig(Map<String, String> smtpConfig) {
+ this.smtpConfig = smtpConfig;
+ }
+
+ public Map<String, String> getSocialConfig() {
+ return socialConfig;
+ }
+
+ public void setSocialConfig(Map<String, String> socialConfig) {
+ this.socialConfig = socialConfig;
+ }
+
+ public Map<String, String> getLdapServerConfig() {
+ return ldapServerConfig;
+ }
+
+ public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
+ this.ldapServerConfig = ldapServerConfig;
+ }
+
+ public Collection<RoleEntity> getDefaultRoles() {
+ return defaultRoles;
+ }
+
+ public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
+ this.defaultRoles = defaultRoles;
+ }
+
+ public String getPasswordPolicy() {
+ return passwordPolicy;
+ }
+
+ public void setPasswordPolicy(String passwordPolicy) {
+ this.passwordPolicy = passwordPolicy;
+ }
+
+ public String getLoginTheme() {
+ return loginTheme;
+ }
+
+ public void setLoginTheme(String theme) {
+ this.loginTheme = theme;
+ }
+
+ public String getAccountTheme() {
+ return accountTheme;
+ }
+
+ public void setAccountTheme(String theme) {
+ this.accountTheme = theme;
+ }
+
+ public String getAdminTheme() {
+ return adminTheme;
+ }
+
+ public void setAdminTheme(String adminTheme) {
+ this.adminTheme = adminTheme;
+ }
+
+ public String getEmailTheme() {
+ return emailTheme;
+ }
+
+ public void setEmailTheme(String emailTheme) {
+ this.emailTheme = emailTheme;
+ }
+
+ public int getNotBefore() {
+ return notBefore;
+ }
+
+ public void setNotBefore(int notBefore) {
+ this.notBefore = notBefore;
+ }
+
+ public boolean isBruteForceProtected() {
+ return bruteForceProtected;
+ }
+
+ public void setBruteForceProtected(boolean bruteForceProtected) {
+ this.bruteForceProtected = bruteForceProtected;
+ }
+
+ public int getMaxFailureWaitSeconds() {
+ return maxFailureWaitSeconds;
+ }
+
+ public void setMaxFailureWaitSeconds(int maxFailureWaitSeconds) {
+ this.maxFailureWaitSeconds = maxFailureWaitSeconds;
+ }
+
+ public int getMinimumQuickLoginWaitSeconds() {
+ return minimumQuickLoginWaitSeconds;
+ }
+
+ public void setMinimumQuickLoginWaitSeconds(int minimumQuickLoginWaitSeconds) {
+ this.minimumQuickLoginWaitSeconds = minimumQuickLoginWaitSeconds;
+ }
+
+ public int getWaitIncrementSeconds() {
+ return waitIncrementSeconds;
+ }
+
+ public void setWaitIncrementSeconds(int waitIncrementSeconds) {
+ this.waitIncrementSeconds = waitIncrementSeconds;
+ }
+
+ public long getQuickLoginCheckMilliSeconds() {
+ return quickLoginCheckMilliSeconds;
+ }
+
+ public void setQuickLoginCheckMilliSeconds(long quickLoginCheckMilliSeconds) {
+ this.quickLoginCheckMilliSeconds = quickLoginCheckMilliSeconds;
+ }
+
+ public int getMaxDeltaTimeSeconds() {
+ return maxDeltaTimeSeconds;
+ }
+
+ public void setMaxDeltaTimeSeconds(int maxDeltaTimeSeconds) {
+ this.maxDeltaTimeSeconds = maxDeltaTimeSeconds;
+ }
+
+ public int getFailureFactor() {
+ return failureFactor;
+ }
+
+ public void setFailureFactor(int failureFactor) {
+ this.failureFactor = failureFactor;
+ }
+
+ public boolean isAuditEnabled() {
+ return auditEnabled;
+ }
+
+ public void setAuditEnabled(boolean auditEnabled) {
+ this.auditEnabled = auditEnabled;
+ }
+
+ public long getAuditExpiration() {
+ return auditExpiration;
+ }
+
+ public void setAuditExpiration(long auditExpiration) {
+ this.auditExpiration = auditExpiration;
+ }
+
+ public Set<String> getAuditListeners() {
+ return auditListeners;
+ }
+
+ public void setAuditListeners(Set<String> auditListeners) {
+ this.auditListeners = auditListeners;
+ }
+
+ public ApplicationEntity getMasterAdminApp() {
+ return masterAdminApp;
+ }
+
+ public void setMasterAdminApp(ApplicationEntity masterAdminApp) {
+ this.masterAdminApp = masterAdminApp;
+ }
+
+}
+
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RequiredCredentialEntity.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RequiredCredentialEntity.java
new file mode 100755
index 0000000..6c14c86
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RequiredCredentialEntity.java
@@ -0,0 +1,64 @@
+package org.keycloak.models.realms.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+public class RequiredCredentialEntity {
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.realms.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ protected String id;
+
+ protected String type;
+ protected boolean input;
+ protected boolean secret;
+ protected String formLabel;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public boolean isInput() {
+ return input;
+ }
+
+ public void setInput(boolean input) {
+ this.input = input;
+ }
+
+ public boolean isSecret() {
+ return secret;
+ }
+
+ public void setSecret(boolean secret) {
+ this.secret = secret;
+ }
+
+ public String getFormLabel() {
+ return formLabel;
+ }
+
+ public void setFormLabel(String formLabel) {
+ this.formLabel = formLabel;
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RoleEntity.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RoleEntity.java
new file mode 100755
index 0000000..36775ad
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/entities/RoleEntity.java
@@ -0,0 +1,152 @@
+package org.keycloak.models.realms.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@Table(uniqueConstraints = {
+ @UniqueConstraint(columnNames = { "name", "appRealmConstraint" })
+})
+@NamedQueries({
+ @NamedQuery(name="getAppRoleByName", query="select role from RoleEntity role where role.name = :name and role.application = :application"),
+ @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.applicationRole = false and role.name = :name and role.realm = :realm")
+})
+
+public class RoleEntity {
+ @Id
+ @Column(name="id")
+ private String id;
+
+ private String name;
+ private String description;
+
+ // hax! couldn't get constraint to work properly
+ private String realmId;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "realm")
+ private RealmEntity realm;
+
+ @Column(name="applicationRole")
+ private boolean applicationRole;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "application")
+ private ApplicationEntity application;
+
+ // Hack to ensure that either name+application or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
+ private String appRealmConstraint;
+
+ @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+ @JoinTable(name = "CompositeRole", joinColumns = @JoinColumn(name = "composite"), inverseJoinColumns = @JoinColumn(name = "childRole"))
+ private Collection<RoleEntity> compositeRoles = new ArrayList<RoleEntity>();
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getRealmId() {
+ return realmId;
+ }
+
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Collection<RoleEntity> getCompositeRoles() {
+ return compositeRoles;
+ }
+
+ public void setCompositeRoles(Collection<RoleEntity> compositeRoles) {
+ this.compositeRoles = compositeRoles;
+ }
+
+ public boolean isApplicationRole() {
+ return applicationRole;
+ }
+
+ public void setApplicationRole(boolean applicationRole) {
+ this.applicationRole = applicationRole;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ this.appRealmConstraint = realm.getId();
+ }
+
+ public ApplicationEntity getApplication() {
+ return application;
+ }
+
+ public void setApplication(ApplicationEntity application) {
+ this.application = application;
+ if (application != null) {
+ this.appRealmConstraint = application.getId();
+ }
+ }
+
+ public String getAppRealmConstraint() {
+ return appRealmConstraint;
+ }
+
+ public void setAppRealmConstraint(String appRealmConstraint) {
+ this.appRealmConstraint = appRealmConstraint;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RoleEntity that = (RoleEntity) o;
+
+ if (!id.equals(that.id)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaKeycloakTransaction.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaKeycloakTransaction.java
new file mode 100755
index 0000000..fd41714
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaKeycloakTransaction.java
@@ -0,0 +1,53 @@
+package org.keycloak.models.realms.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaKeycloakTransaction implements KeycloakTransaction {
+
+ protected EntityManager em;
+
+ public JpaKeycloakTransaction(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public void begin() {
+ em.getTransaction().begin();
+ }
+
+ @Override
+ public void commit() {
+ try {
+ em.getTransaction().commit();
+ } catch (PersistenceException e) {
+ throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
+ }
+ }
+
+ @Override
+ public void rollback() {
+ em.getTransaction().rollback();
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ em.getTransaction().setRollbackOnly();
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ return em.getTransaction().getRollbackOnly();
+ }
+
+ @Override
+ public boolean isActive() {
+ return em.getTransaction().isActive();
+ }
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaRealmProvider.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaRealmProvider.java
new file mode 100755
index 0000000..00c833c
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/JpaRealmProvider.java
@@ -0,0 +1,135 @@
+package org.keycloak.models.realms.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.realms.Application;
+import org.keycloak.models.realms.OAuthClient;
+import org.keycloak.models.realms.Realm;
+import org.keycloak.models.realms.RealmProvider;
+import org.keycloak.models.realms.Role;
+import org.keycloak.models.realms.jpa.entities.ApplicationEntity;
+import org.keycloak.models.realms.jpa.entities.OAuthClientEntity;
+import org.keycloak.models.realms.jpa.entities.RealmEntity;
+import org.keycloak.models.realms.jpa.entities.RoleEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaRealmProvider implements RealmProvider {
+
+ protected final EntityManager em;
+
+ public JpaRealmProvider(EntityManager em) {
+ this.em = PersistenceExceptionConverter.create(em);
+ }
+
+ @Override
+ public KeycloakTransaction getTransaction() {
+ return new JpaKeycloakTransaction(em);
+ }
+
+ @Override
+ public Realm createRealm(String name) {
+ return createRealm(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public Realm createRealm(String id, String name) {
+ RealmEntity realm = new RealmEntity();
+ realm.setName(name);
+ realm.setId(id);
+ em.persist(realm);
+ em.flush();
+ return new RealmAdapter(this, em, realm);
+ }
+
+ @Override
+ public Realm getRealm(String id) {
+ RealmEntity realm = em.find(RealmEntity.class, id);
+ if (realm == null) return null;
+ return new RealmAdapter(this, em, realm);
+ }
+
+ @Override
+ public List<Realm> getRealms() {
+ TypedQuery<RealmEntity> query = em.createNamedQuery("getAllRealms", RealmEntity.class);
+ List<RealmEntity> entities = query.getResultList();
+ List<Realm> realms = new ArrayList<Realm>();
+ for (RealmEntity entity : entities) {
+ realms.add(new RealmAdapter(this, em, entity));
+ }
+ return realms;
+ }
+
+ @Override
+ public Realm getRealmByName(String name) {
+ TypedQuery<RealmEntity> query = em.createNamedQuery("getRealmByName", RealmEntity.class);
+ query.setParameter("name", name);
+ List<RealmEntity> entities = query.getResultList();
+ if (entities.size() == 0) return null;
+ if (entities.size() > 1) throw new IllegalStateException("Should not be more than one realm with same name");
+ RealmEntity realm = query.getResultList().get(0);
+ if (realm == null) return null;
+ return new RealmAdapter(this, em, realm);
+ }
+
+ @Override
+ public boolean removeRealm(String id) {
+ RealmEntity realm = em.find(RealmEntity.class, id);
+ if (realm == null) {
+ return false;
+ }
+
+ RealmAdapter adapter = new RealmAdapter(this, em, realm);
+ for (Application a : adapter.getApplications()) {
+ adapter.removeApplication(a);
+ }
+
+ for (OAuthClient oauth : adapter.getOAuthClients()) {
+ adapter.removeOAuthClient(oauth);
+ }
+
+ em.remove(realm);
+
+ return true;
+ }
+
+ @Override
+ public void close() {
+ if (em.getTransaction().isActive()) em.getTransaction().rollback();
+ if (em.isOpen()) em.close();
+ }
+
+ @Override
+ public Role getRoleById(String id, String realm) {
+ RoleEntity entity = em.find(RoleEntity.class, id);
+ if (entity == null) return null;
+ if (!realm.equals(entity.getRealmId())) return null;
+ return new RoleAdapter(this, em, entity);
+ }
+
+ @Override
+ public Application getApplicationById(String id, String realm) {
+ ApplicationEntity app = em.find(ApplicationEntity.class, id);
+
+ // Check if application belongs to this realm
+ if (app == null || !realm.equals(app.getRealm().getId())) return null;
+ return new ApplicationAdapter(this, em, app);
+ }
+
+ @Override
+ public OAuthClient getOAuthClientById(String id, String realm) {
+ OAuthClientEntity client = em.find(OAuthClientEntity.class, id);
+
+ // Check if client belongs to this realm
+ if (client == null || !realm.equals(client.getRealm().getId())) return null;
+ return new OAuthClientAdapter(this, client, em);
+ }
+
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/OAuthClientAdapter.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/OAuthClientAdapter.java
new file mode 100755
index 0000000..b792a7d
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/OAuthClientAdapter.java
@@ -0,0 +1,52 @@
+package org.keycloak.models.realms.jpa;
+
+import org.keycloak.models.realms.OAuthClient;
+import org.keycloak.models.realms.RealmProvider;
+import org.keycloak.models.realms.jpa.entities.OAuthClientEntity;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OAuthClientAdapter extends ClientAdapter implements OAuthClient {
+
+ protected final OAuthClientEntity oAuthClientEntity;
+
+ public OAuthClientAdapter(RealmProvider provider, OAuthClientEntity entity, EntityManager em) {
+ super(provider, entity, em);
+ oAuthClientEntity = entity;
+ }
+
+ @Override
+ public void setClientId(String id) {
+ entity.setName(id);
+
+ }
+
+ @Override
+ public boolean isDirectGrantsOnly() {
+ return oAuthClientEntity.isDirectGrantsOnly();
+ }
+
+ @Override
+ public void setDirectGrantsOnly(boolean flag) {
+ oAuthClientEntity.setDirectGrantsOnly(flag);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof OAuthClient)) return false;
+
+ OAuthClient that = (OAuthClient) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/PersistenceExceptionConverter.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/PersistenceExceptionConverter.java
new file mode 100644
index 0000000..f479939
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/PersistenceExceptionConverter.java
@@ -0,0 +1,48 @@
+package org.keycloak.models.realms.jpa;
+
+import org.hibernate.exception.ConstraintViolationException;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class PersistenceExceptionConverter implements InvocationHandler {
+
+ private EntityManager em;
+
+ public static EntityManager create(EntityManager em) {
+ return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
+ }
+
+ private PersistenceExceptionConverter(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ return method.invoke(em, args);
+ } catch (InvocationTargetException e) {
+ throw convert(e.getCause());
+ }
+ }
+
+ public static ModelException convert(Throwable t) {
+ if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
+ throw new ModelDuplicateException(t);
+ } if (t instanceof EntityExistsException) {
+ throw new ModelDuplicateException(t);
+ } else {
+ throw new ModelException(t);
+ }
+ }
+
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/RealmAdapter.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/RealmAdapter.java
new file mode 100755
index 0000000..ffbbacf
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/RealmAdapter.java
@@ -0,0 +1,853 @@
+package org.keycloak.models.realms.jpa;
+
+import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.realms.Application;
+import org.keycloak.models.realms.Client;
+import org.keycloak.models.realms.OAuthClient;
+import org.keycloak.models.realms.Realm;
+import org.keycloak.models.realms.RealmProvider;
+import org.keycloak.models.realms.Role;
+import org.keycloak.models.realms.jpa.entities.ApplicationEntity;
+import org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity;
+import org.keycloak.models.realms.jpa.entities.OAuthClientEntity;
+import org.keycloak.models.realms.jpa.entities.RealmEntity;
+import org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity;
+import org.keycloak.models.realms.jpa.entities.RoleEntity;
+import org.keycloak.models.realms.jpa.entities.ScopeMappingEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmAdapter implements Realm {
+ protected RealmEntity realm;
+ protected RealmProvider provider;
+ protected EntityManager em;
+ protected volatile transient PublicKey publicKey;
+ protected volatile transient PrivateKey privateKey;
+ protected PasswordPolicy passwordPolicy;
+
+ public RealmAdapter(RealmProvider provider, EntityManager em, RealmEntity realm) {
+ this.provider = provider;
+ this.em = em;
+ this.realm = realm;
+ }
+
+ public RealmEntity getEntity() {
+ return realm;
+ }
+
+ @Override
+ public String getId() {
+ return realm.getId();
+ }
+
+ @Override
+ public String getName() {
+ return realm.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ realm.setName(name);
+ em.flush();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return realm.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ realm.setEnabled(enabled);
+ em.flush();
+ }
+
+ @Override
+ public boolean isSslNotRequired() {
+ return realm.isSslNotRequired();
+ }
+
+ @Override
+ public void setSslNotRequired(boolean sslNotRequired) {
+ realm.setSslNotRequired(sslNotRequired);
+ em.flush();
+ }
+
+ @Override
+ public boolean isPasswordCredentialGrantAllowed() {
+ return realm.isPasswordCredentialGrantAllowed();
+ }
+
+ @Override
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
+ em.flush();
+ }
+
+ @Override
+ public boolean isRegistrationAllowed() {
+ return realm.isRegistrationAllowed();
+ }
+
+ @Override
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ realm.setRegistrationAllowed(registrationAllowed);
+ em.flush();
+ }
+
+ @Override
+ public boolean isRememberMe() {
+ return realm.isRememberMe();
+ }
+
+ @Override
+ public void setRememberMe(boolean rememberMe) {
+ realm.setRememberMe(rememberMe);
+ em.flush();
+ }
+
+ @Override
+ public boolean isBruteForceProtected() {
+ return realm.isBruteForceProtected();
+ }
+
+ @Override
+ public void setBruteForceProtected(boolean value) {
+ realm.setBruteForceProtected(value);
+ }
+
+ @Override
+ public int getMaxFailureWaitSeconds() {
+ return realm.getMaxFailureWaitSeconds();
+ }
+
+ @Override
+ public void setMaxFailureWaitSeconds(int val) {
+ realm.setMaxFailureWaitSeconds(val);
+ }
+
+ @Override
+ public int getWaitIncrementSeconds() {
+ return realm.getWaitIncrementSeconds();
+ }
+
+ @Override
+ public void setWaitIncrementSeconds(int val) {
+ realm.setWaitIncrementSeconds(val);
+ }
+
+ @Override
+ public long getQuickLoginCheckMilliSeconds() {
+ return realm.getQuickLoginCheckMilliSeconds();
+ }
+
+ @Override
+ public void setQuickLoginCheckMilliSeconds(long val) {
+ realm.setQuickLoginCheckMilliSeconds(val);
+ }
+
+ @Override
+ public int getMinimumQuickLoginWaitSeconds() {
+ return realm.getMinimumQuickLoginWaitSeconds();
+ }
+
+ @Override
+ public void setMinimumQuickLoginWaitSeconds(int val) {
+ realm.setMinimumQuickLoginWaitSeconds(val);
+ }
+
+ @Override
+ public int getMaxDeltaTimeSeconds() {
+ return realm.getMaxDeltaTimeSeconds();
+ }
+
+ @Override
+ public void setMaxDeltaTimeSeconds(int val) {
+ realm.setMaxDeltaTimeSeconds(val);
+ }
+
+ @Override
+ public int getFailureFactor() {
+ return realm.getFailureFactor();
+ }
+
+ @Override
+ public void setFailureFactor(int failureFactor) {
+ realm.setFailureFactor(failureFactor);
+ }
+
+ @Override
+ public boolean isVerifyEmail() {
+ return realm.isVerifyEmail();
+ }
+
+ @Override
+ public void setVerifyEmail(boolean verifyEmail) {
+ realm.setVerifyEmail(verifyEmail);
+ em.flush();
+ }
+
+ @Override
+ public boolean isResetPasswordAllowed() {
+ return realm.isResetPasswordAllowed();
+ }
+
+ @Override
+ public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
+ realm.setResetPasswordAllowed(resetPasswordAllowed);
+ em.flush();
+ }
+
+ @Override
+ public int getNotBefore() {
+ return realm.getNotBefore();
+ }
+
+ @Override
+ public void setNotBefore(int notBefore) {
+ realm.setNotBefore(notBefore);
+ }
+
+ @Override
+ public int getAccessTokenLifespan() {
+ return realm.getAccessTokenLifespan();
+ }
+
+ @Override
+ public void setAccessTokenLifespan(int tokenLifespan) {
+ realm.setAccessTokenLifespan(tokenLifespan);
+ em.flush();
+ }
+
+ @Override
+ public int getSsoSessionIdleTimeout() {
+ return realm.getSsoSessionIdleTimeout();
+ }
+
+ @Override
+ public void setSsoSessionIdleTimeout(int seconds) {
+ realm.setSsoSessionIdleTimeout(seconds);
+ }
+
+ @Override
+ public int getSsoSessionMaxLifespan() {
+ return realm.getSsoSessionMaxLifespan();
+ }
+
+ @Override
+ public void setSsoSessionMaxLifespan(int seconds) {
+ realm.setSsoSessionMaxLifespan(seconds);
+ }
+
+ @Override
+ public int getAccessCodeLifespan() {
+ return realm.getAccessCodeLifespan();
+ }
+
+ @Override
+ public void setAccessCodeLifespan(int accessCodeLifespan) {
+ realm.setAccessCodeLifespan(accessCodeLifespan);
+ em.flush();
+ }
+
+ @Override
+ public int getAccessCodeLifespanUserAction() {
+ return realm.getAccessCodeLifespanUserAction();
+ }
+
+ @Override
+ public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
+ realm.setAccessCodeLifespanUserAction(accessCodeLifespanUserAction);
+ em.flush();
+ }
+
+ @Override
+ public String getPublicKeyPem() {
+ return realm.getPublicKeyPem();
+ }
+
+ @Override
+ public void setPublicKeyPem(String publicKeyPem) {
+ realm.setPublicKeyPem(publicKeyPem);
+ em.flush();
+ }
+
+ @Override
+ public String getPrivateKeyPem() {
+ return realm.getPrivateKeyPem();
+ }
+
+ @Override
+ public void setPrivateKeyPem(String privateKeyPem) {
+ realm.setPrivateKeyPem(privateKeyPem);
+ em.flush();
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ if (publicKey != null) return publicKey;
+ publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
+ return publicKey;
+ }
+
+ @Override
+ public void setPublicKey(PublicKey publicKey) {
+ this.publicKey = publicKey;
+ String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+ setPublicKeyPem(publicKeyPem);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey() {
+ if (privateKey != null) return privateKey;
+ privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
+ return privateKey;
+ }
+
+ @Override
+ public void setPrivateKey(PrivateKey privateKey) {
+ this.privateKey = privateKey;
+ String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+ setPrivateKeyPem(privateKeyPem);
+ }
+
+ protected RequiredCredentialModel initRequiredCredentialModel(String type) {
+ RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type);
+ if (model == null) {
+ throw new RuntimeException("Unknown credential type " + type);
+ }
+ return model;
+ }
+
+ @Override
+ public void addRequiredCredential(String type) {
+ RequiredCredentialModel model = initRequiredCredentialModel(type);
+ addRequiredCredential(model);
+ em.flush();
+ }
+
+ public void addRequiredCredential(RequiredCredentialModel model) {
+ RequiredCredentialEntity entity = new RequiredCredentialEntity();
+ entity.setInput(model.isInput());
+ entity.setSecret(model.isSecret());
+ entity.setType(model.getType());
+ entity.setFormLabel(model.getFormLabel());
+ em.persist(entity);
+ realm.getRequiredCredentials().add(entity);
+ em.flush();
+ }
+
+ @Override
+ public void updateRequiredCredentials(Set<String> creds) {
+ Collection<RequiredCredentialEntity> relationships = realm.getRequiredCredentials();
+ if (relationships == null) relationships = new ArrayList<RequiredCredentialEntity>();
+
+ Set<String> already = new HashSet<String>();
+ List<RequiredCredentialEntity> remove = new ArrayList<RequiredCredentialEntity>();
+ for (RequiredCredentialEntity rel : relationships) {
+ if (!creds.contains(rel.getType())) {
+ remove.add(rel);
+ } else {
+ already.add(rel.getType());
+ }
+ }
+ for (RequiredCredentialEntity entity : remove) {
+ relationships.remove(entity);
+ em.remove(entity);
+ }
+ for (String cred : creds) {
+ if (!already.contains(cred)) {
+ addRequiredCredential(cred);
+ }
+ }
+ em.flush();
+ }
+
+
+ @Override
+ public List<RequiredCredentialModel> getRequiredCredentials() {
+ List<RequiredCredentialModel> requiredCredentialModels = new ArrayList<RequiredCredentialModel>();
+ Collection<RequiredCredentialEntity> entities = realm.getRequiredCredentials();
+ if (entities == null) return requiredCredentialModels;
+ for (RequiredCredentialEntity entity : entities) {
+ RequiredCredentialModel model = new RequiredCredentialModel();
+ model.setFormLabel(entity.getFormLabel());
+ model.setType(entity.getType());
+ model.setSecret(entity.isSecret());
+ model.setInput(entity.isInput());
+ requiredCredentialModels.add(model);
+ }
+ return requiredCredentialModels; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public List<String> getDefaultRoles() {
+ Collection<RoleEntity> entities = realm.getDefaultRoles();
+ List<String> roles = new ArrayList<String>();
+ if (entities == null) return roles;
+ for (RoleEntity entity : entities) {
+ roles.add(entity.getName());
+ }
+ return roles;
+ }
+
+ @Override
+ public void addDefaultRole(String name) {
+ Role role = getRole(name);
+ Collection<RoleEntity> entities = realm.getDefaultRoles();
+ for (RoleEntity entity : entities) {
+ if (entity.getId().equals(role.getId())) {
+ return;
+ }
+ }
+ realm.getDefaultRoles().add(((RoleAdapter) role).getRole());
+ em.flush();
+ }
+
+ public static boolean contains(String str, String[] array) {
+ for (String s : array) {
+ if (str.equals(s)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void updateDefaultRoles(String[] defaultRoles) {
+ Collection<RoleEntity> entities = realm.getDefaultRoles();
+ Set<String> already = new HashSet<String>();
+ List<RoleEntity> remove = new ArrayList<RoleEntity>();
+ for (RoleEntity rel : entities) {
+ if (!contains(rel.getName(), defaultRoles)) {
+ remove.add(rel);
+ } else {
+ already.add(rel.getName());
+ }
+ }
+ for (RoleEntity entity : remove) {
+ entities.remove(entity);
+ }
+ em.flush();
+ for (String roleName : defaultRoles) {
+ if (!already.contains(roleName)) {
+ addDefaultRole(roleName);
+ }
+ }
+ em.flush();
+ }
+
+ @Override
+ public Client findClient(String clientId) {
+ Client model = getApplicationByName(clientId);
+ if (model != null) return model;
+ return getOAuthClient(clientId);
+ }
+
+ @Override
+ public Map<String, Application> getApplicationNameMap() {
+ Map<String, Application> map = new HashMap<String, Application>();
+ for (Application app : getApplications()) {
+ map.put(app.getName(), app);
+ }
+ return map; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public List<Application> getApplications() {
+ List<Application> list = new ArrayList<Application>();
+ if (realm.getApplications() == null) return list;
+ for (ApplicationEntity entity : realm.getApplications()) {
+ list.add(new ApplicationAdapter(provider, em, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public Application addApplication(String id, String name) {
+ ApplicationEntity applicationData = new ApplicationEntity();
+ applicationData.setId(id);
+ applicationData.setName(name);
+ applicationData.setEnabled(true);
+ applicationData.setRealm(realm);
+ realm.getApplications().add(applicationData);
+ em.persist(applicationData);
+ em.flush();
+ Application resource = new ApplicationAdapter(provider, em, applicationData);
+ em.flush();
+ return resource;
+ }
+
+ @Override
+ public boolean removeApplication(Application application) {
+ for (Role role : application.getRoles()) {
+ application.removeRole(role);
+ }
+
+ ApplicationEntity applicationEntity = null;
+ Iterator<ApplicationEntity> it = realm.getApplications().iterator();
+ while (it.hasNext()) {
+ ApplicationEntity ae = it.next();
+ if (ae.getId().equals(application.getId())) {
+ applicationEntity = ae;
+ it.remove();
+ break;
+ }
+ }
+ for (ApplicationEntity a : realm.getApplications()) {
+ if (a.getId().equals(application.getId())) {
+ applicationEntity = a;
+ }
+ }
+ if (application == null) {
+ return false;
+ }
+ em.remove(applicationEntity);
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", applicationEntity).executeUpdate();
+
+ return true;
+ }
+
+ @Override
+ public Application getApplicationByName(String name) {
+ return getApplicationNameMap().get(name);
+ }
+
+ @Override
+ public boolean isSocial() {
+ return realm.isSocial();
+ }
+
+ @Override
+ public void setSocial(boolean social) {
+ realm.setSocial(social);
+ em.flush();
+ }
+
+ @Override
+ public boolean isUpdateProfileOnInitialSocialLogin() {
+ return realm.isUpdateProfileOnInitialSocialLogin();
+ }
+
+ @Override
+ public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+ realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
+ em.flush();
+ }
+
+ @Override
+ public OAuthClient addOAuthClient(String id, String name) {
+ OAuthClientEntity data = new OAuthClientEntity();
+ data.setId(id);
+ data.setEnabled(true);
+ data.setName(name);
+ data.setRealm(realm);
+ em.persist(data);
+ em.flush();
+ return new OAuthClientAdapter(provider, data, em);
+ }
+
+ @Override
+ public boolean removeOAuthClient(OAuthClient client) {
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", client).executeUpdate();
+ em.remove(client);
+ return true;
+ }
+
+
+ @Override
+ public OAuthClient getOAuthClient(String name) {
+ TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByName", OAuthClientEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("realm", realm);
+ List<OAuthClientEntity> entities = query.getResultList();
+ if (entities.size() == 0) return null;
+ return new OAuthClientAdapter(provider, entities.get(0), em);
+ }
+
+ @Override
+ public List<OAuthClient> getOAuthClients() {
+ TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByRealm", OAuthClientEntity.class);
+ query.setParameter("realm", realm);
+ List<OAuthClientEntity> entities = query.getResultList();
+ List<OAuthClient> list = new ArrayList<OAuthClient>();
+ for (OAuthClientEntity entity : entities) list.add(new OAuthClientAdapter(provider, entity, em));
+ return list;
+ }
+
+ @Override
+ public Map<String, String> getSmtpConfig() {
+ return realm.getSmtpConfig();
+ }
+
+ @Override
+ public void setSmtpConfig(Map<String, String> smtpConfig) {
+ realm.setSmtpConfig(smtpConfig);
+ em.flush();
+ }
+
+ @Override
+ public Map<String, String> getSocialConfig() {
+ return realm.getSocialConfig();
+ }
+
+ @Override
+ public void setSocialConfig(Map<String, String> socialConfig) {
+ realm.setSocialConfig(socialConfig);
+ em.flush();
+ }
+
+ @Override
+ public Map<String, String> getLdapServerConfig() {
+ return realm.getLdapServerConfig();
+ }
+
+ @Override
+ public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
+ realm.setLdapServerConfig(ldapServerConfig);
+ em.flush();
+ }
+
+ @Override
+ public List<AuthenticationProviderModel> getAuthenticationProviders() {
+ List<AuthenticationProviderEntity> entities = realm.getAuthenticationProviders();
+ List<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>();
+ for (AuthenticationProviderEntity entity : entities) {
+ copy.add(entity);
+
+ }
+ Collections.sort(copy, new Comparator<AuthenticationProviderEntity>() {
+
+ @Override
+ public int compare(AuthenticationProviderEntity o1, AuthenticationProviderEntity o2) {
+ return o1.getPriority() - o2.getPriority();
+ }
+
+ });
+ List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
+ for (AuthenticationProviderEntity entity : copy) {
+ result.add(new AuthenticationProviderModel(entity.getProviderName(), entity.isPasswordUpdateSupported(), entity.getConfig()));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders) {
+ List<AuthenticationProviderEntity> newEntities = new ArrayList<AuthenticationProviderEntity>();
+ int counter = 1;
+ for (AuthenticationProviderModel model : authenticationProviders) {
+ AuthenticationProviderEntity entity = new AuthenticationProviderEntity();
+ entity.setProviderName(model.getProviderName());
+ entity.setPasswordUpdateSupported(model.isPasswordUpdateSupported());
+ entity.setConfig(model.getConfig());
+ entity.setPriority(counter++);
+ newEntities.add(entity);
+ }
+
+ // Remove all existing first
+ Collection<AuthenticationProviderEntity> existing = realm.getAuthenticationProviders();
+ Collection<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>(existing);
+ for (AuthenticationProviderEntity apToRemove : copy) {
+ existing.remove(apToRemove);
+ em.remove(apToRemove);
+ }
+
+ // Now create all new providers
+ for (AuthenticationProviderEntity apToAdd : newEntities) {
+ existing.add(apToAdd);
+ em.persist(apToAdd);
+ }
+
+ em.flush();
+ }
+
+ @Override
+ public Role getRole(String name) {
+ TypedQuery<RoleEntity> query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("realm", realm);
+ List<RoleEntity> roles = query.getResultList();
+ if (roles.size() == 0) return null;
+ return new RoleAdapter(provider, em, roles.get(0));
+ }
+
+ @Override
+ public Role addRole(String id, String name) {
+ RoleEntity entity = new RoleEntity();
+ entity.setId(id);
+ entity.setName(name);
+ entity.setRealm(realm);
+ entity.setRealmId(realm.getId());
+ em.persist(entity);
+ realm.getRoles().add(entity);
+ em.flush();
+ return new RoleAdapter(provider, em, entity);
+ }
+
+ @Override
+ public boolean removeRole(Role role) {
+ if (role == null) {
+ return false;
+ }
+ if (!role.getContainer().equals(this)) return false;
+
+ RoleEntity roleEntity = ((RoleAdapter) role).getRole();
+ realm.getRoles().remove(role);
+ realm.getDefaultRoles().remove(role);
+
+ em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", roleEntity).executeUpdate();
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
+
+ em.remove(roleEntity);
+
+ return true;
+ }
+
+ @Override
+ public Set<Role> getRoles() {
+ Set<Role> list = new HashSet<Role>();
+ Collection<RoleEntity> roles = realm.getRoles();
+ if (roles == null) return list;
+ for (RoleEntity entity : roles) {
+ list.add(new RoleAdapter(provider, em, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public PasswordPolicy getPasswordPolicy() {
+ if (passwordPolicy == null) {
+ passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy());
+ }
+ return passwordPolicy;
+ }
+
+ @Override
+ public void setPasswordPolicy(PasswordPolicy policy) {
+ this.passwordPolicy = policy;
+ realm.setPasswordPolicy(policy.toString());
+ em.flush();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof Realm)) return false;
+
+ Realm that = (Realm) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ @Override
+ public String getLoginTheme() {
+ return realm.getLoginTheme();
+ }
+
+ @Override
+ public void setLoginTheme(String name) {
+ realm.setLoginTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public String getAccountTheme() {
+ return realm.getAccountTheme();
+ }
+
+ @Override
+ public void setAccountTheme(String name) {
+ realm.setAccountTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public String getAdminTheme() {
+ return realm.getAdminTheme();
+ }
+
+ @Override
+ public void setAdminTheme(String name) {
+ realm.setAdminTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public String getEmailTheme() {
+ return realm.getEmailTheme();
+ }
+
+ @Override
+ public void setEmailTheme(String name) {
+ realm.setEmailTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public boolean isAuditEnabled() {
+ return realm.isAuditEnabled();
+ }
+
+ @Override
+ public void setAuditEnabled(boolean enabled) {
+ realm.setAuditEnabled(enabled);
+ em.flush();
+ }
+
+ @Override
+ public long getAuditExpiration() {
+ return realm.getAuditExpiration();
+ }
+
+ @Override
+ public void setAuditExpiration(long expiration) {
+ realm.setAuditExpiration(expiration);
+ em.flush();
+ }
+
+ @Override
+ public Set<String> getAuditListeners() {
+ return realm.getAuditListeners();
+ }
+
+ @Override
+ public void setAuditListeners(Set<String> listeners) {
+ realm.setAuditListeners(listeners);
+ em.flush();
+ }
+
+ @Override
+ public Application getMasterAdminApp() {
+ return new ApplicationAdapter(provider, em, realm.getMasterAdminApp());
+ }
+
+ @Override
+ public void setMasterAdminApp(Application app) {
+ realm.setMasterAdminApp(((ApplicationAdapter) app).getJpaEntity());
+ em.flush();
+ }
+
+}
diff --git a/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/RoleAdapter.java b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/RoleAdapter.java
new file mode 100755
index 0000000..0063794
--- /dev/null
+++ b/model/realms-jpa/src/main/java/org/keycloak/models/realms/jpa/RoleAdapter.java
@@ -0,0 +1,117 @@
+package org.keycloak.models.realms.jpa;
+
+import org.keycloak.models.realms.RealmProvider;
+import org.keycloak.models.realms.Role;
+import org.keycloak.models.realms.RoleContainer;
+import org.keycloak.models.realms.jpa.entities.RoleEntity;
+
+import javax.persistence.EntityManager;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RoleAdapter implements Role {
+ protected RoleEntity role;
+ protected RealmProvider provider;
+ protected EntityManager em;
+
+ public RoleAdapter(RealmProvider provider, EntityManager em, RoleEntity role) {
+ this.provider = provider;
+ this.em = em;
+ this.role = role;
+ }
+
+ public RoleEntity getRole() {
+ return role;
+ }
+
+ public void setRole(RoleEntity role) {
+ this.role = role;
+ }
+
+ @Override
+ public String getName() {
+ return role.getName();
+ }
+
+ @Override
+ public String getDescription() {
+ return role.getDescription();
+ }
+
+ @Override
+ public void setDescription(String description) {
+ role.setDescription(description);
+ }
+
+ @Override
+ public String getId() {
+ return role.getId();
+ }
+
+ @Override
+ public void setName(String name) {
+ role.setName(name);
+ }
+
+ @Override
+ public boolean isComposite() {
+ return getComposites().size() > 0;
+ }
+
+ @Override
+ public void addCompositeRole(Role role) {
+ RoleEntity entity = ((RoleAdapter)role).getRole();
+ for (RoleEntity composite : getRole().getCompositeRoles()) {
+ if (composite.equals(entity)) return;
+ }
+ getRole().getCompositeRoles().add(entity);
+ em.flush();
+ }
+
+ @Override
+ public void removeCompositeRole(Role role) {
+ RoleEntity entity = ((RoleAdapter)role).getRole();
+ Iterator<RoleEntity> it = getRole().getCompositeRoles().iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(entity)) it.remove();
+ }
+ }
+
+ @Override
+ public Set<Role> getComposites() {
+ Set<Role> set = new HashSet<Role>();
+
+ for (RoleEntity composite : getRole().getCompositeRoles()) {
+ set.add(new RoleAdapter(provider, em, composite));
+ }
+ return set;
+ }
+
+ @Override
+ public RoleContainer getContainer() {
+ if (role.isApplicationRole()) {
+ return provider.getApplicationById(role.getApplication().getId(), role.getApplication().getRealm().getId());
+ } else {
+ return provider.getRealm(role.getRealm().getId());
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof Role)) return false;
+
+ Role that = (Role) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+}
diff --git a/model/realms-jpa/src/main/resources/META-INF/services/org.keycloak.models.realms.RealmProviderFactory b/model/realms-jpa/src/main/resources/META-INF/services/org.keycloak.models.realms.RealmProviderFactory
new file mode 100644
index 0000000..81ec90a
--- /dev/null
+++ b/model/realms-jpa/src/main/resources/META-INF/services/org.keycloak.models.realms.RealmProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.realms.jpa.JpaRealmProviderFactory
\ No newline at end of file
model/sessions-jpa/pom.xml 47(+47 -0)
diff --git a/model/sessions-jpa/pom.xml b/model/sessions-jpa/pom.xml
new file mode 100755
index 0000000..65007be
--- /dev/null
+++ b/model/sessions-jpa/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-model-sessions-jpa</artifactId>
+ <name>Keycloak Model Sessions JPA</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-jpa</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.javax.persistence</groupId>
+ <artifactId>hibernate-jpa-2.0-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>${hibernate.entitymanager.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientUserSessionAssociationEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientUserSessionAssociationEntity.java
new file mode 100755
index 0000000..99eb40e
--- /dev/null
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientUserSessionAssociationEntity.java
@@ -0,0 +1,97 @@
+package org.keycloak.models.sessions.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@Table(name = "CLIENT_USERSESSION")
+@NamedQueries({
+ @NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId)"),
+ @NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId)"),
+ @NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity a where a.clientId = :clientId and a.session IN (select s from UserSessionEntity s where s.realmId = :realmId)"),
+ @NamedQuery(name = "removeClientUserSessionByExpired", query = "delete from ClientUserSessionAssociationEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime))")
+})
+@IdClass(ClientUserSessionAssociationEntity.Key.class)
+public class ClientUserSessionAssociationEntity {
+
+ @Id
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "SESSION_ID")
+ protected UserSessionEntity session;
+
+ @Id
+ @Column(name="CLIENT_ID",length = 36)
+ protected String clientId;
+
+ public UserSessionEntity getSession() {
+ return session;
+ }
+
+ public void setSession(UserSessionEntity session) {
+ this.session = session;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public static class Key implements Serializable {
+
+ private String clientId;
+ private UserSessionEntity session;
+
+ public Key() {
+ }
+
+ public Key(String clientId, UserSessionEntity session) {
+ this.clientId = clientId;
+ this.session = session;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public UserSessionEntity getSession() {
+ return session;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (clientId != null ? !clientId.equals(key.clientId) : key.clientId != null) return false;
+ if (session != null ? !session.getId().equals(key.session != null ? key.session.getId() : null) : key.session != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientId != null ? clientId.hashCode() : 0;
+ result = 31 * result + (session != null ? session.getId().hashCode() : 0);
+ return result;
+ }
+ }
+
+}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaKeycloakTransaction.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaKeycloakTransaction.java
new file mode 100755
index 0000000..09f92c0
--- /dev/null
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaKeycloakTransaction.java
@@ -0,0 +1,53 @@
+package org.keycloak.models.sessions.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaKeycloakTransaction implements KeycloakTransaction {
+
+ protected EntityManager em;
+
+ public JpaKeycloakTransaction(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public void begin() {
+ em.getTransaction().begin();
+ }
+
+ @Override
+ public void commit() {
+ try {
+ em.getTransaction().commit();
+ } catch (PersistenceException e) {
+ throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
+ }
+ }
+
+ @Override
+ public void rollback() {
+ em.getTransaction().rollback();
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ em.getTransaction().setRollbackOnly();
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ return em.getTransaction().getRollbackOnly();
+ }
+
+ @Override
+ public boolean isActive() {
+ return em.getTransaction().isActive();
+ }
+}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
new file mode 100644
index 0000000..640af99
--- /dev/null
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
@@ -0,0 +1,198 @@
+package org.keycloak.models.sessions.jpa;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.UsernameLoginFailureModel;
+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.util.Time;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class JpaUserSessionProvider implements UserSessionProvider {
+
+ protected final KeycloakSession session;
+
+ protected final EntityManager em;
+
+ public JpaUserSessionProvider(KeycloakSession session, EntityManager em) {
+ this.session = session;
+ this.em = em;
+ }
+
+ @Override
+ public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
+ String id = username + "-" + realm;
+ UsernameLoginFailureEntity entity = em.find(UsernameLoginFailureEntity.class, id);
+ if (entity == null) return null;
+ return new UsernameLoginFailureAdapter(entity);
+ }
+
+ @Override
+ public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
+ UsernameLoginFailureModel model = getUserLoginFailure(realm, username);
+ if (model != null) return model;
+ UsernameLoginFailureEntity entity = new UsernameLoginFailureEntity();
+ entity.setUsername(username);
+ entity.setRealmId(realm.getId());
+ em.persist(entity);
+ return new UsernameLoginFailureAdapter(entity);
+ }
+
+ @Override
+ public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
+ TypedQuery<UsernameLoginFailureEntity> query = em.createNamedQuery("getAllFailures", UsernameLoginFailureEntity.class);
+ List<UsernameLoginFailureEntity> entities = query.getResultList();
+ List<UsernameLoginFailureModel> models = new ArrayList<UsernameLoginFailureModel>();
+ for (UsernameLoginFailureEntity entity : entities) {
+ models.add(new UsernameLoginFailureAdapter(entity));
+ }
+ return models;
+ }
+
+ @Override
+ public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
+ UserSessionEntity entity = new UserSessionEntity();
+ entity.setId(KeycloakModelUtils.generateId());
+ entity.setRealmId(realm.getId());
+ entity.setUserId(user.getId());
+ entity.setLoginUsername(loginUsername);
+ entity.setIpAddress(ipAddress);
+ entity.setAuthMethod(authMethod);
+ entity.setRememberMe(rememberMe);
+
+ int currentTime = Time.currentTime();
+
+ entity.setStarted(currentTime);
+ entity.setLastSessionRefresh(currentTime);
+
+ em.persist(entity);
+ return new UserSessionAdapter(session, em, realm, entity);
+ }
+
+ @Override
+ public UserSessionModel getUserSession(RealmModel realm, String id) {
+ UserSessionEntity entity = em.find(UserSessionEntity.class, id);
+ return entity != null ? new UserSessionAdapter(session, em, realm, entity) : null;
+ }
+
+ @Override
+ public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
+ List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+ TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class)
+ .setParameter("realmId", realm.getId())
+ .setParameter("userId", user.getId());
+ for (UserSessionEntity e : query.getResultList()) {
+ sessions.add(new UserSessionAdapter(session, em, realm, e));
+ }
+ return sessions;
+ }
+
+ @Override
+ public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
+ return getUserSessions(realm, client, -1, -1);
+ }
+
+ public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
+ List<UserSessionModel> list = new LinkedList<UserSessionModel>();
+ TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionByClient", UserSessionEntity.class)
+ .setParameter("realmId", realm.getId())
+ .setParameter("clientId", client.getClientId());
+ if (firstResult != -1) {
+ query.setFirstResult(firstResult);
+ }
+ if (maxResults != -1) {
+ query.setMaxResults(maxResults);
+ }
+ for (UserSessionEntity entity : query.getResultList()) {
+ list.add(new UserSessionAdapter(session, em, realm, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public int getActiveUserSessions(RealmModel realm, ClientModel client) {
+ Object count = em.createNamedQuery("getActiveUserSessionByClient")
+ .setParameter("realmId", realm.getId())
+ .setParameter("clientId", client.getClientId())
+ .getSingleResult();
+ return ((Number)count).intValue();
+ }
+
+ @Override
+ public void removeUserSession(RealmModel realm, UserSessionModel session) {
+ UserSessionEntity entity = em.find(UserSessionEntity.class, session.getId());
+ if (entity != null) {
+ em.remove(entity);
+ }
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm, UserModel user) {
+ em.createNamedQuery("removeClientUserSessionByUser")
+ .setParameter("realmId", realm.getId())
+ .setParameter("userId", user.getId())
+ .executeUpdate();
+ em.createNamedQuery("removeUserSessionByUser")
+ .setParameter("realmId", realm.getId())
+ .setParameter("userId", user.getId())
+ .executeUpdate();
+ }
+
+ @Override
+ public void removeExpiredUserSessions(RealmModel realm) {
+ int maxTime = Time.currentTime() - realm.getSsoSessionMaxLifespan();
+ int idleTime = Time.currentTime() - realm.getSsoSessionIdleTimeout();
+
+ em.createNamedQuery("removeClientUserSessionByExpired")
+ .setParameter("realmId", realm.getId())
+ .setParameter("maxTime", maxTime)
+ .setParameter("idleTime", idleTime)
+ .executeUpdate();
+ em.createNamedQuery("removeUserSessionByExpired")
+ .setParameter("realmId", realm.getId())
+ .setParameter("maxTime", maxTime)
+ .setParameter("idleTime", idleTime)
+ .executeUpdate();
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm) {
+ em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
+ em.createNamedQuery("removeUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
+ }
+
+ @Override
+ public void onRealmRemoved(RealmModel realm) {
+ removeUserSessions(realm);
+ em.createNamedQuery("removeLoginFailuresByRealm").setParameter("realmId", realm.getId()).executeUpdate();
+ }
+
+ @Override
+ public void onClientRemoved(RealmModel realm, ClientModel client) {
+ em.createNamedQuery("removeClientUserSessionByClient").setParameter("realmId", realm.getId()).setParameter("clientId", client.getClientId()).executeUpdate();
+ }
+
+ @Override
+ public void onUserRemoved(RealmModel realm, UserModel user) {
+ removeUserSessions(realm, user);
+ em.createNamedQuery("removeLoginFailuresByUser").setParameter("username", user.getUsername()).executeUpdate();
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProviderFactory.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProviderFactory.java
new file mode 100755
index 0000000..fca5ca1
--- /dev/null
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProviderFactory.java
@@ -0,0 +1,37 @@
+package org.keycloak.models.sessions.jpa;
+
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.UserSessionProviderFactory;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class JpaUserSessionProviderFactory implements UserSessionProviderFactory {
+
+ public static final String ID = "jpa";
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public UserSessionProvider create(KeycloakSession session) {
+ EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
+ return new JpaUserSessionProvider(session, em);
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/PersistenceExceptionConverter.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/PersistenceExceptionConverter.java
new file mode 100644
index 0000000..2d27df0
--- /dev/null
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/PersistenceExceptionConverter.java
@@ -0,0 +1,48 @@
+package org.keycloak.models.sessions.jpa;
+
+import org.hibernate.exception.ConstraintViolationException;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class PersistenceExceptionConverter implements InvocationHandler {
+
+ private EntityManager em;
+
+ public static EntityManager create(EntityManager em) {
+ return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
+ }
+
+ private PersistenceExceptionConverter(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ return method.invoke(em, args);
+ } catch (InvocationTargetException e) {
+ throw convert(e.getCause());
+ }
+ }
+
+ public static ModelException convert(Throwable t) {
+ if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
+ throw new ModelDuplicateException(t);
+ } if (t instanceof EntityExistsException) {
+ throw new ModelDuplicateException(t);
+ } else {
+ throw new ModelException(t);
+ }
+ }
+
+}
diff --git a/model/sessions-jpa/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory b/model/sessions-jpa/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory
new file mode 100644
index 0000000..8dc16d6
--- /dev/null
+++ b/model/sessions-jpa/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.sessions.jpa.JpaUserSessionProviderFactory
\ No newline at end of file
model/sessions-mem/pom.xml 31(+31 -0)
diff --git a/model/sessions-mem/pom.xml b/model/sessions-mem/pom.xml
new file mode 100755
index 0000000..c3f40eb
--- /dev/null
+++ b/model/sessions-mem/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-model-sessions-mem</artifactId>
+ <name>Keycloak Model Sessions Mem</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureEntity.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureEntity.java
new file mode 100644
index 0000000..b1788d2
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureEntity.java
@@ -0,0 +1,65 @@
+package org.keycloak.models.sessions.mem.entities;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UsernameLoginFailureEntity {
+
+ private String username;
+ private String realm;
+
+ private AtomicInteger failedLoginNotBefore = new AtomicInteger();
+ private AtomicInteger numFailures = new AtomicInteger();
+ private AtomicLong lastFailure = new AtomicLong();
+ private AtomicReference<String> lastIpFailure = new AtomicReference<String>();
+
+ public UsernameLoginFailureEntity(String username, String realm) {
+ this.username = username;
+ this.realm = realm;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public AtomicInteger getFailedLoginNotBefore() {
+ return failedLoginNotBefore;
+ }
+
+ public void setFailedLoginNotBefore(AtomicInteger failedLoginNotBefore) {
+ this.failedLoginNotBefore = failedLoginNotBefore;
+ }
+
+ public AtomicInteger getNumFailures() {
+ return numFailures;
+ }
+
+ public void setNumFailures(AtomicInteger numFailures) {
+ this.numFailures = numFailures;
+ }
+
+ public AtomicLong getLastFailure() {
+ return lastFailure;
+ }
+
+ public void setLastFailure(AtomicLong lastFailure) {
+ this.lastFailure = lastFailure;
+ }
+
+ public AtomicReference<String> getLastIpFailure() {
+ return lastIpFailure;
+ }
+
+ public void setLastIpFailure(AtomicReference<String> lastIpFailure) {
+ this.lastIpFailure = lastIpFailure;
+ }
+
+}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureKey.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureKey.java
new file mode 100644
index 0000000..68c7a4b
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UsernameLoginFailureKey.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.sessions.mem.entities;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UsernameLoginFailureKey {
+
+ private final String realm;
+ private final String username;
+
+ public UsernameLoginFailureKey(String realm, String username) {
+ this.realm = realm;
+ this.username = username;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ UsernameLoginFailureKey key = (UsernameLoginFailureKey) o;
+
+ if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
+ if (username != null ? !username.equals(key.username) : key.username != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = realm != null ? realm.hashCode() : 0;
+ result = 31 * result + (username != null ? username.hashCode() : 0);
+ return result;
+ }
+
+}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java
new file mode 100644
index 0000000..2596137
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java
@@ -0,0 +1,102 @@
+package org.keycloak.models.sessions.mem.entities;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionEntity {
+
+ private String id;
+ private String realm;
+ private String user;
+ private String loginUsername;
+ private String ipAddress;
+ private String authMethod;
+ private boolean rememberMe;
+ private int started;
+ private int lastSessionRefresh;
+ private List<String> clients = new LinkedList<String>();
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getLoginUsername() {
+ return loginUsername;
+ }
+
+ public void setLoginUsername(String loginUsername) {
+ this.loginUsername = loginUsername;
+ }
+
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ public String getAuthMethod() {
+ return authMethod;
+ }
+
+ public void setAuthMethod(String authMethod) {
+ this.authMethod = authMethod;
+ }
+
+ public boolean isRememberMe() {
+ return rememberMe;
+ }
+
+ public void setRememberMe(boolean rememberMe) {
+ this.rememberMe = rememberMe;
+ }
+
+ public int getStarted() {
+ return started;
+ }
+
+ public void setStarted(int started) {
+ this.started = started;
+ }
+
+ public int getLastSessionRefresh() {
+ return lastSessionRefresh;
+ }
+
+ public void setLastSessionRefresh(int lastSessionRefresh) {
+ this.lastSessionRefresh = lastSessionRefresh;
+ }
+
+ public List<String> getClients() {
+ return clients;
+ }
+
+ public void setClients(List<String> clients) {
+ this.clients = clients;
+ }
+
+}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionKey.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionKey.java
new file mode 100644
index 0000000..0fcb86c
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionKey.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.sessions.mem.entities;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionKey {
+
+ private final String realm;
+ private final String id;
+
+ public UserSessionKey(String realm, String id) {
+ this.realm = realm;
+ this.id = id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ UserSessionKey key = (UserSessionKey) o;
+
+ if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
+ if (id != null ? !id.equals(key.id) : key.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = realm != null ? realm.hashCode() : 0;
+ result = 31 * result + (id != null ? id.hashCode() : 0);
+ return result;
+ }
+
+}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
new file mode 100755
index 0000000..7021046
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
@@ -0,0 +1,227 @@
+package org.keycloak.models.sessions.mem;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
+import org.keycloak.models.sessions.mem.entities.UserSessionKey;
+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.util.Time;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MemUserSessionProvider implements UserSessionProvider {
+
+ private final KeycloakSession session;
+ private final ConcurrentHashMap<UserSessionKey, UserSessionEntity> sessions;
+ private final ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures;
+
+ public MemUserSessionProvider(KeycloakSession session, ConcurrentHashMap<UserSessionKey, UserSessionEntity> sessions, ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures) {
+ this.session = session;
+ this.sessions = sessions;
+ this.loginFailures = loginFailures;
+ }
+
+ @Override
+ public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
+ String id = KeycloakModelUtils.generateId();
+
+ UserSessionEntity entity = new UserSessionEntity();
+ entity.setId(id);
+ entity.setRealm(realm.getId());
+ entity.setUser(user.getId());
+ entity.setLoginUsername(loginUsername);
+ entity.setIpAddress(ipAddress);
+ entity.setAuthMethod(authMethod);
+ entity.setRememberMe(rememberMe);
+
+ int currentTime = Time.currentTime();
+
+ entity.setStarted(currentTime);
+ entity.setLastSessionRefresh(currentTime);
+
+ sessions.put(new UserSessionKey(realm.getId(), id), entity);
+
+ return new UserSessionAdapter(session, realm, entity);
+ }
+
+ @Override
+ public UserSessionModel getUserSession(RealmModel realm, String id) {
+ UserSessionEntity entity = sessions.get(new UserSessionKey(realm.getId(), id));
+ return entity != null ? new UserSessionAdapter(session, realm, entity) : null;
+ }
+
+ @Override
+ public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
+ List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
+ for (UserSessionEntity s : sessions.values()) {
+ if (s.getRealm().equals(realm.getId()) && s.getUser().equals(user.getId())) {
+ userSessions.add(new UserSessionAdapter(session, realm, s));
+ }
+ }
+ return userSessions;
+ }
+
+ @Override
+ public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
+ List<UserSessionModel> clientSessions = new LinkedList<UserSessionModel>();
+ for (UserSessionEntity s : sessions.values()) {
+ if (s.getRealm().equals(realm.getId()) && s.getClients().contains(client.getClientId())) {
+ clientSessions.add(new UserSessionAdapter(session, realm, s));
+ }
+ }
+ Collections.sort(clientSessions, new UserSessionSort());
+ return clientSessions;
+ }
+
+ @Override
+ public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
+ List<UserSessionModel> userSessions = getUserSessions(realm, client);
+ if (firstResult > userSessions.size()) {
+ return Collections.emptyList();
+ }
+
+ int toIndex = (firstResult + maxResults) < userSessions.size() ? firstResult + maxResults : userSessions.size();
+ return userSessions.subList(firstResult, toIndex);
+ }
+
+ @Override
+ public int getActiveUserSessions(RealmModel realm, ClientModel client) {
+ int count = 0;
+ for (UserSessionEntity s : sessions.values()) {
+ if (s.getRealm().equals(realm.getId()) && s.getClients().contains(client.getClientId())) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ @Override
+ public void removeUserSession(RealmModel realm, UserSessionModel session) {
+ sessions.remove(new UserSessionKey(realm.getId(), session.getId()));
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm, UserModel user) {
+ Iterator<UserSessionEntity> itr = sessions.values().iterator();
+ while (itr.hasNext()) {
+ UserSessionEntity s = itr.next();
+ if (s.getRealm().equals(realm.getId()) && s.getUser().equals(user.getId())) {
+ itr.remove();
+ }
+ }
+ }
+
+ @Override
+ public void removeExpiredUserSessions(RealmModel realm) {
+ Iterator<UserSessionEntity> itr = sessions.values().iterator();
+ while (itr.hasNext()) {
+ UserSessionEntity s = itr.next();
+ if (s.getRealm().equals(realm.getId()) && (s.getLastSessionRefresh() < Time.currentTime() - realm.getSsoSessionIdleTimeout() || s.getStarted() < Time.currentTime() - realm.getSsoSessionMaxLifespan())) {
+ itr.remove();
+ }
+ }
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm) {
+ Iterator<UserSessionEntity> itr = sessions.values().iterator();
+ while (itr.hasNext()) {
+ UserSessionEntity s = itr.next();
+ if (s.getRealm().equals(realm.getId())) {
+ itr.remove();
+ }
+ }
+ }
+
+ @Override
+ public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
+ UsernameLoginFailureEntity entity = loginFailures.get(new UsernameLoginFailureKey(username, realm.getId()));
+ return entity != null ? new UsernameLoginFailureAdapter(entity) : null;
+ }
+
+ @Override
+ public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
+ UsernameLoginFailureKey key = new UsernameLoginFailureKey(username, realm.getId());
+ return new UsernameLoginFailureAdapter(loginFailures.putIfAbsent(key, new UsernameLoginFailureEntity(username, realm.getId())));
+ }
+
+ @Override
+ public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
+ List<UsernameLoginFailureModel> failures = new LinkedList<UsernameLoginFailureModel>();
+ for (UsernameLoginFailureEntity entity : loginFailures.values()) {
+ if (entity.getRealm().equals(realm.getId())) {
+ failures.add(new UsernameLoginFailureAdapter(entity));
+ }
+ }
+ return failures;
+ }
+
+ @Override
+ public void onRealmRemoved(RealmModel realm) {
+ removeUserSessions(realm);
+
+ Iterator<UsernameLoginFailureEntity> itr = loginFailures.values().iterator();
+ while (itr.hasNext()) {
+ if (itr.next().getRealm().equals(realm.getId())) {
+ itr.remove();
+ }
+ }
+ }
+
+ @Override
+ public void onClientRemoved(RealmModel realm, ClientModel client) {
+ Iterator<UserSessionEntity> itr = sessions.values().iterator();
+ while (itr.hasNext()) {
+ UserSessionEntity s = itr.next();
+ if (s.getRealm().equals(realm.getId())) {
+ itr.remove();
+ }
+ }
+
+ for (UserSessionEntity s : sessions.values()) {
+ if (s.getRealm().equals(realm.getId())) {
+ s.getClients().remove(client.getClientId());
+ }
+ }
+ }
+
+ @Override
+ public void onUserRemoved(RealmModel realm, UserModel user) {
+ removeUserSessions(realm, user);
+
+ loginFailures.remove(new UsernameLoginFailureKey(realm.getId(), user.getUsername()));
+ }
+
+ @Override
+ public void close() {
+ }
+
+ private class UserSessionSort implements Comparator<UserSessionModel> {
+
+ @Override
+ public int compare(UserSessionModel o1, UserSessionModel o2) {
+ int r = o1.getStarted() - o2.getStarted();
+ if (r == 0) {
+ return o1.getId().compareTo(o2.getId());
+ } else {
+ return r;
+ }
+ }
+ }
+
+}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java
new file mode 100644
index 0000000..c1ae6a3
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java
@@ -0,0 +1,45 @@
+package org.keycloak.models.sessions.mem;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.UserSessionProviderFactory;
+import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
+import org.keycloak.models.sessions.mem.entities.UserSessionKey;
+import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureEntity;
+import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureKey;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MemUserSessionProviderFactory implements UserSessionProviderFactory {
+
+ public static final String ID = "mem";
+
+ private ConcurrentHashMap<UserSessionKey, UserSessionEntity> sessions = new ConcurrentHashMap<UserSessionKey, UserSessionEntity>();
+
+ private ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures = new ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity>();
+
+ @Override
+ public UserSessionProvider create(KeycloakSession session) {
+ return new MemUserSessionProvider(session, sessions, loginFailures);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ sessions.clear();
+ loginFailures.clear();
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UsernameLoginFailureAdapter.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UsernameLoginFailureAdapter.java
new file mode 100644
index 0000000..03dc558
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UsernameLoginFailureAdapter.java
@@ -0,0 +1,71 @@
+package org.keycloak.models.sessions.mem;
+
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureEntity;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel {
+
+ private final UsernameLoginFailureEntity entity;
+
+ public UsernameLoginFailureAdapter(UsernameLoginFailureEntity entity) {
+ this.entity = entity;
+ }
+
+ @Override
+ public String getUsername() {
+ return entity.getUsername();
+ }
+
+ public String getRealm() {
+ return entity.getRealm();
+ }
+
+ @Override
+ public int getFailedLoginNotBefore() {
+ return entity.getFailedLoginNotBefore().get();
+ }
+
+ @Override
+ public void setFailedLoginNotBefore(int notBefore) {
+ entity.getFailedLoginNotBefore().set(notBefore);
+ }
+
+ @Override
+ public int getNumFailures() {
+ return entity.getNumFailures().get();
+ }
+
+ @Override
+ public void incrementFailures() {
+ entity.getNumFailures().incrementAndGet();
+ }
+
+ @Override
+ public void clearFailures() {
+ entity.getNumFailures().set(0);
+ }
+
+ @Override
+ public long getLastFailure() {
+ return entity.getLastFailure().get();
+ }
+
+ @Override
+ public void setLastFailure(long lastFailure) {
+ entity.getLastFailure().set(lastFailure);
+ }
+
+ @Override
+ public String getLastIPFailure() {
+ return entity.getLastIpFailure().get();
+ }
+
+ @Override
+ public void setLastIPFailure(String ip) {
+ entity.getLastIpFailure().set(ip);
+ }
+
+}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java
new file mode 100755
index 0000000..a9008cf
--- /dev/null
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java
@@ -0,0 +1,135 @@
+package org.keycloak.models.sessions.mem;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionAdapter implements UserSessionModel {
+
+ private final KeycloakSession session;
+
+ private final RealmModel realm;
+
+ private final UserSessionEntity entity;
+
+ public UserSessionAdapter(KeycloakSession session, RealmModel realm, UserSessionEntity entity) {
+ this.session = session;
+ this.realm = realm;
+ this.entity = entity;
+ }
+
+ public String getId() {
+ return entity.getId();
+ }
+
+ public void setId(String id) {
+ entity.setId(id);
+ }
+
+ public UserModel getUser() {
+ return session.users().getUserById(entity.getUser(), realm);
+ }
+
+ public void setUser(UserModel user) {
+ entity.setUser(user.getId());
+ }
+
+ @Override
+ public String getLoginUsername() {
+ return entity.getLoginUsername();
+ }
+
+ @Override
+ public void setLoginUsername(String loginUsername) {
+ entity.setLoginUsername(loginUsername);
+ }
+
+ public String getIpAddress() {
+ return entity.getIpAddress();
+ }
+
+ public void setIpAddress(String ipAddress) {
+ entity.setIpAddress(ipAddress);
+ }
+
+ @Override
+ public String getAuthMethod() {
+ return entity.getAuthMethod();
+ }
+
+ @Override
+ public void setAuthMethod(String authMethod) {
+ entity.setAuthMethod(authMethod);
+ }
+
+ @Override
+ public boolean isRememberMe() {
+ return entity.isRememberMe();
+ }
+
+ @Override
+ public void setRememberMe(boolean rememberMe) {
+ entity.setRememberMe(rememberMe);
+ }
+
+ public int getStarted() {
+ return entity.getStarted();
+ }
+
+ public void setStarted(int started) {
+ entity.setStarted(started);
+ }
+
+ public int getLastSessionRefresh() {
+ return entity.getLastSessionRefresh();
+ }
+
+ public void setLastSessionRefresh(int lastSessionRefresh) {
+ entity.setLastSessionRefresh(lastSessionRefresh);
+ }
+
+ @Override
+ public void associateClient(ClientModel client) {
+ if (!entity.getClients().contains(client.getClientId())) {
+ entity.getClients().add(client.getClientId());
+ }
+ }
+
+ @Override
+ public List<ClientModel> getClientAssociations() {
+ List<ClientModel> models = new LinkedList<ClientModel>();
+ for (String clientId : entity.getClients()) {
+ models.add(realm.findClient(clientId));
+ }
+ return models;
+ }
+
+ @Override
+ public void removeAssociatedClient(ClientModel client) {
+ entity.getClients().remove(client.getClientId());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof UserSessionModel)) return false;
+
+ UserSessionModel that = (UserSessionModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+}
diff --git a/model/sessions-mem/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory b/model/sessions-mem/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory
new file mode 100644
index 0000000..a869fa1
--- /dev/null
+++ b/model/sessions-mem/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.sessions.mem.MemUserSessionProviderFactory
\ No newline at end of file
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/AbstractMongoAdapter.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/AbstractMongoAdapter.java
new file mode 100644
index 0000000..3aea2c9
--- /dev/null
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/AbstractMongoAdapter.java
@@ -0,0 +1,44 @@
+package org.keycloak.models.sessions.mongo;
+
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractMongoAdapter<T extends MongoIdentifiableEntity> {
+
+ protected final MongoStoreInvocationContext invocationContext;
+
+ public AbstractMongoAdapter(MongoStoreInvocationContext invocationContext) {
+ this.invocationContext = invocationContext;
+ }
+
+ protected abstract T getMongoEntity();
+
+ protected void updateMongoEntity() {
+ getMongoStore().updateEntity(getMongoEntity(), invocationContext);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AbstractMongoAdapter that = (AbstractMongoAdapter) o;
+
+ if (getMongoEntity() == null && that.getMongoEntity() == null) return true;
+ return getMongoEntity().equals(that.getMongoEntity());
+ }
+
+ @Override
+ public int hashCode() {
+ return getMongoEntity()!=null ? getMongoEntity().hashCode() : super.hashCode();
+ }
+
+ protected MongoStore getMongoStore() {
+ return invocationContext.getMongoStore();
+ }
+}
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
new file mode 100755
index 0000000..b555407
--- /dev/null
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
@@ -0,0 +1,206 @@
+package org.keycloak.models.sessions.mongo;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity;
+import org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity;
+import org.keycloak.util.Time;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MongoUserSessionProvider implements UserSessionProvider {
+
+ private final KeycloakSession session;
+ private final MongoStore mongoStore;
+ private final MongoStoreInvocationContext invocationContext;
+
+ public MongoUserSessionProvider(KeycloakSession session, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
+ this.session = session;
+ this.mongoStore = mongoStore;
+ this.invocationContext = invocationContext;
+ }
+
+ @Override
+ public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
+ MongoUserSessionEntity entity = new MongoUserSessionEntity();
+ entity.setRealmId(realm.getId());
+ entity.setUser(user.getId());
+ entity.setLoginUsername(loginUsername);
+ entity.setIpAddress(ipAddress);
+ entity.setAuthMethod(authMethod);
+ entity.setRememberMe(rememberMe);
+
+ int currentTime = Time.currentTime();
+
+ entity.setStarted(currentTime);
+ entity.setLastSessionRefresh(currentTime);
+
+ mongoStore.insertEntity(entity, invocationContext);
+ return new UserSessionAdapter(session, entity, realm, invocationContext);
+ }
+
+ @Override
+ public UserSessionModel getUserSession(RealmModel realm, String id) {
+ MongoUserSessionEntity entity = mongoStore.loadEntity(MongoUserSessionEntity.class, id, invocationContext);
+ if (entity == null) {
+ return null;
+ } else {
+ return new UserSessionAdapter(session, entity, realm, invocationContext);
+ }
+ }
+
+ @Override
+ public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
+ DBObject query = new BasicDBObject("user", user.getId());
+ List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+ for (MongoUserSessionEntity e : mongoStore.loadEntities(MongoUserSessionEntity.class, query, invocationContext)) {
+ sessions.add(new UserSessionAdapter(session, e, realm, invocationContext));
+ }
+ return sessions;
+ }
+
+ @Override
+ public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
+ return getUserSessions(realm, client, -1, -1);
+ }
+
+ public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
+ DBObject query = new QueryBuilder()
+ .and("associatedClientIds").is(client.getId())
+ .get();
+ DBObject sort = new BasicDBObject("started", 1).append("id", 1);
+
+ List<MongoUserSessionEntity> sessions = mongoStore.loadEntities(MongoUserSessionEntity.class, query, sort, firstResult, maxResults, invocationContext);
+ List<UserSessionModel> result = new LinkedList<UserSessionModel>();
+ for (MongoUserSessionEntity session : sessions) {
+ result.add(new UserSessionAdapter(this.session, session, realm, invocationContext));
+ }
+ return result;
+ }
+
+ @Override
+ public int getActiveUserSessions(RealmModel realm, ClientModel client) {
+ DBObject query = new QueryBuilder()
+ .and("associatedClientIds").is(client.getId())
+ .get();
+ return mongoStore.countEntities(MongoUserSessionEntity.class, query, invocationContext);
+ }
+
+ @Override
+ public void removeUserSession(RealmModel realm, UserSessionModel session) {
+ mongoStore.removeEntity(((UserSessionAdapter) session).getMongoEntity(), invocationContext);
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm, UserModel user) {
+ DBObject query = new BasicDBObject("user", user.getId());
+ mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm) {
+ DBObject query = new BasicDBObject("realmId", realm.getId());
+ mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
+ }
+
+ @Override
+ public void removeExpiredUserSessions(RealmModel realm) {
+ int currentTime = Time.currentTime();
+ DBObject query = new QueryBuilder()
+ .and("started").lessThan(currentTime - realm.getSsoSessionMaxLifespan())
+ .get();
+
+ mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
+ query = new QueryBuilder()
+ .and("lastSessionRefresh").lessThan(currentTime - realm.getSsoSessionIdleTimeout())
+ .get();
+
+ mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
+ }
+
+ @Override
+ public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
+ DBObject query = new QueryBuilder()
+ .and("username").is(username)
+ .and("realmId").is(realm.getId())
+ .get();
+ MongoUsernameLoginFailureEntity user = mongoStore.loadSingleEntity(MongoUsernameLoginFailureEntity.class, query, invocationContext);
+
+ if (user == null) {
+ return null;
+ } else {
+ return new UsernameLoginFailureAdapter(invocationContext, user);
+ }
+ }
+
+ @Override
+ public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
+ UsernameLoginFailureModel userLoginFailure = getUserLoginFailure(realm, username);
+ if (userLoginFailure != null) {
+ return userLoginFailure;
+ }
+
+ MongoUsernameLoginFailureEntity userEntity = new MongoUsernameLoginFailureEntity();
+ userEntity.setUsername(username);
+ userEntity.setRealmId(realm.getId());
+
+ mongoStore.insertEntity(userEntity, invocationContext);
+ return new UsernameLoginFailureAdapter(invocationContext, userEntity);
+ }
+
+ @Override
+ public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(realm.getId())
+ .get();
+ List<MongoUsernameLoginFailureEntity> failures = mongoStore.loadEntities(MongoUsernameLoginFailureEntity.class, query, invocationContext);
+
+ List<UsernameLoginFailureModel> result = new LinkedList<UsernameLoginFailureModel>();
+ if (failures == null) return result;
+ for (MongoUsernameLoginFailureEntity failure : failures) {
+ result.add(new UsernameLoginFailureAdapter(invocationContext, failure));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void onRealmRemoved(RealmModel realm) {
+ removeUserSessions(realm);
+ }
+
+ @Override
+ public void onClientRemoved(RealmModel realm, ClientModel client) {
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(realm.getId())
+ .get();
+ List<MongoUserSessionEntity> sessions = invocationContext.getMongoStore().loadEntities(MongoUserSessionEntity.class, query, invocationContext);
+ for (MongoUserSessionEntity session : sessions) {
+ invocationContext.getMongoStore().pullItemFromList(session, "associatedClientIds", client.getClientId(), invocationContext);
+ }
+ }
+
+ @Override
+ public void onUserRemoved(RealmModel realm, UserModel user) {
+ removeUserSessions(realm, user);
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProviderFactory.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProviderFactory.java
new file mode 100644
index 0000000..a6b22b7
--- /dev/null
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProviderFactory.java
@@ -0,0 +1,35 @@
+package org.keycloak.models.sessions.mongo;
+
+import org.keycloak.Config;
+import org.keycloak.connections.mongo.MongoConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.UserSessionProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MongoUserSessionProviderFactory implements UserSessionProviderFactory {
+
+ public static final String ID = "mongo";
+
+ @Override
+ public UserSessionProvider create(KeycloakSession session) {
+ MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
+ return new MongoUserSessionProvider(session, connection.getMongoStore(), connection.getInvocationContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+}
diff --git a/model/sessions-mongo/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory b/model/sessions-mongo/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory
new file mode 100644
index 0000000..3766f43
--- /dev/null
+++ b/model/sessions-mongo/src/main/resources/META-INF/services/org.keycloak.models.UserSessionProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.sessions.mongo.MongoUserSessionProviderFactory
\ No newline at end of file
model/users-jpa/pom.xml 79(+79 -0)
diff --git a/model/users-jpa/pom.xml b/model/users-jpa/pom.xml
new file mode 100755
index 0000000..46ca527
--- /dev/null
+++ b/model/users-jpa/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-model-users-jpa</artifactId>
+ <name>Keycloak Model Users JPA</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-hybrid</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.javax.persistence</groupId>
+ <artifactId>hibernate-jpa-2.0-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>${hibernate.entitymanager.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-tests-hybrid</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk16</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.iharder</groupId>
+ <artifactId>base64</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
+
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserAttributeEntity.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserAttributeEntity.java
new file mode 100644
index 0000000..c888b55
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserAttributeEntity.java
@@ -0,0 +1,79 @@
+package org.keycloak.models.users.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.ManyToOne;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@Entity
+@IdClass(UserAttributeEntity.Key.class)
+public class UserAttributeEntity {
+
+ @Id
+ protected String name;
+
+ @Id
+ @ManyToOne
+ protected UserEntity user;
+
+ protected String value;
+
+ public UserAttributeEntity() {
+ }
+
+ public UserAttributeEntity(UserEntity user, String name, String value) {
+ this.user = user;
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public static class Key implements Serializable {
+ private String name;
+ private UserEntity user;
+
+ public Key() {
+ }
+
+ public Key(String name, UserEntity user) {
+ this.name = name;
+ this.user = user;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (name != null ? !name.equals(key.name) : key.name != null) return false;
+ if (user != null ? !user.equals(key.user) : key.user != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (user != null ? user.hashCode() : 0);
+ return result;
+ }
+ }
+
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserCredentialEntity.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserCredentialEntity.java
new file mode 100755
index 0000000..1da3606
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserCredentialEntity.java
@@ -0,0 +1,106 @@
+package org.keycloak.models.users.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.ManyToOne;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@IdClass(UserCredentialEntity.Key.class)
+public class UserCredentialEntity {
+
+ @Id
+ protected String type;
+
+ protected String value;
+ protected String device;
+ protected byte[] salt;
+ protected int hashIterations;
+
+ @Id
+ @ManyToOne
+ protected UserEntity user;
+
+ public UserCredentialEntity() {
+ }
+
+ public UserCredentialEntity(UserEntity user, String type) {
+ this.user = user;
+ this.type = type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getDevice() {
+ return device;
+ }
+
+ public void setDevice(String device) {
+ this.device = device;
+ }
+
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public void setSalt(byte[] salt) {
+ this.salt = salt;
+ }
+
+ public int getHashIterations() {
+ return hashIterations;
+ }
+
+ public void setHashIterations(int hashIterations) {
+ this.hashIterations = hashIterations;
+ }
+
+ public static class Key implements Serializable {
+ private String type;
+ private UserEntity user;
+
+ public Key() {
+ }
+
+ public Key(String type, UserEntity user) {
+ this.type = type;
+ this.user = user;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (type != null ? !type.equals(key.type) : key.type != null) return false;
+ if (user != null ? !user.equals(key.user) : key.user != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = type != null ? type.hashCode() : 0;
+ result = 31 * result + (user != null ? user.hashCode() : 0);
+ return result;
+ }
+ }
+
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserEntity.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserEntity.java
new file mode 100755
index 0000000..09f8788
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserEntity.java
@@ -0,0 +1,179 @@
+package org.keycloak.models.users.jpa.entities;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realm = :realm"),
+ @NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realm = :realm"),
+ @NamedQuery(name="getRealmUserByAttribute", query="select u from UserEntity u join u.attributes a where u.realm = :realm and a.name = :name and a.value = :value")
+})
+@Entity
+@Table(uniqueConstraints = {
+ @UniqueConstraint(columnNames = { "realm", "username" }),
+ @UniqueConstraint(columnNames = { "realm", "emailConstraint" })
+})
+@IdClass(UserEntity.Key.class)
+public class UserEntity {
+ @Id
+ protected String id;
+
+ protected boolean enabled;
+ protected String username;
+ protected String firstName;
+ protected String lastName;
+ protected String email;
+
+ // Hack just to workaround the fact that on MS-SQL you can't have unique constraint with multiple NULL values TODO: Find better solution (like unique index with 'where' but that's proprietary)
+ protected String emailConstraint;
+
+ @Id
+ protected String realm;
+
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="user")
+ protected List<UserAttributeEntity> attributes = new LinkedList<UserAttributeEntity>();
+
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="user")
+ protected List<UserCredentialEntity> credentials = new LinkedList<UserCredentialEntity>();
+
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="user")
+ protected List<UserRoleMappingEntity> roles = new LinkedList<UserRoleMappingEntity>();
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ this.emailConstraint = email != null ? email : id;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getEmailConstraint() {
+ return emailConstraint;
+ }
+
+ public void setEmailConstraint(String emailConstraint) {
+ this.emailConstraint = emailConstraint;
+ }
+
+ public List<UserAttributeEntity> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(List<UserAttributeEntity> attributes) {
+ this.attributes = attributes;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ public Collection<UserCredentialEntity> getCredentials() {
+ return credentials;
+ }
+
+ public void setCredentials(List<UserCredentialEntity> credentials) {
+ this.credentials = credentials;
+ }
+
+ public List<UserRoleMappingEntity> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(List<UserRoleMappingEntity> roles) {
+ this.roles = roles;
+ }
+
+ public static class Key implements Serializable {
+ private String id;
+ private String realm;
+
+ public Key() {
+ }
+
+ public Key(String id, String realm) {
+ this.id = id;
+ this.realm = realm;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (id != null ? !id.equals(key.id) : key.id != null) return false;
+ if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id != null ? id.hashCode() : 0;
+ result = 31 * result + (realm != null ? realm.hashCode() : 0);
+ return result;
+ }
+ }
+
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserRoleMappingEntity.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserRoleMappingEntity.java
new file mode 100644
index 0000000..513163e
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/entities/UserRoleMappingEntity.java
@@ -0,0 +1,68 @@
+package org.keycloak.models.users.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.ManyToOne;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@Entity
+@IdClass(UserRoleMappingEntity.Key.class)
+public class UserRoleMappingEntity {
+
+ @Id
+ protected String role;
+
+ @Id
+ @ManyToOne
+ protected UserEntity user;
+
+ public UserRoleMappingEntity() {
+ }
+
+ public UserRoleMappingEntity(UserEntity user, String role) {
+ this.user = user;
+ this.role = role;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public static class Key implements Serializable {
+ private String role;
+ private UserEntity user;
+
+ public Key() {
+ }
+
+ public Key(String role, UserEntity user) {
+ this.role = role;
+ this.user = user;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (role != null ? !role.equals(key.role) : key.role != null) return false;
+ if (user != null ? !user.equals(key.user) : key.user != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = role != null ? role.hashCode() : 0;
+ result = 31 * result + (user != null ? user.hashCode() : 0);
+ return result;
+ }
+ }
+
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaKeycloakTransaction.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaKeycloakTransaction.java
new file mode 100755
index 0000000..2d1ab13
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaKeycloakTransaction.java
@@ -0,0 +1,53 @@
+package org.keycloak.models.users.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaKeycloakTransaction implements KeycloakTransaction {
+
+ protected EntityManager em;
+
+ public JpaKeycloakTransaction(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public void begin() {
+ em.getTransaction().begin();
+ }
+
+ @Override
+ public void commit() {
+ try {
+ em.getTransaction().commit();
+ } catch (PersistenceException e) {
+ throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
+ }
+ }
+
+ @Override
+ public void rollback() {
+ em.getTransaction().rollback();
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ em.getTransaction().setRollbackOnly();
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ return em.getTransaction().getRollbackOnly();
+ }
+
+ @Override
+ public boolean isActive() {
+ return em.getTransaction().isActive();
+ }
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaUserProvider.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaUserProvider.java
new file mode 100755
index 0000000..204ab33
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaUserProvider.java
@@ -0,0 +1,184 @@
+package org.keycloak.models.users.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.users.Credentials;
+import org.keycloak.models.users.Feature;
+import org.keycloak.models.users.User;
+import org.keycloak.models.users.UserProvider;
+import org.keycloak.models.users.jpa.entities.UserEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaUserProvider implements UserProvider {
+
+ protected final EntityManager em;
+
+ public JpaUserProvider(EntityManager em) {
+ this.em = PersistenceExceptionConverter.create(em);
+ }
+
+ @Override
+ public KeycloakTransaction getTransaction() {
+ return new JpaKeycloakTransaction(em);
+ }
+
+ @Override
+ public User addUser(String id, String username, Set<String> initialRoles, String realm) {
+ UserEntity entity = new UserEntity();
+ entity.setId(id);
+ entity.setUsername(username);
+ entity.setEmailConstraint(id);
+ entity.setRealm(realm);
+ em.persist(entity);
+
+ UserAdapter adapter = new UserAdapter(realm, em, entity);
+
+ if (initialRoles != null && !initialRoles.isEmpty()) {
+ for (String role : initialRoles) {
+ adapter.grantRole(role);
+ }
+ }
+
+ return adapter;
+ }
+
+ @Override
+ public boolean removeUser(String name, String realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByLoginName", UserEntity.class);
+ query.setParameter("username", name);
+ query.setParameter("realm", realm);
+ List<UserEntity> results = query.getResultList();
+ if (results.size() == 0) return false;
+ em.remove(results.get(0));
+ return true;
+ }
+
+ @Override
+ public User getUserById(String id, String realm) {
+ UserEntity user = em.find(UserEntity.class, new UserEntity.Key(id, realm));
+ return user != null ? new UserAdapter(realm, em, user) : null;
+ }
+
+ @Override
+ public User getUserByUsername(String username, String realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+ query.setParameter("username", username);
+ query.setParameter("realm", realm);
+ List<UserEntity> results = query.getResultList();
+ if (results.size() == 0) return null;
+ return new UserAdapter(realm, em, results.get(0));
+ }
+
+ @Override
+ public User getUserByEmail(String email, String realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
+ query.setParameter("email", email);
+ query.setParameter("realm", realm);
+ List<UserEntity> results = query.getResultList();
+ return results.isEmpty() ? null : new UserAdapter(realm, em, results.get(0));
+ }
+
+ @Override
+ public User getUserByAttribute(String name, String value, String realm) {
+ List<UserEntity> results = em.createNamedQuery("getRealmUserByAttribute", UserEntity.class)
+ .setParameter("realm", realm)
+ .setParameter("name", name)
+ .setParameter("value", value)
+ .getResultList();
+ return results.isEmpty() ? null : new UserAdapter(realm, em, results.get(0));
+ }
+
+ @Override
+ public void close() {
+ if (em.getTransaction().isActive()) em.getTransaction().rollback();
+ if (em.isOpen()) em.close();
+ }
+
+ @Override
+ public List<User> getUsers(String realm) {
+ TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm", UserEntity.class);
+ query.setParameter("realm", realm);
+ List<UserEntity> results = query.getResultList();
+ List<User> users = new ArrayList<User>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List<User> searchForUser(String search, String realm) {
+ TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm and ( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search )", UserEntity.class);
+ query.setParameter("realm", realm);
+ query.setParameter("search", "%" + search.toLowerCase() + "%");
+ List<UserEntity> results = query.getResultList();
+ List<User> users = new ArrayList<User>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List<User> searchForUserByAttributes(Map<String, String> attributes, String realm) {
+ StringBuilder builder = new StringBuilder("select u from UserEntity u");
+ boolean first = true;
+ for (Map.Entry<String, String> entry : attributes.entrySet()) {
+ String attribute = null;
+ if (entry.getKey().equals(User.USERNAME)) {
+ attribute = "lower(username)";
+ } else if (entry.getKey().equalsIgnoreCase(User.FIRST_NAME)) {
+ attribute = "lower(firstName)";
+ } else if (entry.getKey().equalsIgnoreCase(User.LAST_NAME)) {
+ attribute = "lower(lastName)";
+ } else if (entry.getKey().equalsIgnoreCase(User.EMAIL)) {
+ attribute = "lower(email)";
+ }
+ if (attribute == null) continue;
+ if (first) {
+ first = false;
+ builder.append(" where realm = :realm");
+ } else {
+ builder.append(" and ");
+ }
+ builder.append(attribute).append(" like '%").append(entry.getValue().toLowerCase()).append("%'");
+ }
+ String q = builder.toString();
+ TypedQuery<UserEntity> query = em.createQuery(q, UserEntity.class);
+ query.setParameter("realm", realm);
+ List<UserEntity> results = query.getResultList();
+ List<User> users = new ArrayList<User>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public boolean supports(Feature feature) {
+ return (feature == Feature.READ_CREDENTIALS || feature == Feature.UPDATE_CREDENTIALS);
+ }
+
+ @Override
+ public boolean verifyCredentials(User user, Credentials... credentials) {
+ return false;
+ }
+
+ @Override
+ public void onRealmRemoved(String realm) {
+ TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm", UserEntity.class);
+ query.setParameter("realm", realm);
+ for (UserEntity u : query.getResultList()) {
+ em.remove(u);
+ }
+ }
+
+ @Override
+ public void onRoleRemoved(String role) {
+ em.createQuery("delete from RoleEntity r where r.role = :role").setParameter("role", role).executeUpdate();
+ }
+
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaUserProviderFactory.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaUserProviderFactory.java
new file mode 100755
index 0000000..2d11563
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/JpaUserProviderFactory.java
@@ -0,0 +1,43 @@
+package org.keycloak.models.users.jpa;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.users.UserProvider;
+import org.keycloak.models.users.UserProviderFactory;
+import org.keycloak.util.JpaUtils;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaUserProviderFactory implements UserProviderFactory {
+
+ public static final String ID = "jpa";
+
+ protected EntityManagerFactory emf;
+
+ @Override
+ public void init(Config.Scope config) {
+ String persistenceUnit = config.get("persistenceUnit", "jpa-keycloak-identity-store");
+ emf = Persistence.createEntityManagerFactory(persistenceUnit, JpaUtils.getHibernateProperties());
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public UserProvider create(KeycloakSession session) {
+ return new JpaUserProvider(emf.createEntityManager());
+ }
+
+ @Override
+ public void close() {
+ emf.close();
+ }
+
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/PersistenceExceptionConverter.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/PersistenceExceptionConverter.java
new file mode 100755
index 0000000..42c02d0
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/PersistenceExceptionConverter.java
@@ -0,0 +1,48 @@
+package org.keycloak.models.users.jpa;
+
+import org.hibernate.exception.ConstraintViolationException;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class PersistenceExceptionConverter implements InvocationHandler {
+
+ private EntityManager em;
+
+ public static EntityManager create(EntityManager em) {
+ return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
+ }
+
+ private PersistenceExceptionConverter(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ return method.invoke(em, args);
+ } catch (InvocationTargetException e) {
+ throw convert(e.getCause());
+ }
+ }
+
+ public static ModelException convert(Throwable t) {
+ if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
+ throw new ModelDuplicateException(t);
+ } if (t instanceof EntityExistsException) {
+ throw new ModelDuplicateException(t);
+ } else {
+ throw new ModelException(t);
+ }
+ }
+
+}
diff --git a/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/UserAdapter.java b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/UserAdapter.java
new file mode 100755
index 0000000..fad265a
--- /dev/null
+++ b/model/users-jpa/src/main/java/org/keycloak/models/users/jpa/UserAdapter.java
@@ -0,0 +1,215 @@
+package org.keycloak.models.users.jpa;
+
+import org.keycloak.models.UserModel;
+import org.keycloak.models.users.Credentials;
+import org.keycloak.models.users.User;
+import org.keycloak.models.users.jpa.entities.UserAttributeEntity;
+import org.keycloak.models.users.jpa.entities.UserCredentialEntity;
+import org.keycloak.models.users.jpa.entities.UserEntity;
+import org.keycloak.models.users.jpa.entities.UserRoleMappingEntity;
+
+import javax.persistence.EntityManager;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserAdapter implements User {
+
+ protected UserEntity user;
+ protected EntityManager em;
+ protected String realm;
+
+ public UserAdapter(String realm, EntityManager em, UserEntity user) {
+ this.em = em;
+ this.user = user;
+ this.realm = realm;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ @Override
+ public String getId() {
+ return user.getId();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return user.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ user.setEnabled(enabled);
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getUsername();
+ }
+
+ @Override
+ public void setUsername(String username) {
+ user.setUsername(username);
+ }
+
+ @Override
+ public String getFirstName() {
+ return user.getFirstName();
+ }
+
+ @Override
+ public void setFirstName(String firstName) {
+ user.setFirstName(firstName);
+ }
+
+ @Override
+ public String getLastName() {
+ return user.getLastName();
+ }
+
+ @Override
+ public void setLastName(String lastName) {
+ user.setLastName(lastName);
+ }
+
+ @Override
+ public String getEmail() {
+ return user.getEmail();
+ }
+
+ @Override
+ public void setEmail(String email) {
+ user.setEmail(email);
+ }
+
+
+ @Override
+ public void setAttribute(String name, String value) {
+ List<UserAttributeEntity> attributes = user.getAttributes();
+ for (UserAttributeEntity a : user.getAttributes()) {
+ if (a.getName().equals(name)) {
+ a.setValue(value);
+ return;
+ }
+ }
+ attributes.add(new UserAttributeEntity(user, name, value));
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ for (UserAttributeEntity a : user.getAttributes()) {
+ if (a.getName().equals(name)) {
+ return a.getValue();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Map<String, String> getAttributes() {
+ Map<String, String> result = new HashMap<String, String>();
+ for (UserAttributeEntity a : user.getAttributes()) {
+ result.put(a.getName(), a.getValue());
+ }
+ return result;
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ Iterator<UserAttributeEntity> itr = user.getAttributes().iterator();
+ while(itr.hasNext()) {
+ if (itr.next().getName().equals(name)) {
+ itr.remove();
+ return;
+ }
+ }
+ }
+
+ private UserCredentialEntity getCredentialEntity(String credType) {
+ for (UserCredentialEntity entity : user.getCredentials()) {
+ if (entity.getType().equals(credType)) {
+ return entity;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List<Credentials> getCredentials() {
+ List<Credentials> result = new ArrayList<Credentials>();
+ for (UserCredentialEntity entity : user.getCredentials()) {
+ result.add(new Credentials(entity.getType(), entity.getSalt(), entity.getValue(), entity.getHashIterations(), entity.getDevice()));
+ }
+ return result;
+ }
+
+ @Override
+ public void updateCredential(Credentials credentials) {
+ UserCredentialEntity entity = getCredentialEntity(credentials.getType());
+ if (entity == null) {
+ entity = new UserCredentialEntity(user, credentials.getType());
+ user.getCredentials().add(entity);
+ }
+
+ entity.setValue(credentials.getValue());
+ entity.setSalt(credentials.getSalt());
+ entity.setHashIterations(credentials.getHashIterations());
+ entity.setDevice(credentials.getDevice());
+ }
+
+ @Override
+ public void grantRole(String role) {
+ for (UserRoleMappingEntity r : user.getRoles()) {
+ if (r.getRole().equals(role)) {
+ return;
+ }
+ }
+
+ user.getRoles().add(new UserRoleMappingEntity(user, role));
+ }
+
+ @Override
+ public Set<String> getRoleMappings() {
+ Set<String> roles = new HashSet<String>();
+ for (UserRoleMappingEntity r : user.getRoles()) {
+ roles.add(r.getRole());
+ }
+ return roles;
+ }
+
+ @Override
+ public void deleteRoleMapping(String role) {
+ Iterator<UserRoleMappingEntity> itr = user.getRoles().iterator();
+ while (itr.hasNext()) {
+ if (itr.next().getRole().equals(role)) {
+ itr.remove();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof UserModel)) return false;
+
+ UserModel that = (UserModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+}
diff --git a/model/users-jpa/src/main/resources/META-INF/services/org.keycloak.models.users.UserProviderFactory b/model/users-jpa/src/main/resources/META-INF/services/org.keycloak.models.users.UserProviderFactory
new file mode 100644
index 0000000..43ec95f
--- /dev/null
+++ b/model/users-jpa/src/main/resources/META-INF/services/org.keycloak.models.users.UserProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.users.jpa.JpaUserProviderFactory
\ No newline at end of file
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
index 6f13dac..cf4d70c 100644
--- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
@@ -37,7 +37,7 @@ public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredenti
@Override
public void setup(LDAPIdentityStore store) {
- // TODO: Don't setup it here once PLIDM-508 is fixed
+ // TODO: Don't setup it here once PLINK-508 is fixed
if (store.getConfig().isActiveDirectory() || Boolean.getBoolean("keycloak.ldap.ad.skipUserAccountControlAfterPasswordUpdate")) {
String userAccountControlProp = System.getProperty("keycloak.ldap.ad.userAccountControlAfterPasswordUpdate");
this.userAccountControlAfterPasswordUpdate = userAccountControlProp!=null ? userAccountControlProp : "512";
pom.xml 51(+14 -37)
diff --git a/pom.xml b/pom.xml
index a3871e1..65cf8bd 100755
--- a/pom.xml
+++ b/pom.xml
@@ -99,9 +99,12 @@
<module>authentication</module>
<module>core</module>
<module>core-jaxrs</module>
+ <module>connections</module>
+ <module>dependencies</module>
<module>model</module>
<module>integration</module>
<module>picketlink</module>
+ <module>federation</module>
<module>services</module>
<module>social</module>
<module>forms</module>
@@ -269,6 +272,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>3.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>${hibernate.javax.persistence.version}</version>
@@ -550,6 +559,11 @@
<skip>true</skip>
</configuration>
</plugin>
+ <plugin>
+ <groupId>com.samaxes.maven</groupId>
+ <artifactId>minify-maven-plugin</artifactId>
+ <version>1.7.2</version>
+ </plugin>
</plugins>
</pluginManagement>
@@ -615,42 +629,5 @@
<module>distribution</module>
</modules>
</profile>
-
- <!-- MySQL -->
- <profile>
- <activation>
- <property>
- <name>hibernate.connection.driver_class</name>
- <value>com.mysql.jdbc.Driver</value>
- </property>
- </activation>
- <id>mysql</id>
- <dependencies>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>${mysql.version}</version>
- </dependency>
- </dependencies>
- </profile>
-
- <!-- PostgreSQL -->
- <profile>
- <activation>
- <property>
- <name>hibernate.connection.driver_class</name>
- <value>org.postgresql.Driver</value>
- </property>
- </activation>
- <id>postgresql</id>
- <dependencies>
- <dependency>
- <groupId>org.postgresql</groupId>
- <artifactId>postgresql</artifactId>
- <version>${postgresql.version}</version>
- </dependency>
- </dependencies>
- </profile>
-
</profiles>
</project>
diff --git a/project-integrations/aerogear-ups/app/pom.xml b/project-integrations/aerogear-ups/app/pom.xml
index 2bf361c..eb42ab7 100755
--- a/project-integrations/aerogear-ups/app/pom.xml
+++ b/project-integrations/aerogear-ups/app/pom.xml
@@ -35,11 +35,6 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-as7-adapter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
</dependency>
@@ -119,4 +114,31 @@
</plugin>
</plugins>
</build>
+
+ <profiles>
+ <profile>
+ <id>as7</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-as7-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>wildfly</id>
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wildfly-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
+
</project>
diff --git a/project-integrations/aerogear-ups/app/src/main/webapp/WEB-INF/web.xml b/project-integrations/aerogear-ups/app/src/main/webapp/WEB-INF/web.xml
index 23e2831..77b633e 100755
--- a/project-integrations/aerogear-ups/app/src/main/webapp/WEB-INF/web.xml
+++ b/project-integrations/aerogear-ups/app/src/main/webapp/WEB-INF/web.xml
@@ -41,8 +41,8 @@
-->
</security-constraint>
- <login-config>
- <auth-method>BASIC</auth-method>
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
<realm-name>demo</realm-name>
</login-config>
project-integrations/aerogear-ups/auth-server/pom.xml 174(+14 -160)
diff --git a/project-integrations/aerogear-ups/auth-server/pom.xml b/project-integrations/aerogear-ups/auth-server/pom.xml
index c32789c..61a7d86 100755
--- a/project-integrations/aerogear-ups/auth-server/pom.xml
+++ b/project-integrations/aerogear-ups/auth-server/pom.xml
@@ -16,40 +16,27 @@
<dependencies>
<dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk16</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>net.iharder</groupId>
- <artifactId>base64</artifactId>
- </dependency>
- <dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-services</artifactId>
+ <artifactId>keycloak-dependencies-server-min</artifactId>
<version>${project.version}</version>
+ <type>pom</type>
</dependency>
+
<dependency>
- <groupId>com.google.zxing</groupId>
- <artifactId>javase</artifactId>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
- <version>${project.version}</version>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
</dependency>
+
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-invalidation-cache-model</artifactId>
+ <artifactId>keycloak-connections-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@@ -59,147 +46,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jboss-logging</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- social -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- forms -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.freemarker</groupId>
- <artifactId>freemarker</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-themes</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-js-adapter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- authentication api -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- timer -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-basic</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-api</artifactId>
+ <artifactId>keycloak-model-sessions-mem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-impl</artifactId>
+ <artifactId>keycloak-model-sessions-jpa</artifactId>
<version>${project.version}</version>
</dependency>
-
-
- <dependency>
- <groupId>org.jboss.spec.javax.servlet</groupId>
- <artifactId>jboss-servlet-api_3.0_spec</artifactId>
- <scope>provided</scope>
- </dependency>
- <!-- resteasy -->
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-multipart-provider</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>async-http-servlet-3.0</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>jaxrs-api</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jackson-provider</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
-
-
</dependencies>
<build>
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java b/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java
index 9f322e1..c4f8c9b 100755
--- a/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java
+++ b/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java
@@ -30,8 +30,8 @@ public class UpsSecurityApplication extends KeycloakApplication {
try {
RealmManager manager = new RealmManager(session);
RealmModel master = manager.getKeycloakAdminstrationRealm();
- UserModel admin = master.getUser("admin");
- if (admin != null) master.removeUser(admin.getLoginName());
+ UserModel admin = session.users().getUserByUsername("admin", master);
+ if (admin != null) session.users().removeUser(master, admin);
session.getTransaction().commit();
} finally {
session.close();
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
index 8c92fe9..0925383 100755
--- a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
+++ b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -2,10 +2,19 @@
<deployment>
<dependencies>
<module name="org.apache.httpcomponents"/>
+ <module name="org.bouncycastle"/>
+ <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
<module name="org.codehaus.jackson.jackson-core-asl"/>
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
</dependencies>
<exclusions>
+ <module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
+
+ <!-- Exclude keycloak modules -->
+ <module name="org.keycloak.keycloak-core" />
+ <module name="org.keycloak.keycloak-adapter-core" />
+ <module name="org.keycloak.keycloak-undertow-adapter" />
+ <module name="org.keycloak.keycloak-as7-adapter" />
</exclusions>
</deployment>
</jboss-deployment-structure>
\ No newline at end of file
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json
index 7243991..5b442b0 100755
--- a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json
+++ b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json
@@ -22,7 +22,11 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "account": [ "manage-account" ]
+ }
},
{
"username" : "admin",
@@ -35,7 +39,13 @@
],
"requiredActions": [
"UPDATE_PASSWORD"
- ]
+ ],
+ "realmRoles": [ "user","admin" ],
+ "applicationRoles": {
+ "realm-management": [ "realm-admin" ],
+ "account": [ "manage-account" ]
+ }
+
}
],
"roles" : {
@@ -50,16 +60,6 @@
}
]
},
- "roleMappings": [
- {
- "username": "bburke@redhat.com",
- "roles": ["user"]
- },
- {
- "username": "admin",
- "roles": ["user", "admin"]
- }
- ],
"scopeMappings": [
{
"client": "unified-push-server",
@@ -77,24 +77,6 @@
"/aerogear-ups/*"
]
}
- ],
- "applicationRoleMappings": {
- "account": [
- {
- "username": "bburke@redhat.com",
- "roles": ["manage-account"]
- },
- {
- "username": "admin",
- "roles": ["manage-account"]
- }
- ],
- "realm-management": [
- {
- "username": "admin",
- "roles": ["realm-admin"]
- }
- ]
- }
+ ]
}
diff --git a/project-integrations/aerogear-ups/README.md b/project-integrations/aerogear-ups/README.md
index 3a490b3..780081b 100755
--- a/project-integrations/aerogear-ups/README.md
+++ b/project-integrations/aerogear-ups/README.md
@@ -1,10 +1,15 @@
Self Bootstrapping Keycloak Server and Application
==========================================================
-To get this running:
-1. Boot up JBoss EAP. (This is an EAP build!)
-2. In the project-integrations/aerogear-ups directory do:
-$ mvn clean install jboss-as:deploy
+To get this running boot up JBoss AS7, EAP or WildFly.
+
+To deploy to AS7 run:
+
+ mvn clean install jboss-as:deploy
+
+To deploy to WildFly run:
+
+ mvn -Pwildfly clean install wildfly:deploy
This is an example of bundling two wars: a keycloak server war and application WAR together so that keycloak is bootstrapped out of the
box. The structure of the example is:
project-integrations/README.md 1(+0 -1)
diff --git a/project-integrations/README.md b/project-integrations/README.md
index f9299b2..02814ee 100644
--- a/project-integrations/README.md
+++ b/project-integrations/README.md
@@ -2,4 +2,3 @@ Keycloak External Project Integrations
==========
Everthing in this directory is examples related to integration with non-keycloak projects. Its a sandbox we use to test integrations with third-party projects
-
\ No newline at end of file
server/pom.xml 261(+2 -259)
diff --git a/server/pom.xml b/server/pom.xml
index 2d8e61d..53ee2ef 100755
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -16,267 +16,10 @@
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
+ <artifactId>keycloak-dependencies-server-all</artifactId>
<version>${project.version}</version>
+ <type>pom</type>
</dependency>
- <dependency>
- <groupId>net.iharder</groupId>
- <artifactId>base64</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-services</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.zxing</groupId>
- <artifactId>javase</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-invalidation-cache-model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jboss-logging</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-email</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- social -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-github</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-google</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-twitter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.twitter4j</groupId>
- <artifactId>twitter4j-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-facebook</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- forms -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.freemarker</groupId>
- <artifactId>freemarker</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-themes</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-js-adapter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- authentication api -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-picketlink</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-idm-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-idm-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-idm-simple-schema</artifactId>
- </dependency>
-
- <!-- timer -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-basic</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <!-- picketlink -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-picketlink-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-picketlink-realm</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.jboss.spec.javax.servlet</groupId>
- <artifactId>jboss-servlet-api_3.0_spec</artifactId>
- <scope>provided</scope>
- </dependency>
- <!-- resteasy -->
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-multipart-provider</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>async-http-servlet-3.0</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>jaxrs-api</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jackson-provider</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <!-- Mongo dependencies -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-mongo</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-mongo</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.mongodb</groupId>
- <artifactId>mongo-java-driver</artifactId>
- </dependency>
-
- <!-- export/import -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-impl</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>de.idyl</groupId>
- <artifactId>winzipaes</artifactId>
- </dependency>
-
</dependencies>
<build>
diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json
index 7e3f246..e9bf7f2 100755
--- a/server/src/main/resources/META-INF/keycloak-server.json
+++ b/server/src/main/resources/META-INF/keycloak-server.json
@@ -10,12 +10,27 @@
}
},
- "model": {
+ "realm": {
"provider": "jpa"
},
- "modelCache": {
- "provider": "${keycloak.model.cache.provider:}"
+ "user": {
+ "provider": "${keycloak.user.provider:jpa}"
+ },
+
+ "userSessions": {
+ "provider" : "${keycloak.userSessions.provider:mem}"
+ },
+
+ "realmCache": {
+ "provider": "${keycloak.realm.cache.provider:mem}"
+ },
+
+ "userCache": {
+ "provider": "${keycloak.user.cache.provider:mem}",
+ "mem": {
+ "maxSize": 20000
+ }
},
"timer": {
@@ -45,5 +60,12 @@
"scheduled": {
"interval": 900
+ },
+
+ "connectionsJpa": {
+ "default": {
+ "dataSource": "java:jboss/datasources/ExampleDS",
+ "databaseSchema": "update"
+ }
}
}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
index dce4019..b6b55c8 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
@@ -1,23 +1,20 @@
package org.keycloak.services;
-import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.ClientModel;
+import org.keycloak.models.UserFederationManager;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakTransaction;
-import org.keycloak.models.ModelProvider;
-import org.keycloak.models.OAuthClientModel;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.UsernameLoginFailureModel;
-import org.keycloak.models.cache.CacheModelProvider;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.KeycloakTransactionManager;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.UserProvider;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.cache.CacheRealmProvider;
+import org.keycloak.models.cache.CacheUserProvider;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -27,23 +24,59 @@ import java.util.Set;
*/
public class DefaultKeycloakSession implements KeycloakSession {
- private DefaultKeycloakSessionFactory factory;
- private Map<Integer, Provider> providers = new HashMap<Integer, Provider>();
- private final ModelProvider model;
+ private final DefaultKeycloakSessionFactory factory;
+ private final Map<Integer, Provider> providers = new HashMap<Integer, Provider>();
+ private final List<Provider> closable = new LinkedList<Provider>();
+ private final DefaultKeycloakTransactionManager transactionManager;
+ private RealmProvider model;
+ private UserProvider userModel;
+ private UserSessionProvider sessionProvider;
+ private UserFederationManager federationManager;
public DefaultKeycloakSession(DefaultKeycloakSessionFactory factory) {
this.factory = factory;
+ this.transactionManager = new DefaultKeycloakTransactionManager();
+ federationManager = new UserFederationManager(this);
+ }
- if (factory.getDefaultProvider(CacheModelProvider.class) != null) {
- model = getProvider(CacheModelProvider.class);
+ private RealmProvider getRealmProvider() {
+ if (factory.getDefaultProvider(CacheRealmProvider.class) != null) {
+ return getProvider(CacheRealmProvider.class);
} else {
- model = getProvider(ModelProvider.class);
+ return getProvider(RealmProvider.class);
}
}
+ private UserProvider getUserProvider() {
+ if (factory.getDefaultProvider(CacheUserProvider.class) != null) {
+ return getProvider(CacheUserProvider.class);
+ } else {
+ return getProvider(UserProvider.class);
+ }
+ }
+
+ @Override
+ public void enlistForClose(Provider provider) {
+ closable.add(provider);
+ }
+
+ @Override
+ public KeycloakTransactionManager getTransaction() {
+ return transactionManager;
+ }
+
@Override
- public KeycloakTransaction getTransaction() {
- return model.getTransaction();
+ public KeycloakSessionFactory getKeycloakSessionFactory() {
+ return factory;
+ }
+
+ @Override
+ public UserProvider userStorage() {
+ if (userModel == null) {
+ userModel = getUserProvider();
+ }
+ return userModel;
+
}
public <T extends Provider> T getProvider(Class<T> clazz) {
@@ -86,163 +119,38 @@ public class DefaultKeycloakSession implements KeycloakSession {
}
@Override
- public RealmModel createRealm(String name) {
- return model.createRealm(name);
- }
-
- @Override
- public RealmModel createRealm(String id, String name) {
- return model.createRealm(id, name);
- }
-
- @Override
- public RealmModel getRealm(String id) {
- return model.getRealm(id);
- }
-
- @Override
- public RealmModel getRealmByName(String name) {
- return model.getRealmByName(name);
- }
-
- @Override
- public UserModel getUserById(String id, RealmModel realm) {
- return model.getUserById(id, realm);
- }
-
- @Override
- public UserModel getUserByUsername(String username, RealmModel realm) {
- return model.getUserByUsername(username, realm);
- }
-
- @Override
- public UserModel getUserByEmail(String email, RealmModel realm) {
- return model.getUserByEmail(email, realm);
- }
-
- @Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
- return model.getUserBySocialLink(socialLink, realm);
- }
-
- @Override
- public List<UserModel> getUsers(RealmModel realm) {
- return model.getUsers(realm);
- }
-
- @Override
- public List<UserModel> searchForUser(String search, RealmModel realm) {
- return model.searchForUser(search, realm);
- }
-
- @Override
- public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
- return model.searchForUserByAttributes(attributes, realm);
- }
-
- @Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
- return model.getSocialLinks(user, realm);
- }
-
- @Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
- return model.getSocialLink(user, socialProvider, realm);
- }
-
- @Override
- public RoleModel getRoleById(String id, RealmModel realm) {
- return model.getRoleById(id, realm);
- }
-
- @Override
- public ApplicationModel getApplicationById(String id, RealmModel realm) {
- return model.getApplicationById(id, realm);
- }
-
- @Override
- public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
- return model.getOAuthClientById(id, realm);
- }
-
- @Override
- public List<RealmModel> getRealms() {
- return model.getRealms();
- }
-
- @Override
- public boolean removeRealm(String id) {
- return model.removeRealm(id);
- }
-
- @Override
- public UsernameLoginFailureModel getUserLoginFailure(String username, RealmModel realm) {
- return model.getUserLoginFailure(username, realm);
- }
-
- @Override
- public UsernameLoginFailureModel addUserLoginFailure(String username, RealmModel realm) {
- return model.addUserLoginFailure(username, realm);
- }
-
- @Override
- public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
- return model.getAllUserLoginFailures(realm);
- }
-
- @Override
- public UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress) {
- return model.createUserSession(realm, user, ipAddress);
- }
-
- @Override
- public UserSessionModel getUserSession(String id, RealmModel realm) {
- return model.getUserSession(id, realm);
- }
-
- @Override
- public List<UserSessionModel> getUserSessions(UserModel user, RealmModel realm) {
- return model.getUserSessions(user, realm);
- }
-
- @Override
- public Set<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
- return model.getUserSessions(realm, client);
- }
-
- @Override
- public int getActiveUserSessions(RealmModel realm, ClientModel client) {
- return model.getActiveUserSessions(realm, client);
- }
-
- @Override
- public void removeUserSession(UserSessionModel session) {
- model.removeUserSession(session);
- }
-
- @Override
- public void removeUserSessions(RealmModel realm, UserModel user) {
- model.removeUserSessions(realm, user);
- }
-
- @Override
- public void removeExpiredUserSessions(RealmModel realm) {
- model.removeExpiredUserSessions(realm);
+ public RealmProvider realms() {
+ if (model == null) {
+ model = getRealmProvider();
+ }
+ return model;
}
@Override
- public void removeUserSessions(RealmModel realm) {
- model.removeUserSessions(realm);
+ public UserProvider users() {
+ return federationManager;
}
@Override
- public void removeAllData() {
- model.removeAllData();
+ public UserSessionProvider sessions() {
+ if (sessionProvider == null) {
+ sessionProvider = getProvider(UserSessionProvider.class);
+ }
+ return sessionProvider;
}
public void close() {
for (Provider p : providers.values()) {
- p.close();
+ try {
+ p.close();
+ } catch (Exception e) {
+ }
+ }
+ for (Provider p : closable) {
+ try {
+ p.close();
+ } catch (Exception e) {
+ }
}
}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
index 560cc77..94b3ea7 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
@@ -74,11 +74,13 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
return provider.get(clazz);
}
- <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
+ @Override
+ public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
return getProviderFactory(clazz, provider.get(clazz));
}
- <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
+ @Override
+ public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
return factoriesMap.get(clazz).get(id);
}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakTransactionManager.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakTransactionManager.java
new file mode 100755
index 0000000..a63ed9d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakTransactionManager.java
@@ -0,0 +1,120 @@
+package org.keycloak.services;
+
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.KeycloakTransactionManager;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultKeycloakTransactionManager implements KeycloakTransactionManager {
+
+ private List<KeycloakTransaction> transactions = new LinkedList<KeycloakTransaction>();
+ private List<KeycloakTransaction> afterCompletion = new LinkedList<KeycloakTransaction>();
+ private boolean active;
+ private boolean rollback;
+
+ @Override
+ public void enlist(KeycloakTransaction transaction) {
+ if (active && !transaction.isActive()) {
+ transaction.begin();
+ }
+
+ transactions.add(transaction);
+ }
+
+ @Override
+ public void enlistAfterCompletion(KeycloakTransaction transaction) {
+ if (active && !transaction.isActive()) {
+ transaction.begin();
+ }
+
+ afterCompletion.add(transaction);
+ }
+
+ @Override
+ public void begin() {
+ if (active) {
+ throw new IllegalStateException("Transaction already active");
+ }
+
+ for (KeycloakTransaction tx : transactions) {
+ tx.begin();
+ }
+
+ active = true;
+ }
+
+ @Override
+ public void commit() {
+ RuntimeException exception = null;
+ for (KeycloakTransaction tx : transactions) {
+ try {
+ tx.commit();
+ } catch (RuntimeException e) {
+ exception = exception == null ? e : exception;
+ }
+ }
+ for (KeycloakTransaction tx : afterCompletion) {
+ try {
+ tx.commit();
+ } catch (RuntimeException e) {
+ exception = exception == null ? e : exception;
+ }
+ }
+
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ @Override
+ public void rollback() {
+ RuntimeException exception = null;
+ for (KeycloakTransaction tx : transactions) {
+ try {
+ tx.rollback();
+ } catch (RuntimeException e) {
+ exception = exception != null ? e : exception;
+ }
+ }
+ for (KeycloakTransaction tx : afterCompletion) {
+ try {
+ tx.rollback();
+ } catch (RuntimeException e) {
+ exception = exception != null ? e : exception;
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ rollback = true;
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ if (rollback) {
+ return true;
+ }
+
+ for (KeycloakTransaction tx : transactions) {
+ if (tx.getRollbackOnly()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isActive() {
+ return active;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
index 1f7605f..68e1cf8 100755
--- a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
+++ b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
@@ -1,23 +1,18 @@
package org.keycloak.services.managers;
-import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.keycloak.OAuthErrorException;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessCode;
-import org.keycloak.representations.AccessToken;
import org.keycloak.util.Time;
-import javax.ws.rs.core.MultivaluedMap;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
-import java.util.UUID;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -26,10 +21,12 @@ import java.util.UUID;
public class AccessCodeEntry {
protected AccessCode accessCode;
protected RealmModel realm;
+ KeycloakSession keycloakSession;
- public AccessCodeEntry(RealmModel realm, AccessCode accessCode) {
+ public AccessCodeEntry(KeycloakSession keycloakSession, RealmModel realm, AccessCode accessCode) {
this.realm = realm;
this.accessCode = accessCode;
+ this.keycloakSession = keycloakSession;
}
public String getCodeId() {
@@ -37,88 +34,92 @@ public class AccessCodeEntry {
}
public UserModel getUser() {
- return realm.getUserById(accessCode.getAccessToken().getSubject());
+ return keycloakSession.users().getUserById(accessCode.getUserId(), realm);
}
public String getSessionState() {
- return accessCode.getAccessToken().getSessionState();
+ return accessCode.getSessionState();
}
public boolean isExpired() {
- return accessCode.getExpiration() != 0 && Time.currentTime() > accessCode.getExpiration();
- }
-
- public AccessToken getToken() {
- return accessCode.getAccessToken();
+ int lifespan = accessCode.getAction() == null ? realm.getAccessCodeLifespan() : realm.getAccessCodeLifespanUserAction();
+ return accessCode.getTimestamp() + lifespan < Time.currentTime();
+ }
+
+ public Set<RoleModel> getRequestedRoles() {
+ Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
+ for (String roleId : accessCode.getRequestedRoles()) {
+ RoleModel role = realm.getRoleById(roleId);
+ if (role == null) {
+ new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid role " + roleId);
+ }
+ requestedRoles.add(realm.getRoleById(roleId));
+ }
+ return requestedRoles;
}
public ClientModel getClient() {
- return realm.findClient(accessCode.getAccessToken().getIssuedFor());
+ return realm.findClient(accessCode.getClientId());
}
public String getState() {
return accessCode.getState();
}
- public String getRedirectUri() {
- return accessCode.getRedirectUri();
- }
-
- public boolean isRememberMe() {
- return accessCode.isRememberMe();
- }
-
- public void setRememberMe(boolean remember) {
- accessCode.setRememberMe(remember);
+ public void setState(String state) {
+ accessCode.setState(state);
}
- public String getAuthMethod() {
- return accessCode.getAuthMethod();
- }
-
- public String getUsernameUsed() {
- return accessCode.getUsernameUsed();
- }
-
- public void setUsernameUsed(String username) {
- accessCode.setUsernameUsed(username);
+ public String getRedirectUri() {
+ return accessCode.getRedirectUri();
}
- public void resetExpiration() {
- accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
-
+ public AccessCode.Action getAction() {
+ return accessCode.getAction();
}
- public void setAuthMethod(String authMethod) {
- accessCode.setAuthMethod(authMethod);
+ public void setAction(AccessCode.Action action) {
+ accessCode.setAction(action);
+ accessCode.setTimestamp(Time.currentTime());
}
- public Set<RequiredAction> getRequiredActions() {
- Set<RequiredAction> set = new HashSet<RequiredAction>();
- for (String action : accessCode.getRequiredActions()) {
- set.add(RequiredAction.valueOf(action));
-
+ public RequiredAction getRequiredAction() {
+ AccessCode.Action action = accessCode.getAction();
+ if (action != null) {
+ switch (action) {
+ case CONFIGURE_TOTP:
+ return RequiredAction.CONFIGURE_TOTP;
+ case UPDATE_PASSWORD:
+ return RequiredAction.UPDATE_PASSWORD;
+ case UPDATE_PROFILE:
+ return RequiredAction.UPDATE_PROFILE;
+ case VERIFY_EMAIL:
+ return RequiredAction.VERIFY_EMAIL;
+ }
}
- return set;
- }
-
- public boolean hasRequiredAction(RequiredAction action) {
- return accessCode.getRequiredActions().contains(action.toString());
- }
-
- public void removeRequiredAction(RequiredAction action) {
- accessCode.getRequiredActions().remove(action.toString());
- }
-
- public void setRequiredActions(Set<RequiredAction> set) {
- Set<String> newSet = new HashSet<String>();
- for (RequiredAction action : set) {
- newSet.add(action.toString());
+ return null;
+ }
+
+ public void setRequiredAction(RequiredAction requiredAction) {
+ switch (requiredAction) {
+ case CONFIGURE_TOTP:
+ setAction(AccessCode.Action.CONFIGURE_TOTP);
+ break;
+ case UPDATE_PASSWORD:
+ setAction(AccessCode.Action.UPDATE_PASSWORD);
+ break;
+ case UPDATE_PROFILE:
+ setAction(AccessCode.Action.UPDATE_PROFILE);
+ break;
+ case VERIFY_EMAIL:
+ setAction(AccessCode.Action.VERIFY_EMAIL);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown required action " + requiredAction);
}
- accessCode.setRequiredActions(newSet);
}
public String getCode() {
- return new JWSBuilder().jsonContent(accessCode).rsa256(realm.getPrivateKey());
+ return new JWSBuilder().jsonContent(accessCode).rsa256(realm.getPrivateKey());
}
}
diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
index f73d30d..451131d 100755
--- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
@@ -14,21 +14,16 @@ import javax.ws.rs.core.UriInfo;
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AppAuthManager extends AuthenticationManager {
- protected static Logger logger = Logger.getLogger(AppAuthManager.class);
- public AppAuthManager(KeycloakSession session) {
- super(session);
- }
+ protected static Logger logger = Logger.getLogger(AppAuthManager.class);
@Override
- public AuthResult authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
- AuthResult authResult = super.authenticateIdentityCookie(realm, uriInfo, headers);
+ public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
+ AuthResult authResult = super.authenticateIdentityCookie(session, realm, uriInfo, headers);
if (authResult == null) return null;
- Cookie remember = headers.getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
- boolean rememberMe = remember != null;
// refresh the cookies!
- createLoginCookie(realm, authResult.getUser(), authResult.getSession(), uriInfo, rememberMe);
- if (rememberMe) createRememberMeCookie(realm, uriInfo);
+ createLoginCookie(realm, authResult.getUser(), authResult.getSession(), uriInfo);
+ if (authResult.getSession().isRememberMe()) createRememberMeCookie(realm, uriInfo);
return authResult;
}
@@ -44,10 +39,10 @@ public class AppAuthManager extends AuthenticationManager {
return tokenString;
}
- public AuthResult authenticateBearerToken(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
+ public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
String tokenString = extractAuthorizationHeaderToken(headers);
if (tokenString == null) return null;
- AuthResult authResult = verifyIdentityToken(realm, uriInfo, true, tokenString);
+ AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, true, tokenString);
return authResult;
}
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 8e4440c..8f5490f 100755
--- a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
+++ b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
@@ -12,6 +12,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.CredentialRepresentation;
import java.util.Arrays;
@@ -39,7 +40,7 @@ public class ApplianceBootstrap {
public void bootstrap(KeycloakSession session, String contextPath) {
String adminRealmName = Config.getAdminRealm();
- if (session.getRealm(adminRealmName) != null) {
+ if (session.realms().getRealm(adminRealmName) != null) {
return;
}
@@ -58,12 +59,12 @@ public class ApplianceBootstrap {
realm.setAccessCodeLifespanUserAction(300);
realm.setSslNotRequired(true);
realm.setRegistrationAllowed(false);
- manager.generateRealmKeys(realm);
+ KeycloakModelUtils.generateRealmKeys(realm);
realm.setAuthenticationProviders(Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER));
realm.setAuditListeners(Collections.singleton("jboss-logging"));
- UserModel adminUser = realm.addUser("admin");
+ UserModel adminUser = session.users().addUser(realm, "admin");
adminUser.setEnabled(true);
UserCredentialModel password = new UserCredentialModel();
password.setType(UserCredentialModel.PASSWORD);
diff --git a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
index af861d4..45a6dcd 100755
--- a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
@@ -4,25 +4,15 @@ import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.jboss.logging.Logger;
import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.ClaimMask;
-import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.adapters.config.BaseRealmConfig;
-import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
-import org.keycloak.representations.idm.ScopeMappingRepresentation;
-import org.keycloak.representations.idm.UserRoleMappingRepresentation;
import java.net.URI;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -40,183 +30,20 @@ public class ApplicationManager {
public ApplicationManager() {
}
-
- /**
- * Does not create scope or role mappings!
- *
- * @param realm
- * @param resourceRep
- * @return
- */
- public ApplicationModel createApplication(RealmModel realm, ApplicationRepresentation resourceRep) {
- logger.debug("************ CREATE APPLICATION: {0}" + resourceRep.getName());
- ApplicationModel applicationModel = realm.addApplication(resourceRep.getName());
- if (resourceRep.isEnabled() != null) applicationModel.setEnabled(resourceRep.isEnabled());
- applicationModel.setManagementUrl(resourceRep.getAdminUrl());
- if (resourceRep.isSurrogateAuthRequired() != null)
- applicationModel.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
- applicationModel.setBaseUrl(resourceRep.getBaseUrl());
- if (resourceRep.isBearerOnly() != null) applicationModel.setBearerOnly(resourceRep.isBearerOnly());
- if (resourceRep.isPublicClient() != null) applicationModel.setPublicClient(resourceRep.isPublicClient());
- applicationModel.updateApplication();
-
- if (resourceRep.getNotBefore() != null) {
- applicationModel.setNotBefore(resourceRep.getNotBefore());
- }
-
- applicationModel.setSecret(resourceRep.getSecret());
- if (applicationModel.getSecret() == null) {
- generateSecret(applicationModel);
- }
-
-
- if (resourceRep.getRedirectUris() != null) {
- for (String redirectUri : resourceRep.getRedirectUris()) {
- applicationModel.addRedirectUri(redirectUri);
- }
- }
- if (resourceRep.getWebOrigins() != null) {
- for (String webOrigin : resourceRep.getWebOrigins()) {
- logger.debugv("Application: {0} webOrigin: {1}", resourceRep.getName(), webOrigin);
- applicationModel.addWebOrigin(webOrigin);
- }
- } else {
- // add origins from redirect uris
- if (resourceRep.getRedirectUris() != null) {
- Set<String> origins = new HashSet<String>();
- for (String redirectUri : resourceRep.getRedirectUris()) {
- logger.info("add redirectUri to origin: " + redirectUri);
- if (redirectUri.startsWith("http:")) {
- URI uri = URI.create(redirectUri);
- String origin = uri.getScheme() + "://" + uri.getHost();
- if (uri.getPort() != -1) {
- origin += ":" + uri.getPort();
- }
- logger.debugv("adding default application origin: {0}" , origin);
- origins.add(origin);
- }
- }
- if (origins.size() > 0) {
- applicationModel.setWebOrigins(origins);
- }
- }
- }
-
- if (resourceRep.getDefaultRoles() != null) {
- applicationModel.updateDefaultRoles(resourceRep.getDefaultRoles());
- }
-
- if (resourceRep.getClaims() != null) {
- ClaimManager.setClaims(applicationModel, resourceRep.getClaims());
- } else {
- applicationModel.setAllowedClaimsMask(ClaimMask.USERNAME);
- }
-
- return applicationModel;
- }
-
- public void createRoleMappings(RealmModel realm, ApplicationModel applicationModel, List<UserRoleMappingRepresentation> mappings) {
- for (UserRoleMappingRepresentation mapping : mappings) {
- UserModel user = realm.getUser(mapping.getUsername());
- if (user == null) {
- throw new RuntimeException("User not found");
- }
- for (String roleString : mapping.getRoles()) {
- RoleModel role = applicationModel.getRole(roleString.trim());
- if (role == null) {
- role = applicationModel.addRole(roleString.trim());
- }
- user.grantRole(role);
- }
- }
- }
-
- public void createScopeMappings(RealmModel realm, ApplicationModel applicationModel, List<ScopeMappingRepresentation> mappings) {
- for (ScopeMappingRepresentation mapping : mappings) {
- for (String roleString : mapping.getRoles()) {
- RoleModel role = applicationModel.getRole(roleString.trim());
- if (role == null) {
- role = applicationModel.addRole(roleString.trim());
- }
- ClientModel client = realm.findClient(mapping.getClient());
- client.addScopeMapping(role);
- }
- }
- }
-
public ApplicationModel createApplication(RealmModel realm, String name) {
- ApplicationModel app = realm.addApplication(name);
- generateSecret(app);
-
- return app;
+ return KeycloakModelUtils.createApplication(realm, name);
}
- public UserCredentialModel generateSecret(ApplicationModel app) {
- UserCredentialModel secret = UserCredentialModel.generateSecret();
- app.setSecret(secret.getValue());
- return secret;
- }
-
- public void updateApplication(ApplicationRepresentation rep, ApplicationModel resource) {
- if (rep.getName() != null) resource.setName(rep.getName());
- if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled());
- if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly());
- if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
- if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl());
- if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl());
- if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
- resource.updateApplication();
-
- if (rep.getNotBefore() != null) {
- resource.setNotBefore(rep.getNotBefore());
- }
- if (rep.getDefaultRoles() != null) {
- resource.updateDefaultRoles(rep.getDefaultRoles());
- }
-
- List<String> redirectUris = rep.getRedirectUris();
- if (redirectUris != null) {
- resource.setRedirectUris(new HashSet<String>(redirectUris));
- }
-
- List<String> webOrigins = rep.getWebOrigins();
- if (webOrigins != null) {
- resource.setWebOrigins(new HashSet<String>(webOrigins));
- }
-
- if (rep.getClaims() != null) {
- ClaimManager.setClaims(resource, rep.getClaims());
- }
- }
-
- public ApplicationRepresentation toRepresentation(ApplicationModel applicationModel) {
- ApplicationRepresentation rep = new ApplicationRepresentation();
- rep.setId(applicationModel.getId());
- rep.setName(applicationModel.getName());
- rep.setEnabled(applicationModel.isEnabled());
- rep.setAdminUrl(applicationModel.getManagementUrl());
- rep.setPublicClient(applicationModel.isPublicClient());
- rep.setBearerOnly(applicationModel.isBearerOnly());
- rep.setSurrogateAuthRequired(applicationModel.isSurrogateAuthRequired());
- rep.setBaseUrl(applicationModel.getBaseUrl());
- rep.setNotBefore(applicationModel.getNotBefore());
-
- Set<String> redirectUris = applicationModel.getRedirectUris();
- if (redirectUris != null) {
- rep.setRedirectUris(new LinkedList<String>(redirectUris));
- }
-
- Set<String> webOrigins = applicationModel.getWebOrigins();
- if (webOrigins != null) {
- rep.setWebOrigins(new LinkedList<String>(webOrigins));
- }
-
- if (!applicationModel.getDefaultRoles().isEmpty()) {
- rep.setDefaultRoles(applicationModel.getDefaultRoles().toArray(new String[0]));
+ public boolean removeApplication(RealmModel realm, ApplicationModel application) {
+ if (realm.removeApplication(application.getId())) {
+ UserSessionProvider sessions = realmManager.getSession().sessions();
+ if (sessions != null) {
+ sessions.onClientRemoved(realm, application);
+ }
+ return true;
+ } else {
+ return false;
}
-
- return rep;
-
}
@JsonPropertyOrder({"realm", "realm-public-key", "bearer-only", "auth-server-url", "ssl-not-required",
@@ -319,6 +146,9 @@ public class ApplicationManager {
if (!applicationModel.isBearerOnly() && !applicationModel.isPublicClient()) {
buffer.append(" <credential name=\"secret\">").append(cred).append("</credential>\n");
}
+ if (applicationModel.getRoles().size() > 0) {
+ buffer.append(" <use-resource-role-mappings>true</use-resource-role-mappings>\n");
+ }
buffer.append("</secure-deployment>\n");
return buffer.toString();
}
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 26fe574..7df23f2 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -11,6 +11,7 @@ import org.keycloak.models.AuthenticationLinkModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -45,37 +46,34 @@ public class AuthenticationManager {
public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
public static final String KEYCLOAK_REMEMBER_ME = "KEYCLOAK_REMEMBER_ME";
- protected KeycloakSession session;
protected BruteForceProtector protector;
- public AuthenticationManager(KeycloakSession session) {
- this.session = session;
+ public AuthenticationManager() {
}
- public AuthenticationManager(KeycloakSession session, BruteForceProtector protector) {
- this.session = session;
+ public AuthenticationManager(BruteForceProtector protector) {
this.protector = protector;
}
- public static boolean isSessionValid(RealmModel realm, UserSessionModel session) {
- if (session == null) return false;
+ public static boolean isSessionValid(RealmModel realm, UserSessionModel userSession) {
+ if (userSession == null) return false;
int currentTime = Time.currentTime();
- int max = session.getStarted() + realm.getSsoSessionMaxLifespan();
- boolean valid = session != null && session.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout() > currentTime && max > currentTime;
+ int max = userSession.getStarted() + realm.getSsoSessionMaxLifespan();
+ boolean valid = userSession != null && userSession.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout() > currentTime && max > currentTime;
return valid;
}
- public static void logout(RealmModel realm, UserSessionModel session, UriInfo uriInfo) {
- if (session == null) return;
- UserModel user = session.getUser();
+ public static void logout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo) {
+ if (userSession == null) return;
+ UserModel user = userSession.getUser();
- logger.infov("Logging out: {0} ({1})", user.getLoginName(), session.getId());
+ logger.infov("Logging out: {0} ({1})", user.getUsername(), userSession.getId());
- realm.removeUserSession(session);
+ session.sessions().removeUserSession(realm, userSession);
expireIdentityCookie(realm, uriInfo);
expireRememberMeCookie(realm, uriInfo);
- new ResourceAdminManager().logoutUser(uriInfo.getRequestUri(), realm, user.getId(), session.getId());
+ new ResourceAdminManager().logoutUser(uriInfo.getRequestUri(), realm, user.getId(), userSession.getId());
}
@@ -96,7 +94,7 @@ public class AuthenticationManager {
return token;
}
- public void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, boolean rememberMe) {
+ public void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo) {
logger.info("createLoginCookie");
String cookiePath = getIdentityCookiePath(realm, uriInfo);
AccessToken identityToken = createIdentityToken(realm, user, session);
@@ -104,7 +102,7 @@ public class AuthenticationManager {
boolean secureOnly = !realm.isSslNotRequired();
logger.debugv("creatingLoginCookie - name: {0} path: {1}", KEYCLOAK_IDENTITY_COOKIE, cookiePath);
int maxAge = NewCookie.DEFAULT_MAX_AGE;
- if (rememberMe) {
+ if (session.isRememberMe()) {
maxAge = realm.getSsoSessionIdleTimeout();
logger.info("createLoginCookie maxAge: " + maxAge);
}
@@ -161,11 +159,11 @@ public class AuthenticationManager {
CookieHelper.addCookie(cookieName, "", path, null, "Expiring cookie", 0, secureOnly, httpOnly);
}
- public AuthResult authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
- return authenticateIdentityCookie(realm, uriInfo, headers, true);
+ public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
+ return authenticateIdentityCookie(session, realm, uriInfo, headers, true);
}
- public AuthResult authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, boolean checkActive) {
+ public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, boolean checkActive) {
logger.info("authenticateIdentityCookie");
Cookie cookie = headers.getCookies().get(KEYCLOAK_IDENTITY_COOKIE);
if (cookie == null) {
@@ -174,7 +172,7 @@ public class AuthenticationManager {
}
String tokenString = cookie.getValue();
- AuthResult authResult = verifyIdentityToken(realm, uriInfo, checkActive, tokenString);
+ AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, checkActive, tokenString);
if (authResult == null) {
expireIdentityCookie(realm, uriInfo);
return null;
@@ -183,7 +181,7 @@ public class AuthenticationManager {
return authResult;
}
- protected AuthResult verifyIdentityToken(RealmModel realm, UriInfo uriInfo, boolean checkActive, String tokenString) {
+ protected AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, boolean checkActive, String tokenString) {
try {
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName(), checkActive);
logger.info("identity token verified");
@@ -199,32 +197,27 @@ public class AuthenticationManager {
}
}
- UserModel user = realm.getUserById(token.getSubject());
+ UserModel user = session.users().getUserById(token.getSubject(), realm);
if (user == null || !user.isEnabled() ) {
logger.info("Unknown user in identity token");
return null;
}
- if (token.getIssuedAt() < user.getNotBefore()) {
- logger.info("Stale cookie");
- return null;
- }
-
- UserSessionModel session = realm.getUserSession(token.getSessionState());
- if (!isSessionValid(realm, session)) {
- if (session != null) logout(realm, session, uriInfo);
+ UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
+ if (!isSessionValid(realm, userSession)) {
+ if (userSession != null) logout(session, realm, userSession, uriInfo);
logger.info("User session not active");
return null;
}
- return new AuthResult(user, session, token);
+ return new AuthResult(user, userSession, token);
} catch (VerificationException e) {
logger.info("Failed to verify identity token", e);
}
return null;
}
- public AuthenticationStatus authenticateForm(ClientConnection clientConnection, RealmModel realm, MultivaluedMap<String, String> formData) {
+ public AuthenticationStatus authenticateForm(KeycloakSession session, ClientConnection clientConnection, RealmModel realm, MultivaluedMap<String, String> formData) {
String username = formData.getFirst(FORM_USERNAME);
if (username == null) {
logger.warn("Username not provided");
@@ -232,12 +225,12 @@ public class AuthenticationManager {
}
if (realm.isBruteForceProtected()) {
- if (protector.isTemporarilyDisabled(realm, username)) {
+ if (protector.isTemporarilyDisabled(session, realm, username)) {
return AuthenticationStatus.ACCOUNT_TEMPORARILY_DISABLED;
}
}
- AuthenticationStatus status = authenticateInternal(realm, formData, username);
+ AuthenticationStatus status = authenticateInternal(session, realm, formData, username);
if (realm.isBruteForceProtected()) {
switch (status) {
case SUCCESS:
@@ -260,13 +253,14 @@ public class AuthenticationManager {
return status;
}
- protected AuthenticationStatus authenticateInternal(RealmModel realm, MultivaluedMap<String, String> formData, String username) {
- UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
+ protected AuthenticationStatus authenticateInternal(KeycloakSession session, RealmModel realm, MultivaluedMap<String, String> formData, String username) {
+ UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
+
if (user == null) {
AuthUser authUser = AuthenticationProviderManager.getManager(realm, session).getUser(username);
if (authUser != null) {
// Create new user and link him with authentication provider
- user = realm.addUser(authUser.getUsername());
+ user = session.users().addUser(realm, authUser.getUsername());
user.setEnabled(true);
user.setFirstName(authUser.getFirstName());
user.setLastName(authUser.getLastName());
@@ -304,19 +298,19 @@ public class AuthenticationManager {
}
logger.debug("validating TOTP");
- if (!realm.validateTOTP(user, password, token)) {
+ if (!session.users().validCredentials(realm, user, UserCredentialModel.totp(token))) {
return AuthenticationStatus.INVALID_CREDENTIALS;
}
- } else {
- logger.debug("validating password for user: " + username);
+ }
- AuthProviderStatus authStatus = AuthenticationProviderManager.getManager(realm, session).validatePassword(user, password);
- if (authStatus == AuthProviderStatus.INVALID_CREDENTIALS) {
- logger.debug("invalid password for user: " + username);
- return AuthenticationStatus.INVALID_CREDENTIALS;
- } else if (authStatus == AuthProviderStatus.FAILED) {
- return AuthenticationStatus.FAILED;
- }
+ logger.debug("validating password for user: " + username);
+
+ AuthProviderStatus authStatus = AuthenticationProviderManager.getManager(realm, session).validatePassword(user, password);
+ if (authStatus == AuthProviderStatus.INVALID_CREDENTIALS) {
+ logger.debug("invalid password for user: " + username);
+ return AuthenticationStatus.INVALID_CREDENTIALS;
+ } else if (authStatus == AuthProviderStatus.FAILED) {
+ return AuthenticationStatus.FAILED;
}
if (!user.getRequiredActions().isEmpty()) {
@@ -343,7 +337,7 @@ public class AuthenticationManager {
private boolean checkEnabled(UserModel user) {
if (!user.isEnabled()) {
- logger.warn("AccountProvider is disabled, contact admin. " + user.getLoginName());
+ logger.warn("AccountProvider is disabled, contact admin. " + user.getUsername());
return false;
} else {
return true;
diff --git a/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java b/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
index f44b752..3dab830 100755
--- a/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
+++ b/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
@@ -83,7 +83,7 @@ public class BruteForceProtector implements Runnable {
logFailure(event);
UsernameLoginFailureModel user = getUserModel(session, event);
if (user == null) {
- user = realm.addUserLoginFailure(event.username);
+ user = session.sessions().addUserLoginFailure(realm, event.username);
}
user.setLastIPFailure(event.ip);
long currentTime = System.currentTimeMillis();
@@ -122,13 +122,13 @@ public class BruteForceProtector implements Runnable {
protected UsernameLoginFailureModel getUserModel(KeycloakSession session, LoginEvent event) {
RealmModel realm = getRealmModel(session, event);
if (realm == null) return null;
- UsernameLoginFailureModel user = realm.getUserLoginFailure(event.username);
+ UsernameLoginFailureModel user = session.sessions().getUserLoginFailure(realm, event.username);
if (user == null) return null;
return user;
}
protected RealmModel getRealmModel(KeycloakSession session, LoginEvent event) {
- RealmModel realm = session.getRealm(event.realmId);
+ RealmModel realm = session.realms().getRealm(event.realmId);
if (realm == null) return null;
return realm;
}
@@ -237,8 +237,8 @@ public class BruteForceProtector implements Runnable {
}
}
- public boolean isTemporarilyDisabled(RealmModel realm, String username) {
- UsernameLoginFailureModel failure = realm.getUserLoginFailure(username);
+ public boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, String username) {
+ UsernameLoginFailureModel failure = session.sessions().getUserLoginFailure(realm, username);
if (failure == null) {
return false;
}
diff --git a/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java b/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java
old mode 100644
new mode 100755
index a11fd74..468a146
--- a/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java
@@ -1,15 +1,11 @@
package org.keycloak.services.managers;
-import java.util.Hashtable;
-import java.util.Map;
+import org.jboss.logging.Logger;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
-
-import org.jboss.logging.Logger;
-import org.keycloak.models.LDAPConstants;
-import org.keycloak.models.RealmModel;
+import java.util.Hashtable;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
diff --git a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
index 56559cb..f69df51 100755
--- a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
@@ -4,99 +4,39 @@ import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserSessionProvider;
import org.keycloak.representations.adapters.config.BaseRealmConfig;
import org.keycloak.representations.idm.CredentialRepresentation;
-import org.keycloak.representations.idm.OAuthClientRepresentation;
import java.net.URI;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthClientManager {
- protected RealmModel realm;
- public OAuthClientManager(RealmModel realm) {
- this.realm = realm;
- }
+ private RealmManager realmManager;
- public UserCredentialModel generateSecret(OAuthClientModel app) {
- UserCredentialModel secret = UserCredentialModel.generateSecret();
- app.setSecret(secret.getValue());
- return secret;
+ public OAuthClientManager() {
}
-
- public OAuthClientModel create(String name) {
- OAuthClientModel model = realm.addOAuthClient(name);
- generateSecret(model);
- return model;
+ public OAuthClientManager(RealmManager realmManager) {
+ this.realmManager = realmManager;
}
- public OAuthClientModel create(OAuthClientRepresentation rep) {
- OAuthClientModel model = create(rep.getName());
- update(rep, model);
- return model;
- }
-
- public void update(OAuthClientRepresentation rep, OAuthClientModel model) {
- if (rep.getName() != null) model.setClientId(rep.getName());
- if (rep.isEnabled() != null) model.setEnabled(rep.isEnabled());
- if (rep.isPublicClient() != null) model.setPublicClient(rep.isPublicClient());
- if (rep.isDirectGrantsOnly() != null) model.setDirectGrantsOnly(rep.isDirectGrantsOnly());
- if (rep.getClaims() != null) {
- ClaimManager.setClaims(model, rep.getClaims());
- }
- if (rep.getNotBefore() != null) {
- model.setNotBefore(rep.getNotBefore());
- }
- if (rep.getSecret() != null) model.setSecret(rep.getSecret());
- List<String> redirectUris = rep.getRedirectUris();
- if (redirectUris != null) {
- model.setRedirectUris(new HashSet<String>(redirectUris));
- }
-
- List<String> webOrigins = rep.getWebOrigins();
- if (webOrigins != null) {
- model.setWebOrigins(new HashSet<String>(webOrigins));
- }
-
- if (rep.getClaims() != null) {
- ClaimManager.setClaims(model, rep.getClaims());
+ public boolean removeClient(RealmModel realm, OAuthClientModel client) {
+ if (realm.removeOAuthClient(client.getId())) {
+ UserSessionProvider sessions = realmManager.getSession().sessions();
+ if (sessions != null) {
+ realmManager.getSession().sessions().onClientRemoved(realm, client);
+ }
+ return true;
+ } else {
+ return false;
}
-
- if (rep.getNotBefore() != null) {
- model.setNotBefore(rep.getNotBefore());
- }
-
- }
-
- public static OAuthClientRepresentation toRepresentation(OAuthClientModel model) {
- OAuthClientRepresentation rep = new OAuthClientRepresentation();
- rep.setId(model.getId());
- rep.setName(model.getClientId());
- rep.setEnabled(model.isEnabled());
- rep.setPublicClient(model.isPublicClient());
- rep.setDirectGrantsOnly(model.isDirectGrantsOnly());
- Set<String> redirectUris = model.getRedirectUris();
- if (redirectUris != null) {
- rep.setRedirectUris(new LinkedList<String>(redirectUris));
- }
-
- Set<String> webOrigins = model.getWebOrigins();
- if (webOrigins != null) {
- rep.setWebOrigins(new LinkedList<String>(webOrigins));
- }
- rep.setNotBefore(model.getNotBefore());
- return rep;
}
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required",
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 6fae8e7..b5d4dd5 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -1,48 +1,26 @@
package org.keycloak.services.managers;
import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.exportimport.util.ImportUtils;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.AuthenticationLinkModel;
-import org.keycloak.models.AuthenticationProviderModel;
-import org.keycloak.models.ClientModel;
-import org.keycloak.Config;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.OAuthClientModel;
-import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
-import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
-import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.representations.idm.ApplicationRepresentation;
-import org.keycloak.representations.idm.AuthenticationLinkRepresentation;
-import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
-import org.keycloak.representations.idm.CredentialRepresentation;
-import org.keycloak.representations.idm.OAuthClientRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.RealmAuditRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.representations.idm.ScopeMappingRepresentation;
-import org.keycloak.representations.idm.SocialLinkRepresentation;
-import org.keycloak.representations.idm.SocialMappingRepresentation;
-import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.representations.idm.UserRoleMappingRepresentation;
-
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
+
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
/**
* Per request object
@@ -54,6 +32,7 @@ public class RealmManager {
protected static final Logger logger = Logger.getLogger(RealmManager.class);
protected KeycloakSession session;
+ protected RealmProvider model;
protected String contextPath = "";
public String getContextPath() {
@@ -66,6 +45,11 @@ public class RealmManager {
public RealmManager(KeycloakSession session) {
this.session = session;
+ this.model = session.realms();
+ }
+
+ public KeycloakSession getSession() {
+ return session;
}
public RealmModel getKeycloakAdminstrationRealm() {
@@ -73,11 +57,11 @@ public class RealmManager {
}
public RealmModel getRealm(String id) {
- return session.getRealm(id);
+ return model.getRealm(id);
}
public RealmModel getRealmByName(String name) {
- return session.getRealmByName(name);
+ return model.getRealmByName(name);
}
public RealmModel createRealm(String name) {
@@ -86,7 +70,7 @@ public class RealmManager {
public RealmModel createRealm(String id, String name) {
if (id == null) id = KeycloakModelUtils.generateId();
- RealmModel realm = session.createRealm(id, name);
+ RealmModel realm = model.createRealm(id, name);
realm.setName(name);
// setup defaults
@@ -122,10 +106,6 @@ public class RealmManager {
adminConsole.addScopeMapping(adminRole);
}
- public String getMasterRealmAdminApplicationName(RealmModel realm) {
- return realm.getName() + "-realm";
- }
-
public String getRealmAdminApplicationName(RealmModel realm) {
return "realm-management";
}
@@ -144,85 +124,16 @@ public class RealmManager {
}
public boolean removeRealm(RealmModel realm) {
- boolean removed = session.removeRealm(realm.getId());
+ boolean removed = model.removeRealm(realm.getId());
if (removed) {
- getKeycloakAdminstrationRealm().removeApplication(realm.getMasterAdminApp().getId());
- }
- return removed;
- }
-
- public void generateRealmKeys(RealmModel realm) {
- KeyPair keyPair = null;
- try {
- keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- realm.setPrivateKey(keyPair.getPrivate());
- realm.setPublicKey(keyPair.getPublic());
- }
-
- public void updateRealm(RealmRepresentation rep, RealmModel realm) {
- if (rep.getRealm() != null) {
- realm.setName(rep.getRealm());
- }
- if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled());
- if (rep.isSocial() != null) realm.setSocial(rep.isSocial());
- if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected());
- if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
- if (rep.getMinimumQuickLoginWaitSeconds() != null) realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
- if (rep.getWaitIncrementSeconds() != null) realm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds());
- if (rep.getQuickLoginCheckMilliSeconds() != null) realm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds());
- if (rep.getMaxDeltaTimeSeconds() != null) realm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds());
- if (rep.getFailureFactor() != null) realm.setFailureFactor(rep.getFailureFactor());
- if (rep.isPasswordCredentialGrantAllowed() != null) realm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed());
- 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)
- realm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin());
- if (rep.isSslNotRequired() != null) realm.setSslNotRequired((rep.isSslNotRequired()));
- if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
- if (rep.getAccessCodeLifespanUserAction() != null)
- realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
- if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore());
- if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
- if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
- if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
- if (rep.getRequiredCredentials() != null) {
- realm.updateRequiredCredentials(rep.getRequiredCredentials());
- }
- if (rep.getLoginTheme() != null) realm.setLoginTheme(rep.getLoginTheme());
- if (rep.getAccountTheme() != null) realm.setAccountTheme(rep.getAccountTheme());
- if (rep.getAdminTheme() != null) realm.setAdminTheme(rep.getAdminTheme());
- if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme());
-
- if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
+ new ApplicationManager(this).removeApplication(getKeycloakAdminstrationRealm(), realm.getMasterAdminApp());
- if (rep.getDefaultRoles() != null) {
- realm.updateDefaultRoles(rep.getDefaultRoles().toArray(new String[rep.getDefaultRoles().size()]));
- }
-
- if (rep.getSmtpServer() != null) {
- realm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
- }
-
- if (rep.getSocialProviders() != null) {
- realm.setSocialConfig(new HashMap(rep.getSocialProviders()));
- }
-
- if (rep.getLdapServer() != null) {
- realm.setLdapServerConfig(new HashMap(rep.getLdapServer()));
- }
- if (rep.getAuthenticationProviders() != null) {
- List<AuthenticationProviderModel> authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders());
- realm.setAuthenticationProviders(authProviderModels);
- }
-
- if ("GENERATE".equals(rep.getPublicKey())) {
- generateRealmKeys(realm);
+ UserSessionProvider sessions = session.sessions();
+ if (sessions != null) {
+ sessions.onRealmRemoved(realm);
+ }
}
+ return removed;
}
public void updateRealmAudit(RealmAuditRepresentation rep, RealmModel realm) {
@@ -233,32 +144,9 @@ public class RealmManager {
}
}
+ // Should be RealmManager moved to model/api instead of referencing methods this way?
private void setupMasterAdminManagement(RealmModel realm) {
- RealmModel adminRealm;
- RoleModel adminRole;
-
- if (realm.getName().equals(Config.getAdminRealm())) {
- adminRealm = realm;
-
- adminRole = realm.addRole(AdminRoles.ADMIN);
-
- RoleModel createRealmRole = realm.addRole(AdminRoles.CREATE_REALM);
- adminRole.addCompositeRole(createRealmRole);
- } else {
- adminRealm = session.getRealmByName(Config.getAdminRealm());
- adminRole = adminRealm.getRole(AdminRoles.ADMIN);
- }
-
- ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
-
- ApplicationModel realmAdminApp = applicationManager.createApplication(adminRealm, getMasterRealmAdminApplicationName(realm));
- realmAdminApp.setBearerOnly(true);
- realm.setMasterAdminApp(realmAdminApp);
-
- for (String r : AdminRoles.ALL_REALM_ROLES) {
- RoleModel role = realmAdminApp.addRole(r);
- adminRole.addCompositeRole(role);
- }
+ ImportUtils.setupMasterAdminManagement(model, realm);
}
private void setupRealmAdminManagement(RealmModel realm) {
@@ -308,283 +196,7 @@ public class RealmManager {
}
public void importRealm(RealmRepresentation rep, RealmModel newRealm) {
- newRealm.setName(rep.getRealm());
- if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled());
- if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial());
- if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected());
- if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
- if (rep.getMinimumQuickLoginWaitSeconds() != null) newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
- if (rep.getWaitIncrementSeconds() != null) newRealm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds());
- if (rep.getQuickLoginCheckMilliSeconds() != null) newRealm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds());
- if (rep.getMaxDeltaTimeSeconds() != null) newRealm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds());
- if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor());
-
- if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore());
-
- if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
- else newRealm.setAccessTokenLifespan(300);
-
- if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
- else newRealm.setSsoSessionIdleTimeout(600);
- if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
- else newRealm.setSsoSessionMaxLifespan(36000);
-
- if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
- else newRealm.setAccessCodeLifespan(60);
-
- if (rep.getAccessCodeLifespanUserAction() != null)
- newRealm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
- else newRealm.setAccessCodeLifespanUserAction(300);
-
- if (rep.isSslNotRequired() != null) newRealm.setSslNotRequired(rep.isSslNotRequired());
- if (rep.isPasswordCredentialGrantAllowed() != null) newRealm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed());
- 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)
- newRealm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin());
- if (rep.getPrivateKey() == null || rep.getPublicKey() == null) {
- generateRealmKeys(newRealm);
- } else {
- newRealm.setPrivateKeyPem(rep.getPrivateKey());
- newRealm.setPublicKeyPem(rep.getPublicKey());
- }
- if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme());
- if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme());
- if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme());
- if (rep.getEmailTheme() != null) newRealm.setEmailTheme(rep.getEmailTheme());
-
- Map<String, UserModel> userMap = new HashMap<String, UserModel>();
-
- if (rep.getRequiredCredentials() != null) {
- for (String requiredCred : rep.getRequiredCredentials()) {
- addRequiredCredential(newRealm, requiredCred);
- }
- } else {
- addRequiredCredential(newRealm, CredentialRepresentation.PASSWORD);
- }
-
- if (rep.getPasswordPolicy() != null) newRealm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
-
- if (rep.getUsers() != null) {
- for (UserRepresentation userRep : rep.getUsers()) {
- UserModel user = createUser(newRealm, userRep);
- userMap.put(user.getLoginName(), user);
- }
- }
-
- if (rep.getApplications() != null) {
- Map<String, ApplicationModel> appMap = createApplications(rep, newRealm);
- }
-
- if (rep.getRoles() != null) {
- if (rep.getRoles().getRealm() != null) { // realm roles
- for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
- createRole(newRealm, roleRep);
- }
- }
- if (rep.getRoles().getApplication() != null) {
- for (Map.Entry<String, List<RoleRepresentation>> entry : rep.getRoles().getApplication().entrySet()) {
- ApplicationModel app = newRealm.getApplicationByName(entry.getKey());
- if (app == null) {
- throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey());
- }
- for (RoleRepresentation roleRep : entry.getValue()) {
- RoleModel role = app.addRole(roleRep.getName());
- role.setDescription(roleRep.getDescription());
- }
- }
- }
- // now that all roles are created, re-iterate and set up composites
- if (rep.getRoles().getRealm() != null) { // realm roles
- for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
- RoleModel role = newRealm.getRole(roleRep.getName());
- addComposites(role, roleRep, newRealm);
- }
- }
- if (rep.getRoles().getApplication() != null) {
- for (Map.Entry<String, List<RoleRepresentation>> entry : rep.getRoles().getApplication().entrySet()) {
- ApplicationModel app = newRealm.getApplicationByName(entry.getKey());
- if (app == null) {
- throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey());
- }
- for (RoleRepresentation roleRep : entry.getValue()) {
- RoleModel role = app.getRole(roleRep.getName());
- addComposites(role, roleRep, newRealm);
- }
- }
- }
- }
-
-
- if (rep.getDefaultRoles() != null) {
- for (String roleString : rep.getDefaultRoles()) {
- newRealm.addDefaultRole(roleString.trim());
- }
- }
-
- if (rep.getOauthClients() != null) {
- createOAuthClients(rep, newRealm);
- }
-
- // Now that all possible users and applications are created (users, apps, and oauth clients), do role mappings and scope mappings
-
- Map<String, ApplicationModel> appMap = newRealm.getApplicationNameMap();
-
- if (rep.getApplicationRoleMappings() != null) {
- ApplicationManager manager = new ApplicationManager(this);
- for (Map.Entry<String, List<UserRoleMappingRepresentation>> entry : rep.getApplicationRoleMappings().entrySet()) {
- ApplicationModel app = appMap.get(entry.getKey());
- if (app == null) {
- throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey());
- }
- manager.createRoleMappings(newRealm, app, entry.getValue());
- }
- }
-
- if (rep.getApplicationScopeMappings() != null) {
- ApplicationManager manager = new ApplicationManager(this);
- for (Map.Entry<String, List<ScopeMappingRepresentation>> entry : rep.getApplicationScopeMappings().entrySet()) {
- ApplicationModel app = appMap.get(entry.getKey());
- if (app == null) {
- throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey());
- }
- manager.createScopeMappings(newRealm, app, entry.getValue());
- }
- }
-
-
- if (rep.getRoleMappings() != null) {
- for (UserRoleMappingRepresentation mapping : rep.getRoleMappings()) {
- UserModel user = userMap.get(mapping.getUsername());
- for (String roleString : mapping.getRoles()) {
- RoleModel role = newRealm.getRole(roleString.trim());
- if (role == null) {
- role = newRealm.addRole(roleString.trim());
- }
- user.grantRole(role);
- }
- }
- }
-
- if (rep.getScopeMappings() != null) {
- for (ScopeMappingRepresentation scope : rep.getScopeMappings()) {
- for (String roleString : scope.getRoles()) {
- RoleModel role = newRealm.getRole(roleString.trim());
- if (role == null) {
- role = newRealm.addRole(roleString.trim());
- }
- ClientModel client = newRealm.findClient(scope.getClient());
- client.addScopeMapping(role);
- }
-
- }
- }
-
- if (rep.getSocialMappings() != null) {
- for (SocialMappingRepresentation socialMapping : rep.getSocialMappings()) {
- UserModel user = userMap.get(socialMapping.getUsername());
- for (SocialLinkRepresentation link : socialMapping.getSocialLinks()) {
- SocialLinkModel mappingModel = new SocialLinkModel(link.getSocialProvider(), link.getSocialUserId(), link.getSocialUsername());
- newRealm.addSocialLink(user, mappingModel);
- }
- }
- }
-
- if (rep.getSmtpServer() != null) {
- newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
- }
-
- if (rep.getSocialProviders() != null) {
- newRealm.setSocialConfig(new HashMap(rep.getSocialProviders()));
- }
- if (rep.getLdapServer() != null) {
- newRealm.setLdapServerConfig(new HashMap(rep.getLdapServer()));
- }
-
- if (rep.getAuthenticationProviders() != null) {
- List<AuthenticationProviderModel> authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders());
- newRealm.setAuthenticationProviders(authProviderModels);
- } else {
- List<AuthenticationProviderModel> authProviderModels = Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER);
- newRealm.setAuthenticationProviders(authProviderModels);
- }
- }
-
- public void addComposites(RoleModel role, RoleRepresentation roleRep, RealmModel realm) {
- if (roleRep.getComposites() == null) return;
- if (roleRep.getComposites().getRealm() != null) {
- for (String roleStr : roleRep.getComposites().getRealm()) {
- RoleModel realmRole = realm.getRole(roleStr);
- if (realmRole == null) throw new RuntimeException("Unable to find composite realm role: " + roleStr);
- role.addCompositeRole(realmRole);
- }
- }
- if (roleRep.getComposites().getApplication() != null) {
- for (Map.Entry<String, List<String>> entry : roleRep.getComposites().getApplication().entrySet()) {
- ApplicationModel app = realm.getApplicationByName(entry.getKey());
- if (app == null) {
- throw new RuntimeException("App doesn't exist in role definitions: " + roleRep.getName());
- }
- for (String roleStr : entry.getValue()) {
- RoleModel appRole = app.getRole(roleStr);
- if (appRole == null) throw new RuntimeException("Unable to find composite app role: " + roleStr);
- role.addCompositeRole(appRole);
- }
-
- }
-
- }
-
- }
-
- public void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
- RoleModel role = newRealm.addRole(roleRep.getName());
- if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
- }
-
- public void createRole(RealmModel newRealm, ApplicationModel app, RoleRepresentation roleRep) {
- RoleModel role = app.addRole(roleRep.getName());
- if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
- }
-
-
- public UserModel createUser(RealmModel newRealm, UserRepresentation userRep) {
- 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<String, String> entry : userRep.getAttributes().entrySet()) {
- user.setAttribute(entry.getKey(), entry.getValue());
- }
- }
- if (userRep.getRequiredActions() != null) {
- for (String requiredAction : userRep.getRequiredActions()) {
- user.addRequiredAction(RequiredAction.valueOf(requiredAction));
- }
- }
- if (userRep.getCredentials() != null) {
- for (CredentialRepresentation cred : userRep.getCredentials()) {
- UserCredentialModel credential = fromRepresentation(cred);
- user.updateCredential(credential);
- }
- }
- if (userRep.getAuthenticationLink() != null) {
- AuthenticationLinkRepresentation link = userRep.getAuthenticationLink();
- AuthenticationLinkModel authLink = new AuthenticationLinkModel(link.getAuthProvider(), link.getAuthUserId());
- user.setAuthenticationLink(authLink);
- }
- return user;
- }
-
- public static UserCredentialModel fromRepresentation(CredentialRepresentation cred) {
- UserCredentialModel credential = new UserCredentialModel();
- credential.setType(cred.getType());
- credential.setValue(cred.getValue());
- return credential;
+ RepresentationToModel.importRealm(session, rep, newRealm);
}
/**
@@ -602,40 +214,7 @@ public class RealmManager {
if (searchString == null) {
return Collections.emptyList();
}
- return realmModel.searchForUser(searchString.trim());
- }
-
- public void addRequiredCredential(RealmModel newRealm, String requiredCred) {
- newRealm.addRequiredCredential(requiredCred);
- }
-
- protected Map<String, ApplicationModel> createApplications(RealmRepresentation rep, RealmModel realm) {
- Map<String, ApplicationModel> appMap = new HashMap<String, ApplicationModel>();
- ApplicationManager manager = new ApplicationManager(this);
- for (ApplicationRepresentation resourceRep : rep.getApplications()) {
- ApplicationModel app = manager.createApplication(realm, resourceRep);
- appMap.put(app.getName(), app);
- }
- return appMap;
+ return session.users().searchForUser(searchString.trim(), realmModel);
}
- protected void createOAuthClients(RealmRepresentation realmRep, RealmModel realm) {
- OAuthClientManager manager = new OAuthClientManager(realm);
- for (OAuthClientRepresentation rep : realmRep.getOauthClients()) {
- OAuthClientModel app = manager.create(rep);
- }
- }
-
- protected List<AuthenticationProviderModel> convertAuthenticationProviders(List<AuthenticationProviderRepresentation> authenticationProviders) {
- List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
-
- for (AuthenticationProviderRepresentation representation : authenticationProviders) {
- AuthenticationProviderModel model = new AuthenticationProviderModel(representation.getProviderName(),
- representation.isPasswordUpdateSupported(), representation.getConfig());
- result.add(model);
- }
- return result;
- }
-
-
}
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 2498eda..a47892f 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -8,6 +8,7 @@ import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
import org.keycloak.TokenIdGenerator;
import org.keycloak.adapters.AdapterConstants;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.adapters.action.LogoutAction;
@@ -34,11 +35,11 @@ import java.util.Map;
public class ResourceAdminManager {
protected static Logger logger = Logger.getLogger(ResourceAdminManager.class);
- public SessionStats getSessionStats(URI requestUri, RealmModel realm, ApplicationModel application, boolean users) {
+ public SessionStats getSessionStats(URI requestUri, KeycloakSession session, RealmModel realm, ApplicationModel application, boolean users) {
ApacheHttpClient4Executor executor = createExecutor();
try {
- return getSessionStats(requestUri, realm, application, users, executor);
+ return getSessionStats(requestUri, session, realm, application, users, executor);
} finally {
executor.getHttpClient().getConnectionManager().shutdown();
}
@@ -52,7 +53,7 @@ public class ResourceAdminManager {
return new ApacheHttpClient4Executor(client);
}
- public SessionStats getSessionStats(URI requestUri, RealmModel realm, ApplicationModel application, boolean users, ApacheHttpClient4Executor client) {
+ public SessionStats getSessionStats(URI requestUri, KeycloakSession session, RealmModel realm, ApplicationModel application, boolean users, ApacheHttpClient4Executor client) {
String managementUrl = getManagementUrl(requestUri, application);
if (managementUrl != null) {
SessionStatsAction adminAction = new SessionStatsAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, application.getName());
@@ -77,9 +78,9 @@ public class ResourceAdminManager {
if (users && stats.getUsers() != null) {
Map<String, UserStats> newUsers = new HashMap<String, UserStats>();
for (Map.Entry<String, UserStats> entry : stats.getUsers().entrySet()) {
- UserModel user = realm.getUserById(entry.getKey());
+ UserModel user = session.users().getUserById(entry.getKey(), realm);
if (user == null) continue;
- newUsers.put(user.getLoginName(), entry.getValue());
+ newUsers.put(user.getUsername(), entry.getValue());
}
stats.setUsers(newUsers);
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 c28825a..727679e 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -1,7 +1,6 @@
package org.keycloak.services.managers;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.OAuthErrorException;
import org.keycloak.audit.Audit;
import org.keycloak.audit.Details;
@@ -11,6 +10,7 @@ import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
@@ -23,17 +23,12 @@ import org.keycloak.representations.IDToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.util.Time;
-import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
/**
* Stateful object that creates tokens and manages oauth access codes
@@ -44,23 +39,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class TokenManager {
protected static final Logger logger = Logger.getLogger(TokenManager.class);
- /*
- protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
-
- public void clearAccessCodes() {
- accessCodeMap.clear();
- }
-
- public AccessCodeEntry getAccessCode(String key) {
- return accessCodeMap.get(key);
- }
-
- public AccessCodeEntry pullAccessCode(String key) {
- return accessCodeMap.remove(key);
- }
- */
-
- public AccessCodeEntry parseCode(String code, RealmModel realm) {
+ public AccessCodeEntry parseCode(String code, KeycloakSession session, RealmModel realm) {
try {
JWSInput input = new JWSInput(code);
if (!RSAProvider.verify(input, realm.getPublicKey())) {
@@ -68,7 +47,7 @@ public class TokenManager {
return null;
}
AccessCode accessCode = input.readJsonContent(AccessCode.class);
- return new AccessCodeEntry(realm, accessCode);
+ return new AccessCodeEntry(session, realm, accessCode);
} catch (Exception e) {
logger.error("error parsing access code", e);
return null;
@@ -92,29 +71,31 @@ public class TokenManager {
- public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
- return createAccessCodeEntry(scopeParam, state, redirect, realm, client, user, session);
+ public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, KeycloakSession keycloakSession, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
+ return createAccessCodeEntry(scopeParam, state, redirect, keycloakSession, realm, client, user, session);
}
- private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
- List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
- MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
-
- AccessToken token = createClientAccessToken(scopeParam, realm, client, user, session, realmRolesRequested, resourceRolesRequested);
- if (session != null) token.setSessionState(session.getId());
+ private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, KeycloakSession keycloakSession, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
AccessCode code = new AccessCode();
code.setId(UUID.randomUUID().toString() + System.currentTimeMillis());
- code.setAccessToken(token);
+ code.setClientId(client.getClientId());
+ code.setUserId(user.getId());
code.setTimestamp(Time.currentTime());
- code.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
- code.setState(state);
+ code.setSessionState(session != null ? session.getId() : null);
code.setRedirectUri(redirect);
- AccessCodeEntry entry = new AccessCodeEntry(realm, code);
- return entry;
+ code.setState(state);
+ Set<String> requestedRoles = new HashSet<String>();
+ for (RoleModel r : getAccess(scopeParam, client, user)) {
+ requestedRoles.add(r.getId());
+ }
+ code.setRequestedRoles(requestedRoles);
+
+ AccessCodeEntry entry = new AccessCodeEntry(keycloakSession, realm, code);
+ return entry;
}
- public AccessToken refreshAccessToken(UriInfo uriInfo, RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
+ public AccessToken refreshAccessToken(KeycloakSession session, UriInfo uriInfo, RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
JWSInput jws = new JWSInput(encodedRefreshToken);
RefreshToken refreshToken = null;
try {
@@ -135,7 +116,7 @@ public class TokenManager {
audit.user(refreshToken.getSubject()).session(refreshToken.getSessionState()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId());
- UserModel user = realm.getUserById(refreshToken.getSubject());
+ UserModel user = session.users().getUserById(refreshToken.getSubject(), realm);
if (user == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown user");
}
@@ -144,10 +125,10 @@ public class TokenManager {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled");
}
- UserSessionModel session = realm.getUserSession(refreshToken.getSessionState());
+ UserSessionModel userSession = session.sessions().getUserSession(realm, refreshToken.getSessionState());
int currentTime = Time.currentTime();
- if (!AuthenticationManager.isSessionValid(realm, session)) {
- AuthenticationManager.logout(realm, session, uriInfo);
+ if (!AuthenticationManager.isSessionValid(realm, userSession)) {
+ AuthenticationManager.logout(session, realm, userSession, uriInfo);
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
}
@@ -155,15 +136,58 @@ public class TokenManager {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Unmatching clients", "Unmatching clients");
}
- if (refreshToken.getIssuedAt() < client.getNotBefore() || refreshToken.getIssuedAt() < user.getNotBefore()) {
+ if (refreshToken.getIssuedAt() < client.getNotBefore()) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token");
}
+ verifyAccess(refreshToken, realm, client, user);
+
+ AccessToken accessToken = initToken(realm, client, user, userSession);
+ accessToken.setRealmAccess(refreshToken.getRealmAccess());
+ accessToken.setResourceAccess(refreshToken.getResourceAccess());
+
+ // only refresh session if next token refresh will be after idle timeout
+ if (currentTime + realm.getAccessTokenLifespan() > userSession.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout()) {
+ userSession.setLastSessionRefresh(currentTime);
+ }
+
+ return accessToken;
+ }
+
+ public AccessToken createClientAccessToken(Set<RoleModel> requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
+ AccessToken token = initToken(realm, client, user, session);
+ for (RoleModel role : requestedRoles) {
+ addComposites(token, role);
+ }
+ return token;
+ }
+
+ public Set<RoleModel> getAccess(String scopeParam, ClientModel client, UserModel user) {
+ // todo scopeParam is ignored until we figure out a scheme that fits with openid connect
+ Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
+
+ Set<RoleModel> roleMappings = user.getRoleMappings();
+ Set<RoleModel> scopeMappings = client.getScopeMappings();
+ if (client instanceof ApplicationModel) {
+ scopeMappings.addAll(((ApplicationModel) client).getRoles());
+ }
+
+ for (RoleModel role : roleMappings) {
+ for (RoleModel desiredRole : scopeMappings) {
+ Set<RoleModel> visited = new HashSet<RoleModel>();
+ applyScope(role, desiredRole, visited, requestedRoles);
+ }
+ }
+
+ return requestedRoles;
+ }
+
+ public void verifyAccess(AccessToken token, RealmModel realm, ClientModel client, UserModel user) throws OAuthErrorException {
ApplicationModel clientApp = (client instanceof ApplicationModel) ? (ApplicationModel)client : null;
- if (refreshToken.getRealmAccess() != null) {
- for (String roleName : refreshToken.getRealmAccess().getRoles()) {
+ if (token.getRealmAccess() != null) {
+ for (String roleName : token.getRealmAccess().getRoles()) {
RoleModel role = realm.getRole(roleName);
if (role == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid realm role " + roleName);
@@ -176,8 +200,8 @@ public class TokenManager {
}
}
}
- if (refreshToken.getResourceAccess() != null) {
- for (Map.Entry<String, AccessToken.Access> entry : refreshToken.getResourceAccess().entrySet()) {
+ if (token.getResourceAccess() != null) {
+ for (Map.Entry<String, AccessToken.Access> entry : token.getResourceAccess().entrySet()) {
ApplicationModel app = realm.getApplicationByName(entry.getKey());
if (app == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Application no longer exists", "Application no longer exists: " + app.getName());
@@ -197,72 +221,11 @@ public class TokenManager {
}
}
-
- AccessToken accessToken = initToken(realm, client, user, session);
- accessToken.setRealmAccess(refreshToken.getRealmAccess());
- accessToken.setResourceAccess(refreshToken.getResourceAccess());
-
- // only refresh session if next token refresh will be after idle timeout
- if (currentTime + realm.getAccessTokenLifespan() > session.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout()) {
- session.setLastSessionRefresh(currentTime);
- }
-
- return accessToken;
- }
-
- public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
- return createClientAccessToken(scopeParam, realm, client, user, session, new LinkedList<RoleModel>(), new MultivaluedMapImpl<String, RoleModel>());
- }
-
- public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested) {
- // todo scopeParam is ignored until we figure out a scheme that fits with openid connect
-
- Set<RoleModel> roleMappings = user.getRoleMappings();
- Set<RoleModel> scopeMappings = client.getScopeMappings();
- ApplicationModel clientApp = (client instanceof ApplicationModel) ? (ApplicationModel)client : null;
- Set<RoleModel> clientAppRoles = clientApp == null ? null : clientApp.getRoles();
- if (clientAppRoles != null) scopeMappings.addAll(clientAppRoles);
-
- Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
-
- for (RoleModel role : roleMappings) {
- if (clientApp != null && role.getContainer().equals(clientApp)) requestedRoles.add(role);
- for (RoleModel desiredRole : scopeMappings) {
- Set<RoleModel> visited = new HashSet<RoleModel>();
- applyScope(role, desiredRole, visited, requestedRoles);
- }
- }
-
- for (RoleModel role : requestedRoles) {
- if (role.getContainer() instanceof RealmModel) {
- realmRolesRequested.add(role);
- } else if (role.getContainer() instanceof ApplicationModel) {
- ApplicationModel app = (ApplicationModel)role.getContainer();
- resourceRolesRequested.add(app.getName(), role);
- }
- }
-
- AccessToken token = initToken(realm, client, user, session);
-
- if (realmRolesRequested.size() > 0) {
- for (RoleModel role : realmRolesRequested) {
- addComposites(token, role);
- }
- }
-
- if (resourceRolesRequested.size() > 0) {
- for (List<RoleModel> roles : resourceRolesRequested.values()) {
- for (RoleModel role : roles) {
- addComposites(token, role);
- }
- }
- }
- return token;
}
public void initClaims(IDToken token, ClientModel model, UserModel user) {
if (ClaimMask.hasUsername(model.getAllowedClaimsMask())) {
- token.setPreferredUsername(user.getLoginName());
+ token.setPreferredUsername(user.getUsername());
}
if (ClaimMask.hasEmail(model.getAllowedClaimsMask())) {
token.setEmail(user.getEmail());
@@ -284,7 +247,7 @@ public class TokenManager {
token.subject(user.getId());
token.audience(realm.getName());
token.issuedNow();
- token.issuedFor(client.getLoginName());
+ token.issuedFor(client.getUsername());
token.issuer(realm.getName());
if (realm.getAccessTokenLifespan() > 0) {
token.expiration(Time.currentTime() + realm.getAccessTokenLifespan());
@@ -380,7 +343,8 @@ public class TokenManager {
}
public AccessTokenResponseBuilder generateAccessToken(String scopeParam, ClientModel client, UserModel user, UserSessionModel session) {
- accessToken = createClientAccessToken(scopeParam, realm, client, user, session);
+ Set<RoleModel> requestedRoles = getAccess(scopeParam, client, user);
+ accessToken = createClientAccessToken(requestedRoles, realm, client, user, session);
return this;
}
diff --git a/services/src/main/java/org/keycloak/services/managers/UserManager.java b/services/src/main/java/org/keycloak/services/managers/UserManager.java
new file mode 100755
index 0000000..c6732b7
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/managers/UserManager.java
@@ -0,0 +1,30 @@
+package org.keycloak.services.managers;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionProvider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserManager {
+
+ private KeycloakSession session;
+
+ public UserManager(KeycloakSession session) {
+ this.session = session;
+ }
+
+ public boolean removeUser(RealmModel realm, UserModel user) {
+ if (session.users().removeUser(realm, user)) {
+ UserSessionProvider sessions = session.sessions();
+ if (sessions != null) {
+ sessions.onUserRemoved(realm, user);
+ }
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index aa34c27..57d229a 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -46,13 +46,14 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.OAuthRedirect;
@@ -131,7 +132,7 @@ public class AccountService {
this.realm = realm;
this.application = application;
this.audit = audit;
- this.authManager = new AppAuthManager(session);
+ this.authManager = new AppAuthManager();
}
public void init() {
@@ -140,11 +141,11 @@ public class AccountService {
account = session.getProvider(AccountProvider.class).setRealm(realm).setUriInfo(uriInfo);
boolean passwordUpdateSupported = false;
- AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(realm, uriInfo, headers);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, headers);
if (authResult != null) {
auth = new Auth(realm, authResult.getToken(), authResult.getUser(), application, true);
} else {
- authResult = authManager.authenticateBearerToken(realm, uriInfo, headers);
+ authResult = authManager.authenticateBearerToken(session, realm, uriInfo, headers);
if (authResult != null) {
auth = new Auth(realm, authResult.getToken(), authResult.getUser(), application, false);
}
@@ -220,7 +221,15 @@ public class AccountService {
} else if (types.contains(MediaType.APPLICATION_JSON_TYPE)) {
requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
- return Cors.add(request, Response.ok(ModelToRepresentation.toRepresentation(auth.getUser()))).auth().allowedOrigins(auth.getToken()).build();
+ UserRepresentation rep = ModelToRepresentation.toRepresentation(auth.getUser());
+ Iterator<String> itr = rep.getAttributes().keySet().iterator();
+ while (itr.hasNext()) {
+ if (itr.next().startsWith("keycloak.")) {
+ itr.remove();
+ }
+ }
+
+ return Cors.add(request, Response.ok(rep)).auth().allowedOrigins(auth.getToken()).build();
} else {
return Response.notAcceptable(Variant.VariantListBuilder.newInstance().mediaTypes(MediaType.TEXT_HTML_TYPE, MediaType.APPLICATION_JSON_TYPE).build()).build();
}
@@ -279,7 +288,7 @@ public class AccountService {
@GET
public Response sessionsPage() {
if (auth != null) {
- account.setSessions(realm.getUserSessions(auth.getUser()));
+ account.setSessions(session.sessions().getUserSessions(realm, auth.getUser()));
}
return forwardToPage("sessions", AccountPages.SESSIONS);
}
@@ -360,7 +369,7 @@ public class AccountService {
require(AccountRoles.MANAGE_ACCOUNT);
UserModel user = auth.getUser();
- realm.removeUserSessions(user);
+ session.sessions().removeUserSessions(realm, user);
return Response.seeOther(Urls.accountSessionsPage(uriInfo.getBaseUri(), realm.getName())).build();
}
@@ -507,14 +516,14 @@ public class AccountService {
return account.setError(Messages.SOCIAL_REDIRECT_ERROR).createResponse(AccountPages.SOCIAL);
}
case REMOVE:
- SocialLinkModel link = realm.getSocialLink(user, providerId);
+ SocialLinkModel link = session.users().getSocialLink(user, providerId, realm);
if (link != null) {
// Removing last social provider is not possible if you don't have other possibility to authenticate
- if (realm.getSocialLinks(user).size() > 1 || user.getAuthenticationLink() != null) {
- realm.removeSocialLink(user, providerId);
+ if (session.users().getSocialLinks(user, realm).size() > 1 || user.getAuthenticationLink() != null) {
+ session.users().removeSocialLink(realm, user, providerId);
- logger.debug("Social provider " + providerId + " removed successfully from user " + user.getLoginName());
+ logger.debug("Social provider " + providerId + " removed successfully from user " + user.getUsername());
audit.event(EventType.REMOVE_SOCIAL_LINK).client(auth.getClient()).user(auth.getUser())
.detail(Details.USERNAME, link.getSocialUserId() + "@" + link.getSocialProvider())
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
index 8976f2b..327c36e 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
@@ -78,7 +78,7 @@ public class AdminConsole {
public AdminConsole(RealmModel realm) {
this.realm = realm;
- this.authManager = new AppAuthManager(session);
+ this.authManager = new AppAuthManager();
}
public static class WhoAmI {
@@ -173,7 +173,7 @@ public class AdminConsole {
@NoCache
public Response whoAmI(final @Context HttpHeaders headers) {
RealmManager realmManager = new RealmManager(session);
- AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(realm, uriInfo, headers);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm, uriInfo, headers);
if (authResult == null) {
return Response.status(401).build();
}
@@ -185,7 +185,7 @@ public class AdminConsole {
displayName = displayName != null ? displayName + " " + user.getLastName() : user.getLastName();
}
} else {
- displayName = user.getLoginName();
+ displayName = user.getUsername();
}
RealmModel masterRealm = getAdminstrationRealm(realmManager);
@@ -224,7 +224,7 @@ public class AdminConsole {
}
private void addMasterRealmAccess(RealmModel masterRealm, UserModel user, Map<String, Set<String>> realmAdminAccess) {
- List<RealmModel> realms = session.getRealms();
+ List<RealmModel> realms = session.realms().getRealms();
for (RealmModel realm : realms) {
ApplicationModel realmAdminApp = realm.getMasterAdminApp();
Set<RoleModel> roles = realmAdminApp.getRoles();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
index deb3513..07c7525 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
@@ -55,7 +55,7 @@ public class AdminRoot {
public AdminRoot(TokenManager tokenManager) {
this.tokenManager = tokenManager;
- this.authManager = new AppAuthManager(null);
+ this.authManager = new AppAuthManager();
}
public static UriBuilder adminBaseUrl(UriInfo uriInfo) {
@@ -142,7 +142,7 @@ public class AdminRoot {
if (realm == null) {
throw new UnauthorizedException("Unknown realm in token");
}
- AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(realm, uriInfo, headers);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm, uriInfo, headers);
if (authResult == null) {
logger.debug("Token not valid");
throw new UnauthorizedException("Bearer");
@@ -182,7 +182,7 @@ public class AdminRoot {
AdminAuth auth = authenticateRealmAdminRequest(headers);
if (auth != null) {
- logger.info("authenticated admin access for: " + auth.getUser().getLoginName());
+ logger.info("authenticated admin access for: " + auth.getUser().getUsername());
}
Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index 9f4527b..4b2b39c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -10,13 +10,15 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.adapters.action.SessionStats;
import org.keycloak.representations.adapters.action.UserStats;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.managers.ApplicationManager;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.resources.KeycloakApplication;
@@ -97,7 +99,7 @@ public class ApplicationResource {
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
try {
- applicationManager.updateApplication(rep, application);
+ RepresentationToModel.updateApplication(rep, application);
return Response.noContent().build();
} catch (ModelDuplicateException e) {
return Flows.errors().exists("Application " + rep.getName() + " already exists");
@@ -116,8 +118,7 @@ public class ApplicationResource {
public ApplicationRepresentation getApplication() {
auth.requireView();
- ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
- return applicationManager.toRepresentation(application);
+ return ModelToRepresentation.toRepresentation(application);
}
@@ -167,7 +168,7 @@ public class ApplicationResource {
public void deleteApplication() {
auth.requireManage();
- realm.removeApplication(application.getId());
+ new ApplicationManager(new RealmManager(session)).removeApplication(realm, application);
}
@@ -184,7 +185,7 @@ public class ApplicationResource {
auth.requireManage();
logger.debug("regenerateSecret");
- UserCredentialModel cred = new ApplicationManager().generateSecret(application);
+ UserCredentialModel cred = KeycloakModelUtils.generateSecret(application);
CredentialRepresentation rep = ModelToRepresentation.toRepresentation(cred);
return rep;
}
@@ -301,7 +302,7 @@ public class ApplicationResource {
if (users) stats.setUsers(new HashMap<String, UserStats>());
return stats;
}
- SessionStats stats = new ResourceAdminManager().getSessionStats(uriInfo.getRequestUri(), realm, application, users);
+ SessionStats stats = new ResourceAdminManager().getSessionStats(uriInfo.getRequestUri(), session, realm, application, users);
if (stats == null) {
logger.info("app returned null stats");
} else {
@@ -327,7 +328,7 @@ public class ApplicationResource {
public Map<String, Integer> getApplicationSessionCount() {
auth.requireView();
Map<String, Integer> map = new HashMap<String, Integer>();
- map.put("count", application.getActiveUserSessions());
+ map.put("count", session.sessions().getActiveUserSessions(application.getRealm(), application));
return map;
}
@@ -340,11 +341,13 @@ public class ApplicationResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
- public List<UserSessionRepresentation> getUserSessions() {
+ public List<UserSessionRepresentation> getUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
auth.requireView();
+ firstResult = firstResult != null ? firstResult : -1;
+ maxResults = maxResults != null ? maxResults : -1;
List<UserSessionRepresentation> sessions = new ArrayList<UserSessionRepresentation>();
- for (UserSessionModel session : application.getUserSessions()) {
- UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
+ for (UserSessionModel userSession : session.sessions().getUserSessions(application.getRealm(), application, firstResult, maxResults)) {
+ UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession);
sessions.add(rep);
}
return sessions;
@@ -369,7 +372,7 @@ public class ApplicationResource {
@POST
public void logout(final @PathParam("username") String username) {
auth.requireManage();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java
index f83bfc7..610e772 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java
@@ -8,9 +8,9 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
-import org.keycloak.services.managers.ApplicationManager;
-import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.Consumes;
@@ -60,12 +60,11 @@ public class ApplicationsResource {
List<ApplicationRepresentation> rep = new ArrayList<ApplicationRepresentation>();
List<ApplicationModel> applicationModels = realm.getApplications();
- ApplicationManager resourceManager = new ApplicationManager(new RealmManager(session));
boolean view = auth.hasView();
for (ApplicationModel applicationModel : applicationModels) {
if (view) {
- rep.add(resourceManager.toRepresentation(applicationModel));
+ rep.add(ModelToRepresentation.toRepresentation(applicationModel));
} else {
ApplicationRepresentation app = new ApplicationRepresentation();
app.setName(applicationModel.getName());
@@ -87,9 +86,8 @@ public class ApplicationsResource {
public Response createApplication(final @Context UriInfo uriInfo, final ApplicationRepresentation rep) {
auth.requireManage();
- ApplicationManager resourceManager = new ApplicationManager(new RealmManager(session));
try {
- ApplicationModel applicationModel = resourceManager.createApplication(realm, rep);
+ ApplicationModel applicationModel = RepresentationToModel.createApplication(realm, rep, true);
return Response.created(uriInfo.getAbsolutePathBuilder().path(applicationModel.getName()).build()).build();
} catch (ModelDuplicateException e) {
return Flows.errors().exists("Application " + rep.getName() + " already exists");
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java
index 9a9ebaa..1b8c0d9 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java
@@ -1,9 +1,9 @@
package org.keycloak.services.resources.admin;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ClaimRepresentation;
-import org.keycloak.services.managers.ClaimManager;
-import org.keycloak.services.managers.ModelToRepresentation;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -47,6 +47,6 @@ public class ClaimResource {
@Consumes(MediaType.APPLICATION_JSON)
public void updateClaims(ClaimRepresentation rep) {
auth.requireManage();
- ClaimManager.setClaims(model, rep);
+ RepresentationToModel.setClaims(model, rep);
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
index ac318b6..84f1b88 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
@@ -8,10 +8,12 @@ import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.OAuthClientManager;
+import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.util.JsonSerialization;
@@ -81,9 +83,8 @@ public class OAuthClientResource {
public Response update(final OAuthClientRepresentation rep) {
auth.requireManage();
- OAuthClientManager manager = new OAuthClientManager(realm);
try {
- manager.update(rep, oauthClient);
+ RepresentationToModel.updateOAuthClient(rep, oauthClient);
return Response.noContent().build();
} catch (ModelDuplicateException e) {
return Flows.errors().exists("Client " + rep.getName() + " already exists");
@@ -101,7 +102,7 @@ public class OAuthClientResource {
public OAuthClientRepresentation getOAuthClient() {
auth.requireView();
- return OAuthClientManager.toRepresentation(oauthClient);
+ return ModelToRepresentation.toRepresentation(oauthClient);
}
/**
@@ -117,7 +118,7 @@ public class OAuthClientResource {
public String getInstallation() throws IOException {
auth.requireView();
- OAuthClientManager manager = new OAuthClientManager(realm);
+ OAuthClientManager manager = new OAuthClientManager();
Object rep = manager.toInstallationRepresentation(realm, oauthClient, getApplication().getBaseUri(uriInfo));
// TODO Temporary solution to pretty-print
@@ -133,7 +134,7 @@ public class OAuthClientResource {
public void deleteOAuthClient() {
auth.requireManage();
- realm.removeOAuthClient(oauthClient.getId());
+ new OAuthClientManager(new RealmManager(session)).removeClient(realm, oauthClient);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java
index f80a8c9..3bb4144 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java
@@ -8,8 +8,9 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.OAuthClientRepresentation;
-import org.keycloak.services.managers.OAuthClientManager;
import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.Consumes;
@@ -65,7 +66,7 @@ public class OAuthClientsResource {
boolean view = auth.hasView();
for (OAuthClientModel oauth : oauthModels) {
if (view) {
- rep.add(OAuthClientManager.toRepresentation(oauth));
+ rep.add(ModelToRepresentation.toRepresentation(oauth));
} else {
OAuthClientRepresentation client = new OAuthClientRepresentation();
client.setName(oauth.getClientId());
@@ -87,9 +88,8 @@ public class OAuthClientsResource {
public Response createOAuthClient(final @Context UriInfo uriInfo, final OAuthClientRepresentation rep) {
auth.requireManage();
- OAuthClientManager resourceManager = new OAuthClientManager(realm);
try {
- OAuthClientModel oauth = resourceManager.create(rep);
+ OAuthClientModel oauth = RepresentationToModel.createOAuthClient(rep, realm);
return Response.created(uriInfo.getAbsolutePathBuilder().path(oauth.getId()).build()).build();
} catch (ModelDuplicateException e) {
return Flows.errors().exists("Client " + rep.getName() + " already exists");
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 1009812..1a50ffa 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -13,11 +13,14 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.cache.CacheRealmProvider;
+import org.keycloak.models.cache.CacheUserProvider;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.adapters.action.SessionStats;
import org.keycloak.representations.idm.RealmAuditRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.LDAPConnectionTestManager;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
@@ -113,7 +116,16 @@ public class RealmAdminResource {
@Produces("application/json")
public RealmRepresentation getRealm() {
if (auth.hasView()) {
- return ModelToRepresentation.toRepresentation(realm);
+ RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm);
+ if (session.realms() instanceof CacheRealmProvider) {
+ CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
+ rep.setRealmCacheEnabled(cacheRealmProvider.isEnabled());
+ }
+ if (session.users() instanceof CacheUserProvider) {
+ CacheUserProvider cache = (CacheUserProvider)session.users();
+ rep.setUserCacheEnabled(cache.isEnabled());
+ }
+ return rep;
} else {
auth.requireAny();
@@ -138,7 +150,16 @@ public class RealmAdminResource {
logger.debug("updating realm: " + realm.getName());
try {
- new RealmManager(session).updateRealm(rep, realm);
+ RepresentationToModel.updateRealm(rep, realm);
+ if (rep.isRealmCacheEnabled() != null && session.realms() instanceof CacheRealmProvider) {
+ CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
+ cacheRealmProvider.setEnabled(rep.isRealmCacheEnabled());
+ }
+ if (rep.isUserCacheEnabled() != null && session.users() instanceof CacheUserProvider) {
+ CacheUserProvider cache = (CacheUserProvider)session.users();
+ cache.setEnabled(rep.isUserCacheEnabled());
+ }
+
return Response.noContent().build();
} catch (ModelDuplicateException e) {
return Flows.errors().exists("Realm " + rep.getRealm() + " already exists");
@@ -204,7 +225,7 @@ public class RealmAdminResource {
@POST
public void logoutAll() {
auth.requireManage();
- realm.removeUserSessions();
+ session.sessions().removeUserSessions(realm);
new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
}
@@ -217,10 +238,10 @@ public class RealmAdminResource {
@Path("sessions/{session}")
@DELETE
public void deleteSession(@PathParam("session") String sessionId) {
- UserSessionModel session = realm.getUserSession(sessionId);
- if (session == null) throw new NotFoundException("Sesssion not found");
- realm.removeUserSession(session);
- new ResourceAdminManager().logoutSession(uriInfo.getRequestUri(), realm, session.getId());
+ UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
+ if (userSession == null) throw new NotFoundException("Sesssion not found");
+ session.sessions().removeUserSession(realm, userSession);
+ new ResourceAdminManager().logoutSession(uriInfo.getRequestUri(), realm, userSession.getId());
}
/**
@@ -236,10 +257,10 @@ public class RealmAdminResource {
public Map<String, Integer> getApplicationSessionStats() {
auth.requireView();
Map<String, Integer> stats = new HashMap<String, Integer>();
- for (ApplicationModel applicationModel : realm.getApplications()) {
- int size = applicationModel.getActiveUserSessions();
+ for (ApplicationModel application : realm.getApplications()) {
+ int size = session.sessions().getActiveUserSessions(application.getRealm(), application);
if (size == 0) continue;
- stats.put(applicationModel.getName(), size);
+ stats.put(application.getName(), size);
}
return stats;
}
@@ -260,7 +281,7 @@ public class RealmAdminResource {
Map<String, SessionStats> stats = new HashMap<String, SessionStats>();
for (ApplicationModel applicationModel : realm.getApplications()) {
if (applicationModel.getManagementUrl() == null) continue;
- SessionStats appStats = new ResourceAdminManager().getSessionStats(uriInfo.getRequestUri(), realm, applicationModel, false);
+ SessionStats appStats = new ResourceAdminManager().getSessionStats(uriInfo.getRequestUri(), this.session, realm, applicationModel, false);
stats.put(applicationModel.getName(), appStats);
}
return stats;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
index 89d9ef3..e2ef922 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
@@ -3,9 +3,6 @@ package org.keycloak.services.resources.admin;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel;
import org.keycloak.services.ForbiddenException;
-import org.keycloak.services.managers.Auth;
-
-import javax.ws.rs.WebApplicationException;
/**
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index 7600c87..ab703ac 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -6,17 +6,15 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
-import org.jboss.resteasy.util.GenericType;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.ForbiddenException;
-import org.keycloak.services.managers.Auth;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.KeycloakApplication;
@@ -87,7 +85,7 @@ public class RealmsAdminResource {
RealmManager realmManager = new RealmManager(session);
List<RealmRepresentation> reps = new ArrayList<RealmRepresentation>();
if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
- List<RealmModel> realms = session.getRealms();
+ List<RealmModel> realms = session.realms().getRealms();
for (RealmModel realm : realms) {
addRealmRep(reps, realm, realm.getMasterAdminApp());
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
index d1bb149..a4b6f58 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
@@ -2,13 +2,12 @@ package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
-import org.keycloak.models.Constants;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.Consumes;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
index 8ef17d8..52bfae9 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
@@ -4,8 +4,8 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.services.managers.ModelToRepresentation;
import java.util.Collections;
import java.util.HashSet;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
index 92b129c..9da9085 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
@@ -7,12 +7,10 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ApplicationMappingsRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.services.managers.ModelToRepresentation;
-import org.keycloak.services.managers.RealmManager;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
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 788cc56..8c1cffa 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
@@ -17,6 +17,8 @@ import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.adapters.action.UserStats;
import org.keycloak.representations.idm.ApplicationMappingsRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -26,13 +28,12 @@ import org.keycloak.representations.idm.SocialLinkRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.managers.AccessCodeEntry;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
+import org.keycloak.services.managers.UserManager;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls;
-import org.keycloak.util.Time;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -99,7 +100,7 @@ public class UsersResource {
auth.requireManage();
try {
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -124,10 +125,10 @@ public class UsersResource {
auth.requireManage();
try {
- UserModel user = realm.addUser(rep.getUsername());
+ UserModel user = session.users().addUser(realm, rep.getUsername());
updateUserFromRep(user, rep);
- return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getLoginName()).build()).build();
+ return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getUsername()).build()).build();
} catch (ModelDuplicateException e) {
return Flows.errors().exists("User exists with same username or email");
}
@@ -174,7 +175,7 @@ public class UsersResource {
public UserRepresentation getUser(final @PathParam("username") String username) {
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -197,7 +198,7 @@ public class UsersResource {
public Map<String, UserStats> getSessionStats(final @PathParam("username") String username) {
logger.info("session-stats");
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -224,11 +225,11 @@ public class UsersResource {
public List<UserSessionRepresentation> getSessions(final @PathParam("username") String username) {
logger.info("sessions");
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
- List<UserSessionModel> sessions = realm.getUserSessions(user);
+ List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
List<UserSessionRepresentation> reps = new ArrayList<UserSessionRepresentation>();
for (UserSessionModel session : sessions) {
UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
@@ -249,11 +250,11 @@ public class UsersResource {
@Produces(MediaType.APPLICATION_JSON)
public List<SocialLinkRepresentation> getSocialLinks(final @PathParam("username") String username) {
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
- Set<SocialLinkModel> socialLinks = realm.getSocialLinks(user);
+ Set<SocialLinkModel> socialLinks = session.users().getSocialLinks(user, realm);
List<SocialLinkRepresentation> result = new ArrayList<SocialLinkRepresentation>();
for (SocialLinkModel socialLink : socialLinks) {
SocialLinkRepresentation rep = ModelToRepresentation.toRepresentation(socialLink);
@@ -272,13 +273,11 @@ public class UsersResource {
@POST
public void logout(final @PathParam("username") String username) {
auth.requireManage();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
- realm.removeUserSessions(user);
- // set notBefore so that user will be forced to log in.
- user.setNotBefore(Time.currentTime());
+ session.sessions().removeUserSessions(realm, user);
new ResourceAdminManager().logoutUser(uriInfo.getRequestUri(), realm, user.getId(), null);
}
@@ -293,7 +292,12 @@ public class UsersResource {
public void deleteUser(final @PathParam("username") String username) {
auth.requireManage();
- realm.removeUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
+ if (user == null) {
+ throw new NotFoundException("User not found");
+ }
+
+ new UserManager(session).removeUser(realm, user);
}
/**
@@ -313,14 +317,18 @@ public class UsersResource {
@QueryParam("lastName") String last,
@QueryParam("firstName") String first,
@QueryParam("email") String email,
- @QueryParam("username") String username) {
+ @QueryParam("username") String username,
+ @QueryParam("first") Integer firstResult,
+ @QueryParam("max") Integer maxResults) {
auth.requireView();
- RealmManager manager = new RealmManager(session);
+ firstResult = firstResult != null ? firstResult : -1;
+ maxResults = maxResults != null ? maxResults : -1;
+
List<UserRepresentation> results = new ArrayList<UserRepresentation>();
List<UserModel> userModels;
if (search != null) {
- userModels = manager.searchUsers(search, realm);
+ userModels = session.users().searchForUser(search.trim(), realm, firstResult, maxResults);
} else if (last != null || first != null || email != null || username != null) {
Map<String, String> attributes = new HashMap<String, String>();
if (last != null) {
@@ -333,14 +341,14 @@ public class UsersResource {
attributes.put(UserModel.EMAIL, email);
}
if (username != null) {
- attributes.put(UserModel.LOGIN_NAME, username);
+ attributes.put(UserModel.USERNAME, username);
}
- userModels = realm.searchForUserByAttributes(attributes);
+ userModels = session.users().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
for (UserModel user : userModels) {
results.add(ModelToRepresentation.toRepresentation(user));
}
} else {
- userModels = realm.getUsers();
+ userModels = session.users().getUsers(realm, firstResult, maxResults);
}
for (UserModel user : userModels) {
@@ -362,7 +370,7 @@ public class UsersResource {
public MappingsRepresentation getRoleMappings(@PathParam("username") String username) {
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -413,7 +421,7 @@ public class UsersResource {
public List<RoleRepresentation> getRealmRoleMappings(@PathParam("username") String username) {
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -439,7 +447,7 @@ public class UsersResource {
public List<RoleRepresentation> getCompositeRealmRoleMappings(@PathParam("username") String username) {
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -467,7 +475,7 @@ public class UsersResource {
public List<RoleRepresentation> getAvailableRealmRoleMappings(@PathParam("username") String username) {
auth.requireView();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -489,7 +497,7 @@ public class UsersResource {
auth.requireManage();
logger.debugv("** addRealmRoleMappings: {0}", roles);
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -518,7 +526,7 @@ public class UsersResource {
auth.requireManage();
logger.debug("deleteRealmRoleMappings");
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -556,7 +564,7 @@ public class UsersResource {
logger.debug("getApplicationRoleMappings");
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -592,7 +600,7 @@ public class UsersResource {
logger.debug("getCompositeApplicationRoleMappings");
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -628,7 +636,7 @@ public class UsersResource {
logger.debug("getApplicationRoleMappings");
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -670,7 +678,7 @@ public class UsersResource {
auth.requireManage();
logger.debug("addApplicationRoleMapping");
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -704,7 +712,7 @@ public class UsersResource {
public void deleteApplicationRoleMapping(@PathParam("username") String username, @PathParam("app") String appName, List<RoleRepresentation> roles) {
auth.requireManage();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -749,7 +757,7 @@ public class UsersResource {
public void resetPassword(@PathParam("username") String username, CredentialRepresentation pass) {
auth.requireManage();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -757,7 +765,7 @@ public class UsersResource {
throw new BadRequestException("No password provided");
}
- UserCredentialModel cred = RealmManager.fromRepresentation(pass);
+ UserCredentialModel cred = RepresentationToModel.convertCredential(pass);
user.updateCredential(cred);
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
}
@@ -773,7 +781,7 @@ public class UsersResource {
public void removeTotp(@PathParam("username") String username) {
auth.requireManage();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -793,7 +801,7 @@ public class UsersResource {
public Response resetPasswordEmail(@PathParam("username") String username) {
auth.requireManage();
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
@@ -812,13 +820,8 @@ public class UsersResource {
return Flows.errors().error("AccountProvider management not enabled", Response.Status.INTERNAL_SERVER_ERROR);
}
- Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>(user.getRequiredActions());
- requiredActions.add(UserModel.RequiredAction.UPDATE_PASSWORD);
-
- AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user, null);
- accessCode.setRequiredActions(requiredActions);
- accessCode.setUsernameUsed(username);
- accessCode.resetExpiration();
+ AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, session, realm, client, user, null);
+ accessCode.setRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
try {
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java
index c77b001..7689ea0 100755
--- a/services/src/main/java/org/keycloak/services/resources/Cors.java
+++ b/services/src/main/java/org/keycloak/services/resources/Cors.java
@@ -1,13 +1,5 @@
package org.keycloak.services.resources;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.ResponseBuilder;
-
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
@@ -15,6 +7,13 @@ import org.keycloak.models.ClientModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.util.CollectionUtil;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index c44f39d..b73c0fc 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -37,22 +37,19 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessCode;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.TokenManager;
-import org.keycloak.services.util.CookieHelper;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -85,12 +82,7 @@ public class OAuthFlows {
this.tokenManager = tokenManager;
}
- public Response redirectAccessCode(AccessCodeEntry accessCode, UserSessionModel session, String state, String redirect) {
- return redirectAccessCode(accessCode, session, state, redirect, false);
- }
-
-
- public Response redirectAccessCode(AccessCodeEntry accessCode, UserSessionModel session, String state, String redirect, boolean rememberMe) {
+ public Response redirectAccessCode(AccessCodeEntry accessCode, UserSessionModel userSession, String state, String redirect) {
String code = accessCode.getCode();
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
log.debugv("redirectAccessCode: state: {0}", state);
@@ -98,23 +90,22 @@ public class OAuthFlows {
redirectUri.queryParam(OAuth2Constants.STATE, state);
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
- rememberMe = rememberMe || remember != null;
Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE);
if (sessionCookie != null) {
String oldSessionId = sessionCookie.getValue().split("/")[2];
- if (!oldSessionId.equals(session.getId())) {
- UserSessionModel oldSession = realm.getUserSession(oldSessionId);
+ if (!oldSessionId.equals(userSession.getId())) {
+ UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId);
if (oldSession != null) {
log.debugv("Removing old user session: session: {0}", oldSessionId);
- realm.removeUserSession(oldSession);
+ session.sessions().removeUserSession(realm, oldSession);
}
}
}
// refresh the cookies!
- authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe);
- if (rememberMe) authManager.createRememberMeCookie(realm, uriInfo);
+ authManager.createLoginCookie(realm, accessCode.getUser(), userSession, uriInfo);
+ if (userSession.isRememberMe()) authManager.createRememberMeCookie(realm, uriInfo);
return location.build();
}
@@ -126,15 +117,12 @@ public class OAuthFlows {
return Response.status(302).location(redirectUri.build()).build();
}
- public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, String username, boolean rememberMe, String authMethod, Audit audit) {
+ public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, Audit audit) {
isTotpConfigurationRequired(user);
isEmailVerificationRequired(user);
boolean isResource = client instanceof ApplicationModel;
- AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user, session);
- accessCode.setRememberMe(rememberMe);
- accessCode.setAuthMethod(authMethod);
- accessCode.setUsernameUsed(username);
+ AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, this.session, realm, client, user, session);
log.debugv("processAccessCode: isResource: {0}", isResource);
log.debugv("processAccessCode: go to oauth page?: {0}",
@@ -144,10 +132,9 @@ public class OAuthFlows {
Set<RequiredAction> requiredActions = user.getRequiredActions();
if (!requiredActions.isEmpty()) {
- accessCode.setRequiredActions(new HashSet<UserModel.RequiredAction>(requiredActions));
- accessCode.resetExpiration();
-
RequiredAction action = user.getRequiredActions().iterator().next();
+ accessCode.setRequiredAction(action);
+
if (action.equals(RequiredAction.VERIFY_EMAIL)) {
audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
}
@@ -157,38 +144,28 @@ public class OAuthFlows {
}
if (!isResource) {
- accessCode.resetExpiration();
- List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
- MultivaluedMap<String, RoleModel> appRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
- if (accessCode.getToken().getRealmAccess() != null) {
- if (accessCode.getToken().getRealmAccess().getRoles() != null) {
- for (String role : accessCode.getToken().getRealmAccess().getRoles()) {
- RoleModel roleModel = realm.getRole(role);
- if (roleModel != null) realmRolesRequested.add(roleModel);
- }
- }
- }
- if (accessCode.getToken().getResourceAccess().size() > 0) {
- for (Map.Entry<String, AccessToken.Access> entry : accessCode.getToken().getResourceAccess().entrySet()) {
- ApplicationModel app = realm.getApplicationByName(entry.getKey());
- if (app == null) continue;
- if (entry.getValue().getRoles() != null) {
- for (String role : entry.getValue().getRoles()) {
- RoleModel roleModel = app.getRole(role);
- if (roleModel != null) appRolesRequested.add(entry.getKey(), roleModel);
- }
-
- }
+ accessCode.setAction(AccessCode.Action.OAUTH_GRANT);
+
+ List<RoleModel> realmRoles = new LinkedList<RoleModel>();
+ MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<String, RoleModel>();
+ for (RoleModel r : accessCode.getRequestedRoles()) {
+ if (r.getContainer() instanceof RealmModel) {
+ realmRoles.add(r);
+ } else {
+ resourceRoles.add(((ApplicationModel) r.getContainer()).getName(), r);
}
}
- return Flows.forms(this.session, realm, uriInfo).setAccessCode(accessCode.getCode()).
- setAccessRequest(realmRolesRequested, appRolesRequested).
- setClient(client).createOAuthGrant();
+
+ return Flows.forms(this.session, realm, uriInfo)
+ .setAccessCode(accessCode.getCode())
+ .setAccessRequest(realmRoles, resourceRoles)
+ .setClient(client)
+ .createOAuthGrant();
}
if (redirect != null) {
audit.success();
- return redirectAccessCode(accessCode, session, state, redirect, rememberMe);
+ return redirectAccessCode(accessCode, session, state, redirect);
} else {
return null;
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java
index 37c0922..5091bc3 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java
@@ -3,18 +3,15 @@ package org.keycloak.services.resources.flows;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
-import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.SocialResource;
import org.keycloak.services.util.CookieHelper;
import org.keycloak.social.AuthRequest;
-import org.keycloak.social.UriBuilder;
import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import java.net.URI;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
index fa311fa..539674b 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
@@ -22,10 +22,10 @@
package org.keycloak.services.resources.flows;
import org.keycloak.services.resources.AccountService;
-import org.keycloak.services.resources.ThemeResource;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.RequiredActionsService;
import org.keycloak.services.resources.SocialResource;
+import org.keycloak.services.resources.ThemeResource;
import org.keycloak.services.resources.TokenService;
import javax.ws.rs.core.UriBuilder;
diff --git a/services/src/main/java/org/keycloak/services/resources/JsResource.java b/services/src/main/java/org/keycloak/services/resources/JsResource.java
index 62349db..ce012e7 100755
--- a/services/src/main/java/org/keycloak/services/resources/JsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/JsResource.java
@@ -31,4 +31,16 @@ public class JsResource {
}
}
+ @GET
+ @Path("/keycloak.min.js")
+ @Produces("text/javascript")
+ public Response getMinJs() {
+ InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.min.js");
+ if (inputStream != null) {
+ return Response.ok(inputStream).build();
+ } else {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index c868e3f..f4094af 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -7,7 +7,7 @@ import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
import org.keycloak.SkeletonKeyContextResolver;
-import org.keycloak.exportimport.ExportImportProvider;
+import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
@@ -24,7 +24,6 @@ import org.keycloak.services.scheduled.ScheduledTaskRunner;
import org.keycloak.services.util.JsonConfigProvider;
import org.keycloak.timer.TimerProvider;
import org.keycloak.util.JsonSerialization;
-import org.keycloak.util.ProviderLoader;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
@@ -38,7 +37,6 @@ import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
@@ -81,7 +79,7 @@ public class KeycloakApplication extends Application {
classes.add(JsResource.class);
classes.add(WelcomeResource.class);
- checkExportImportProvider();
+ new ExportImportManager().checkExportImport(this.sessionFactory);
setupDefaultRealm(context.getContextPath());
@@ -235,16 +233,4 @@ public class KeycloakApplication extends Application {
}
}
- protected void checkExportImportProvider() {
- Iterator<ExportImportProvider> providers = ProviderLoader.load(ExportImportProvider.class).iterator();
-
- if (providers.hasNext()) {
- ExportImportProvider exportImport = providers.next();
- exportImport.checkExportImport(sessionFactory);
- } else {
- log.warn("No ExportImportProvider found!");
- }
- }
-
-
}
diff --git a/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java b/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java
index 097b25a..565303c 100755
--- a/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java
@@ -7,7 +7,6 @@ import org.keycloak.representations.idm.PublishedRealmRepresentation;
import org.keycloak.services.resources.admin.AdminRoot;
import javax.ws.rs.GET;
-import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index 1e15cf4..6fafca4 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -94,7 +94,7 @@ public class RealmsResource {
@QueryParam("client_id") String client_id,
@QueryParam("origin") String origin) {
logger.info("getLoginStatusIframe");
- AuthenticationManager auth = new AuthenticationManager(session);
+ AuthenticationManager auth = new AuthenticationManager();
//logger.info("getting login-status-iframe.html for client_id: " + client_id);
RealmManager realmManager = new RealmManager(session);
@@ -103,10 +103,6 @@ public class RealmsResource {
if (client == null) {
throw new NotFoundException("could not find client: " + client_id);
}
- AuthenticationManager.AuthResult result = auth.authenticateIdentityCookie(realm, uriInfo, headers);
- if (result == null) {
- throw new UnauthorizedException("not logged in, can't get page");
- }
InputStream is = getClass().getClassLoader().getResourceAsStream("login-status-iframe.html");
if (is == null) throw new NotFoundException("Could not find login-status-iframe.html ");
@@ -148,7 +144,7 @@ public class RealmsResource {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = locateRealm(name, realmManager);
Audit audit = new AuditManager(realm, session, clientConnection).createAudit();
- AuthenticationManager authManager = new AuthenticationManager(session, protector);
+ AuthenticationManager authManager = new AuthenticationManager(protector);
TokenService tokenService = new TokenService(realm, tokenManager, audit, authManager);
ResteasyProviderFactory.getInstance().injectProperties(tokenService);
//resourceContext.initResource(tokenService);
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index 269fa51..60988ca 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -28,6 +28,8 @@ import org.keycloak.audit.Audit;
import org.keycloak.audit.Details;
import org.keycloak.audit.Errors;
import org.keycloak.audit.EventType;
+import org.keycloak.authentication.AuthenticationProviderException;
+import org.keycloak.authentication.AuthenticationProviderManager;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.login.LoginFormsProvider;
@@ -48,8 +50,6 @@ import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.services.validation.Validation;
-import org.keycloak.authentication.AuthenticationProviderException;
-import org.keycloak.authentication.AuthenticationProviderManager;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -63,7 +63,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
-import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -131,7 +130,6 @@ public class RequiredActionsService {
user.setEmail(email);
user.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
- accessCode.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
audit.clone().event(EventType.UPDATE_PROFILE).success();
if (emailChanged) {
@@ -173,7 +171,6 @@ public class RequiredActionsService {
user.setTotp(true);
user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
- accessCode.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
audit.clone().event(EventType.UPDATE_TOTP).success();
@@ -218,20 +215,15 @@ public class RequiredActionsService {
logger.debug("updatePassword updated credential");
user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
- if (accessCode != null) {
- accessCode.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
- }
audit.clone().event(EventType.UPDATE_PASSWORD).success();
- // Password reset through email won't have an associated session
+ // Redirect to account management to login if password reset was initiated by admin
if (accessCode.getSessionState() == null) {
- UserSessionModel userSession = realm.createUserSession(realm.getUserById(accessCode.getUser().getId()), clientConnection.getRemoteAddr());
- accessCode.getToken().setSessionState(userSession.getId());
- audit.session(userSession);
+ return Response.seeOther(Urls.accountPage(uriInfo.getBaseUri(), realm.getId())).build();
+ } else {
+ return redirectOauth(user, accessCode);
}
-
- return redirectOauth(user, accessCode);
}
@@ -239,9 +231,8 @@ public class RequiredActionsService {
@GET
public Response emailVerification() {
if (uriInfo.getQueryParameters().containsKey("key")) {
- AccessCodeEntry accessCode = tokenManager.parseCode(uriInfo.getQueryParameters().getFirst("key"), realm);
- if (accessCode == null || accessCode.isExpired()
- || !accessCode.hasRequiredAction(RequiredAction.VERIFY_EMAIL)) {
+ AccessCodeEntry accessCode = tokenManager.parseCode(uriInfo.getQueryParameters().getFirst("key"), session, realm);
+ if (accessCode == null || accessCode.isExpired() || !RequiredAction.VERIFY_EMAIL.equals(accessCode.getRequiredAction())) {
return unauthorized();
}
@@ -252,7 +243,6 @@ public class RequiredActionsService {
user.setEmailVerified(true);
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
- accessCode.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
audit.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
@@ -275,10 +265,8 @@ public class RequiredActionsService {
@GET
public Response passwordReset() {
if (uriInfo.getQueryParameters().containsKey("key")) {
- AccessCodeEntry accessCode = tokenManager.parseCode(uriInfo.getQueryParameters().getFirst("key"), realm);
- accessCode.setAuthMethod("form");
- if (accessCode == null || accessCode.isExpired()
- || !accessCode.hasRequiredAction(RequiredAction.UPDATE_PASSWORD)) {
+ AccessCodeEntry accessCode = tokenManager.parseCode(uriInfo.getQueryParameters().getFirst("key"), session, realm);
+ if (accessCode == null || accessCode.isExpired() || !RequiredAction.UPDATE_PASSWORD.equals(accessCode.getRequiredAction())) {
return unauthorized();
}
@@ -299,7 +287,7 @@ public class RequiredActionsService {
String redirect = uriInfo.getQueryParameters().getFirst(OAuth2Constants.REDIRECT_URI);
String clientId = uriInfo.getQueryParameters().getFirst(OAuth2Constants.CLIENT_ID);
- AuthenticationManager authManager = new AuthenticationManager(session);
+ AuthenticationManager authManager = new AuthenticationManager();
ClientModel client = realm.findClient(clientId);
if (client == null) {
@@ -317,22 +305,20 @@ public class RequiredActionsService {
.detail(Details.AUTH_METHOD, "form")
.detail(Details.USERNAME, username);
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user == null && username.contains("@")) {
- user = realm.getUserByEmail(username);
+ user = session.users().getUserByEmail(username, realm);
}
if (user == null) {
logger.warn("Failed to send password reset email: user not found");
audit.error(Errors.USER_NOT_FOUND);
} else {
- Set<RequiredAction> requiredActions = new HashSet<RequiredAction>(user.getRequiredActions());
- requiredActions.add(RequiredAction.UPDATE_PASSWORD);
+ UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false);
+ audit.session(userSession);
- AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user, null);
- accessCode.setRequiredActions(requiredActions);
- accessCode.setAuthMethod("form");
- accessCode.setUsernameUsed(username);
+ AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, session, realm, client, user, userSession);
+ accessCode.setRequiredAction(RequiredAction.UPDATE_PASSWORD);
try {
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
@@ -360,7 +346,7 @@ public class RequiredActionsService {
return null;
}
- AccessCodeEntry accessCodeEntry = tokenManager.parseCode(code, realm);
+ AccessCodeEntry accessCodeEntry = tokenManager.parseCode(code, session, realm);
if (accessCodeEntry == null) {
logger.debug("getAccessCodeEntry access code entry null");
return null;
@@ -372,8 +358,8 @@ public class RequiredActionsService {
return null;
}
- if (accessCodeEntry.getRequiredActions() == null || !accessCodeEntry.getRequiredActions().contains(requiredAction)) {
- logger.debugv("getAccessCodeEntry required actions null || entry does not contain required action: {0}|{1}", (accessCodeEntry.getRequiredActions() == null),!accessCodeEntry.getRequiredActions().contains(requiredAction) );
+ if (!requiredAction.equals(accessCodeEntry.getRequiredAction())) {
+ logger.debugv("Invalid access code action: {0}", requiredAction);
return null;
}
@@ -381,7 +367,7 @@ public class RequiredActionsService {
}
private UserModel getUser(AccessCodeEntry accessCode) {
- return realm.getUser(accessCode.getUser().getLoginName());
+ return session.users().getUserByUsername(accessCode.getUser().getUsername(), realm);
}
private Response redirectOauth(UserModel user, AccessCodeEntry accessCode) {
@@ -391,25 +377,26 @@ public class RequiredActionsService {
Set<RequiredAction> requiredActions = user.getRequiredActions();
if (!requiredActions.isEmpty()) {
+ accessCode.setRequiredAction(requiredActions.iterator().next());
return Flows.forms(session, realm, uriInfo).setAccessCode(accessCode.getCode()).setUser(user)
.createResponse(requiredActions.iterator().next());
} else {
logger.debugv("redirectOauth: redirecting to: {0}", accessCode.getRedirectUri());
- accessCode.resetExpiration();
+ accessCode.setAction(null);
- AuthenticationManager authManager = new AuthenticationManager(session);
+ AuthenticationManager authManager = new AuthenticationManager();
- UserSessionModel session = realm.getUserSession(accessCode.getSessionState());
- if (!AuthenticationManager.isSessionValid(realm, session)) {
- AuthenticationManager.logout(realm, session, uriInfo);
+ UserSessionModel userSession = session.sessions().getUserSession(realm, accessCode.getSessionState());
+ if (!AuthenticationManager.isSessionValid(realm, userSession)) {
+ AuthenticationManager.logout(session, realm, userSession, uriInfo);
return Flows.oauth(this.session, realm, request, uriInfo, authManager, tokenManager).redirectError(accessCode.getClient(), "access_denied", accessCode.getState(), accessCode.getRedirectUri());
}
- audit.session(session);
+ audit.session(userSession);
audit.success();
return Flows.oauth(this.session, realm, request, uriInfo, authManager, tokenManager).redirectAccessCode(accessCode,
- session, accessCode.getState(), accessCode.getRedirectUri());
+ userSession, accessCode.getState(), accessCode.getRedirectUri());
}
}
@@ -419,12 +406,16 @@ public class RequiredActionsService {
.session(accessCode.getSessionState())
.detail(Details.CODE_ID, accessCode.getCodeId())
.detail(Details.REDIRECT_URI, accessCode.getRedirectUri())
- .detail(Details.RESPONSE_TYPE, "code")
- .detail(Details.AUTH_METHOD, accessCode.getAuthMethod())
- .detail(Details.USERNAME, accessCode.getUsernameUsed());
+ .detail(Details.RESPONSE_TYPE, "code");
+
+ UserSessionModel userSession = accessCode.getSessionState() != null ? session.sessions().getUserSession(realm, accessCode.getSessionState()) : null;
- if (accessCode.isRememberMe()) {
- audit.detail(Details.REMEMBER_ME, "true");
+ if (userSession != null) {
+ audit.detail(Details.AUTH_METHOD, userSession.getAuthMethod());
+ audit.detail(Details.USERNAME, userSession.getLoginUsername());
+ if (userSession.isRememberMe()) {
+ audit.detail(Details.REMEMBER_ME, "true");
+ }
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index c59c8f6..d1691c1 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -116,6 +116,7 @@ public class SocialResource {
SocialProvider provider = SocialLoader.load(initialRequest.getProvider());
String realmName = initialRequest.getRealm();
+ String authMethod = "social@" + provider.getId();
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.getRealmByName(realmName);
@@ -123,9 +124,9 @@ public class SocialResource {
Audit audit = new AuditManager(realm, session, clientConnection).createAudit()
.event(EventType.LOGIN)
.detail(Details.RESPONSE_TYPE, initialRequest.get(OAuth2Constants.RESPONSE_TYPE))
- .detail(Details.AUTH_METHOD, "social@" + provider.getId());
+ .detail(Details.AUTH_METHOD, authMethod);
- AuthenticationManager authManager = new AuthenticationManager(session);
+ AuthenticationManager authManager = new AuthenticationManager();
OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, authManager, tokenManager);
if (!realm.isEnabled()) {
@@ -182,12 +183,12 @@ public class SocialResource {
audit.detail(Details.USERNAME, socialUser.getId() + "@" + provider.getId());
SocialLinkModel socialLink = new SocialLinkModel(provider.getId(), socialUser.getId(), socialUser.getUsername());
- UserModel user = realm.getUserBySocialLink(socialLink);
+ UserModel user = session.users().getUserBySocialLink(socialLink, realm);
// Check if user is already authenticated (this means linking social into existing user account)
String userId = initialRequest.getUser();
if (userId != null) {
- UserModel authenticatedUser = realm.getUserById(userId);
+ UserModel authenticatedUser = session.users().getUserById(userId, realm);
audit.event(EventType.SOCIAL_LINK).user(userId);
@@ -211,8 +212,8 @@ public class SocialResource {
return oauth.forwardToSecurityFailure("Unknown redirectUri");
}
- realm.addSocialLink(authenticatedUser, socialLink);
- logger.debug("Social provider " + provider.getId() + " linked with user " + authenticatedUser.getLoginName());
+ session.users().addSocialLink(realm, authenticatedUser, socialLink);
+ logger.debug("Social provider " + provider.getId() + " linked with user " + authenticatedUser.getUsername());
audit.success();
return Response.status(302).location(UriBuilder.fromUri(redirectUri).build()).build();
@@ -225,7 +226,7 @@ public class SocialResource {
return oauth.forwardToSecurityFailure("Registration not allowed");
}
- user = realm.addUser(KeycloakModelUtils.generateId());
+ user = session.users().addUser(realm, KeycloakModelUtils.generateId());
user.setEnabled(true);
user.setFirstName(socialUser.getFirstName());
user.setLastName(socialUser.getLastName());
@@ -235,7 +236,7 @@ public class SocialResource {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
}
- realm.addSocialLink(user, socialLink);
+ session.users().addSocialLink(realm, user, socialLink);
audit.clone().user(user).event(EventType.REGISTER)
.detail(Details.REGISTER_METHOD, "social@" + provider.getId())
@@ -251,10 +252,12 @@ public class SocialResource {
return oauth.forwardToSecurityFailure("Your account is not enabled.");
}
- UserSessionModel session = realm.createUserSession(user, clientConnection.getRemoteAddr());
- audit.session(session);
+ String username = socialLink.getSocialUserId() + "@" + socialLink.getSocialProvider();
- return oauth.processAccessCode(scope, state, redirectUri, client, user, session, socialLink.getSocialUserId() + "@" + socialLink.getSocialProvider(), false, "social@" + provider.getId(), audit);
+ UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), authMethod, false);
+ audit.session(userSession);
+
+ return oauth.processAccessCode(scope, state, redirectUri, client, user, userSession, audit);
}
@GET
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 93949d1..917dd2d 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -21,13 +21,14 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.AccessCode;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -99,8 +100,6 @@ public class TokenService {
@Context
protected KeycloakSession session;
@Context
- protected KeycloakTransaction transaction;
- @Context
protected ClientConnection clientConnection;
/*
@@ -234,7 +233,7 @@ public class TokenService {
}
audit.detail(Details.USERNAME, username);
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
if (user != null) audit.user(user);
ClientModel client = authorizeClient(authorizationHeader, form, audit);
@@ -249,7 +248,7 @@ public class TokenService {
return createError("realm_disabled", "Realm is disabled", Response.Status.UNAUTHORIZED);
}
- AuthenticationStatus authenticationStatus = authManager.authenticateForm(clientConnection, realm, form);
+ AuthenticationStatus authenticationStatus = authManager.authenticateForm(session, clientConnection, realm, form);
Map<String, String> err;
switch (authenticationStatus) {
@@ -281,12 +280,12 @@ public class TokenService {
String scope = form.getFirst(OAuth2Constants.SCOPE);
- UserSessionModel session = realm.createUserSession(user, clientConnection.getRemoteAddr());
- session.associateClient(client);
- audit.session(session);
+ UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "oauth_credentials", false);
+ userSession.associateClient(client);
+ audit.session(userSession);
AccessTokenResponse res = tokenManager.responseBuilder(realm, client, audit)
- .generateAccessToken(scope, client, user, session)
+ .generateAccessToken(scope, client, user, userSession)
.generateRefreshToken()
.generateIDToken()
.build();
@@ -327,7 +326,7 @@ public class TokenService {
String refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
AccessToken accessToken;
try {
- accessToken = tokenManager.refreshAccessToken(uriInfo, realm, client, refreshToken, audit);
+ accessToken = tokenManager.refreshAccessToken(session, uriInfo, realm, client, refreshToken, audit);
} catch (OAuthErrorException e) {
Map<String, String> error = new HashMap<String, String>();
error.put(OAuth2Constants.ERROR, e.getError());
@@ -412,7 +411,7 @@ public class TokenService {
return oauth.redirectError(client, "access_denied", state, redirect);
}
- AuthenticationStatus status = authManager.authenticateForm(clientConnection, realm, formData);
+ AuthenticationStatus status = authManager.authenticateForm(session, clientConnection, realm, formData);
if (remember) {
authManager.createRememberMeCookie(realm, uriInfo);
@@ -420,7 +419,7 @@ public class TokenService {
authManager.expireRememberMeCookie(realm, uriInfo);
}
- UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
+ UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
if (user != null) {
audit.user(user);
}
@@ -428,10 +427,10 @@ public class TokenService {
switch (status) {
case SUCCESS:
case ACTIONS_REQUIRED:
- UserSessionModel session = realm.createUserSession(user, clientConnection.getRemoteAddr());
- audit.session(session);
+ UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", remember);
+ audit.session(userSession);
- return oauth.processAccessCode(scopeParam, state, redirect, client, user, session, username, remember, "form", audit);
+ return oauth.processAccessCode(scopeParam, state, redirect, client, user, userSession, audit);
case ACCOUNT_TEMPORARILY_DISABLED:
audit.error(Errors.USER_TEMPORARILY_DISABLED);
return Flows.forms(this.session, realm, uriInfo).setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).setFormData(formData).createLogin();
@@ -536,12 +535,12 @@ public class TokenService {
AuthenticationProviderManager authenticationProviderManager = AuthenticationProviderManager.getManager(realm, session);
// Validate that user with this username doesn't exist in realm or any authentication provider
- if (realm.getUser(username) != null || authenticationProviderManager.getUser(username) != null) {
+ if (session.users().getUserByUsername(username, realm) != null || authenticationProviderManager.getUser(username) != null) {
audit.error(Errors.USERNAME_IN_USE);
return Flows.forms(session, realm, uriInfo).setError(Messages.USERNAME_EXISTS).setFormData(formData).createRegistration();
}
- UserModel user = realm.addUser(username);
+ UserModel user = session.users().addUser(realm, username);
user.setEnabled(true);
user.setFirstName(formData.getFirst("firstName"));
user.setLastName(formData.getFirst("lastName"));
@@ -626,7 +625,7 @@ public class TokenService {
- AccessCodeEntry accessCode = tokenManager.parseCode(code, realm);
+ AccessCodeEntry accessCode = tokenManager.parseCode(code, session, realm);
if (accessCode == null) {
Map<String, String> res = new HashMap<String, String>();
res.put(OAuth2Constants.ERROR, "invalid_grant");
@@ -644,10 +643,10 @@ public class TokenService {
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
.build();
}
- if (!accessCode.getToken().isActive()) {
+ if (accessCode.getAction() != null) {
Map<String, String> res = new HashMap<String, String>();
res.put(OAuth2Constants.ERROR, "invalid_grant");
- res.put(OAuth2Constants.ERROR_DESCRIPTION, "Token expired");
+ res.put(OAuth2Constants.ERROR_DESCRIPTION, "Code is not active");
audit.error(Errors.INVALID_CODE);
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
.build();
@@ -667,7 +666,7 @@ public class TokenService {
.build();
}
- UserModel user = realm.getUserById(accessCode.getUser().getId());
+ UserModel user = session.users().getUserById(accessCode.getUser().getId(), realm);
if (user == null) {
Map<String, String> res = new HashMap<String, String>();
res.put(OAuth2Constants.ERROR, "invalid_grant");
@@ -686,9 +685,9 @@ public class TokenService {
.build();
}
- UserSessionModel session = realm.getUserSession(accessCode.getSessionState());
- if (!AuthenticationManager.isSessionValid(realm, session)) {
- AuthenticationManager.logout(realm, session, uriInfo);
+ UserSessionModel userSession = session.sessions().getUserSession(realm, accessCode.getSessionState());
+ if (!AuthenticationManager.isSessionValid(realm, userSession)) {
+ AuthenticationManager.logout(session, realm, userSession, uriInfo);
Map<String, String> res = new HashMap<String, String>();
res.put(OAuth2Constants.ERROR, "invalid_grant");
res.put(OAuth2Constants.ERROR_DESCRIPTION, "Session not active");
@@ -699,10 +698,22 @@ public class TokenService {
logger.debug("accessRequest SUCCESS");
- session.associateClient(client);
+ userSession.associateClient(client);
+
+ AccessToken token = tokenManager.createClientAccessToken(accessCode.getRequestedRoles(), realm, client, user, userSession);
+
+ try {
+ tokenManager.verifyAccess(token, realm, client, user);
+ } catch (OAuthErrorException e) {
+ Map<String, String> error = new HashMap<String, String>();
+ error.put(OAuth2Constants.ERROR, e.getError());
+ if (e.getDescription() != null) error.put(OAuth2Constants.ERROR_DESCRIPTION, e.getDescription());
+ audit.error(Errors.INVALID_CODE);
+ return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+ }
AccessTokenResponse res = tokenManager.responseBuilder(realm, client, audit)
- .accessToken(accessCode.getToken())
+ .accessToken(token)
.generateIDToken()
.generateRefreshToken().build();
@@ -825,14 +836,14 @@ public class TokenService {
}
logger.info("Checking cookie...");
- AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(realm, uriInfo, headers);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, headers);
if (authResult != null) {
UserModel user = authResult.getUser();
UserSessionModel session = authResult.getSession();
- logger.debug(user.getLoginName() + " already logged in.");
+ logger.debug(user.getUsername() + " already logged in.");
audit.user(user).session(session).detail(Details.AUTH_METHOD, "sso");
- return oauth.processAccessCode(scopeParam, state, redirect, client, user, session, null, false, "sso", audit);
+ return oauth.processAccessCode(scopeParam, state, redirect, client, user, session, audit);
}
if (prompt != null && prompt.equals("none")) {
@@ -924,11 +935,11 @@ public class TokenService {
}
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
- AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(realm, uriInfo, headers, false);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, headers, false);
if (authResult != null) {
logout(authResult.getSession());
} else if (sessionState != null) {
- UserSessionModel userSession = realm.getUserSession(sessionState);
+ UserSessionModel userSession = session.sessions().getUserSession(realm, sessionState);
if (userSession != null) {
logout(userSession);
} else {
@@ -946,10 +957,9 @@ public class TokenService {
}
}
- private void logout(UserSessionModel session) {
- UserModel user = session.getUser();
- authManager.logout(realm, session, uriInfo);
- audit.user(user).session(session).success();
+ private void logout(UserSessionModel userSession) {
+ authManager.logout(session, realm, userSession, uriInfo);
+ audit.user(userSession.getUser()).session(userSession).success();
}
/**
@@ -972,8 +982,8 @@ public class TokenService {
String code = formData.getFirst(OAuth2Constants.CODE);
- AccessCodeEntry accessCodeEntry = tokenManager.parseCode(code, realm);
- if (accessCodeEntry == null) {
+ AccessCodeEntry accessCodeEntry = tokenManager.parseCode(code, session, realm);
+ if (accessCodeEntry == null || !AccessCode.Action.OAUTH_GRANT.equals(accessCodeEntry.getAction())) {
audit.error(Errors.INVALID_CODE);
return oauth.forwardToSecurityFailure("Unknown access code.");
}
@@ -985,21 +995,23 @@ public class TokenService {
audit.client(accessCodeEntry.getClient())
.user(accessCodeEntry.getUser())
.detail(Details.RESPONSE_TYPE, "code")
- .detail(Details.AUTH_METHOD, accessCodeEntry.getAuthMethod())
- .detail(Details.REDIRECT_URI, redirect)
- .detail(Details.USERNAME, accessCodeEntry.getUsernameUsed());
-
- if (accessCodeEntry.isRememberMe()) {
- audit.detail(Details.REMEMBER_ME, "true");
+ .detail(Details.REDIRECT_URI, redirect);
+
+ UserSessionModel userSession = session.sessions().getUserSession(realm, accessCodeEntry.getSessionState());
+ if (userSession != null) {
+ audit.detail(Details.AUTH_METHOD, userSession.getAuthMethod());
+ audit.detail(Details.USERNAME, userSession.getLoginUsername());
+ if (userSession.isRememberMe()) {
+ audit.detail(Details.REMEMBER_ME, "true");
+ }
}
- UserSessionModel session = realm.getUserSession(accessCodeEntry.getSessionState());
- if (!AuthenticationManager.isSessionValid(realm, session)) {
- AuthenticationManager.logout(realm, session, uriInfo);
+ if (!AuthenticationManager.isSessionValid(realm, userSession)) {
+ AuthenticationManager.logout(session, realm, userSession, uriInfo);
audit.error(Errors.INVALID_CODE);
return oauth.forwardToSecurityFailure("Session not active");
}
- audit.session(session);
+ audit.session(userSession);
if (formData.containsKey("cancel")) {
audit.error(Errors.REJECTED_BY_USER);
@@ -1008,8 +1020,8 @@ public class TokenService {
audit.success();
- accessCodeEntry.resetExpiration();
- return oauth.redirectAccessCode(accessCodeEntry, session, state, redirect);
+ accessCodeEntry.setAction(null);
+ return oauth.redirectAccessCode(accessCodeEntry, userSession, state, redirect);
}
@Path("oauth/oob")
diff --git a/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredAuditEvents.java b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredAuditEvents.java
old mode 100644
new mode 100755
index 5884f49..193589c
--- a/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredAuditEvents.java
+++ b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredAuditEvents.java
@@ -13,7 +13,7 @@ public class ClearExpiredAuditEvents implements ScheduledTask {
public void run(KeycloakSession session) {
AuditProvider audit = session.getProvider(AuditProvider.class);
if (audit != null) {
- for (RealmModel realm : session.getRealms()) {
+ for (RealmModel realm : session.realms().getRealms()) {
if (realm.isAuditEnabled() && realm.getAuditExpiration() > 0) {
long olderThan = System.currentTimeMillis() - realm.getAuditExpiration() * 1000;
audit.clear(realm.getId(), olderThan);
diff --git a/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
index b6b5f99..96c8bad 100755
--- a/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
+++ b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
@@ -2,6 +2,7 @@ package org.keycloak.services.scheduled;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionProvider;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -10,8 +11,9 @@ public class ClearExpiredUserSessions implements ScheduledTask {
@Override
public void run(KeycloakSession session) {
- for (RealmModel realm : session.getRealms()) {
- realm.removeExpiredUserSessions();
+ UserSessionProvider sessions = session.sessions();
+ for (RealmModel realm : session.realms().getRealms()) {
+ sessions.removeExpiredUserSessions(realm);
}
}
diff --git a/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java b/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java
old mode 100644
new mode 100755
index 0eb1cef..785cc81
--- a/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java
+++ b/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java
@@ -4,8 +4,6 @@ import org.codehaus.jackson.JsonNode;
import org.keycloak.Config;
import org.keycloak.util.StringPropertyReplacer;
-import java.util.ArrayList;
-
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
testsuite/integration/pom.xml 322(+101 -221)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 052f6b1..55fe1a0 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -25,6 +25,25 @@
<dependencies>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-dependencies-server-all</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
</dependency>
@@ -86,188 +105,20 @@
<artifactId>bcprov-jdk16</artifactId>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jboss-logging</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-email</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-services</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-invalidation-cache-model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-basic</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-js-adapter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-undertow-adapter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${keycloak.apache.httpcomponents.version}</version>
</dependency>
-
- <!--
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-picketlink</artifactId>
- <version>${project.version}</version>
- </dependency>
- -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-github</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-google</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-twitter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.twitter4j</groupId>
- <artifactId>twitter4j-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-facebook</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.freemarker</groupId>
- <artifactId>freemarker</artifactId>
- </dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-themes</artifactId>
+ <artifactId>keycloak-ldap-federation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-picketlink-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-picketlink-realm</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-impl</artifactId>
+ <artifactId>keycloak-undertow-adapter</artifactId>
<version>${project.version}</version>
</dependency>
-
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
@@ -338,36 +189,17 @@
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
</dependency>
-
- <!-- Mongo dependencies specified here and not in mongo profile, just to allow running tests from IDE -->
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-mongo</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-mongo</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.mongodb</groupId>
- <artifactId>mongo-java-driver</artifactId>
- </dependency>
-
- <!-- Encrypted ZIP -->
- <dependency>
- <groupId>de.idyl</groupId>
- <artifactId>winzipaes</artifactId>
+ <groupId>org.picketbox</groupId>
+ <artifactId>picketbox-ldap</artifactId>
+ <scope>compile</scope>
+ <type>test-jar</type>
</dependency>
-
- <!-- This adds couple of other dependencies (like picketlink) -->
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-tests</artifactId>
- <version>${project.version}</version>
+ <groupId>org.picketbox</groupId>
+ <artifactId>picketbox-ldap</artifactId>
+ <scope>compile</scope>
</dependency>
-
</dependencies>
<build>
<plugins>
@@ -441,20 +273,35 @@
</profile>
<profile>
+ <id>jpa</id>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <keycloak.realm.provider>jpa</keycloak.realm.provider>
+ <keycloak.user.provider>jpa</keycloak.user.provider>
+ <keycloak.audit.provider>jpa</keycloak.audit.provider>
+ <keycloak.userSessions.provider>jpa</keycloak.userSessions.provider>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
<id>mongo</id>
- <activation>
- <property>
- <name>keycloak.model.provider</name>
- <value>mongo</value>
- </property>
- </activation>
<properties>
- <keycloak.model.mongo.host>localhost</keycloak.model.mongo.host>
- <keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
- <keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
- <keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
- <keycloak.model.mongo.bindIp>127.0.0.1</keycloak.model.mongo.bindIp>
+ <keycloak.connectionsMongo.host>localhost</keycloak.connectionsMongo.host>
+ <keycloak.connectionsMongo.port>27018</keycloak.connectionsMongo.port>
+ <keycloak.connectionsMongo.db>keycloak</keycloak.connectionsMongo.db>
+ <keycloak.connectionsMongo.clearOnStartup>true</keycloak.connectionsMongo.clearOnStartup>
+ <keycloak.connectionsMongo.bindIp>127.0.0.1</keycloak.connectionsMongo.bindIp>
</properties>
<build>
@@ -473,19 +320,15 @@
</goals>
<configuration>
<systemPropertyVariables>
- <keycloak.model.provider>mongo</keycloak.model.provider>
- <keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
- <keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
- <keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
- <keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
- <keycloak.model.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.model.mongo.bindIp>
-
+ <keycloak.realm.provider>mongo</keycloak.realm.provider>
+ <keycloak.user.provider>mongo</keycloak.user.provider>
<keycloak.audit.provider>mongo</keycloak.audit.provider>
- <keycloak.audit.mongo.host>${keycloak.model.mongo.host}</keycloak.audit.mongo.host>
- <keycloak.audit.mongo.port>${keycloak.model.mongo.port}</keycloak.audit.mongo.port>
- <keycloak.audit.mongo.db>${keycloak.model.mongo.db}</keycloak.audit.mongo.db>
- <keycloak.audit.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.audit.mongo.clearOnStartup>
- <keycloak.audit.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.audit.mongo.bindIp>
+ <keycloak.userSessions.provider>mongo</keycloak.userSessions.provider>
+ <keycloak.connectionsMongo.host>${keycloak.connectionsMongo.host}</keycloak.connectionsMongo.host>
+ <keycloak.connectionsMongo.port>${keycloak.connectionsMongo.port}</keycloak.connectionsMongo.port>
+ <keycloak.connectionsMongo.db>${keycloak.connectionsMongo.db}</keycloak.connectionsMongo.db>
+ <keycloak.connectionsMongo.clearOnStartup>${keycloak.connectionsMongo.clearOnStartup}</keycloak.connectionsMongo.clearOnStartup>
+ <keycloak.connectionsMongo.bindIp>${keycloak.connectionsMongo.bindIp}</keycloak.connectionsMongo.bindIp>
</systemPropertyVariables>
</configuration>
</execution>
@@ -510,10 +353,10 @@
<goal>start</goal>
</goals>
<configuration>
- <port>${keycloak.model.mongo.port}</port>
+ <port>${keycloak.connectionsMongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
- <bindIp>${keycloak.model.mongo.bindIp}</bindIp>
+ <bindIp>${keycloak.connectionsMongo.bindIp}</bindIp>
</configuration>
</execution>
<execution>
@@ -529,5 +372,42 @@
</build>
</profile>
+
+ <!-- MySQL -->
+ <profile>
+ <activation>
+ <property>
+ <name>keycloak.connectionsJpa.driver</name>
+ <value>com.mysql.jdbc.Driver</value>
+ </property>
+ </activation>
+ <id>mysql</id>
+ <dependencies>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ <version>${mysql.version}</version>
+ </dependency>
+ </dependencies>
+ </profile>
+
+ <!-- PostgreSQL -->
+ <profile>
+ <activation>
+ <property>
+ <name>keycloak.connectionsJpa.driver</name>
+ <value>org.postgresql.Driver</value>
+ </property>
+ </activation>
+ <id>postgresql</id>
+ <dependencies>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>${postgresql.version}</version>
+ </dependency>
+ </dependencies>
+ </profile>
+
</profiles>
</project>
testsuite/integration/README.md 7(+4 -3)
diff --git a/testsuite/integration/README.md b/testsuite/integration/README.md
index a2033eb..564451f 100644
--- a/testsuite/integration/README.md
+++ b/testsuite/integration/README.md
@@ -11,7 +11,8 @@ To run the tests with Firefox add `-Dbrowser=firefox` or for Chrome add `-Dbrows
Mongo
-----
-The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.model.provider=mongo` when executing it.
+The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.realm.provider=mongo` when executing it.
+This single property will cause that mongo will be used for realm-model, user-model and audit.
Note that this will automatically run embedded Mongo database on localhost/27018 and it will stop it after whole testsuite is finished.
So you don't need to have Mongo installed on your laptop to run mongo execution tests.
@@ -52,11 +53,11 @@ For example to use the example themes run the server with:
To start a Keycloak server with identity model data persisted in Mongo database instead of default JPA/H2 you can run:
- mvn exec:java -Pkeycloak-server -Dkeycloak.model.provider=mongo
+ mvn exec:java -Pkeycloak-server -Dkeycloak.realm.provider=mongo -Dkeycloak.user.provider=mongo -Dkeycloak.audit.provider=mongo
By default it's using database `keycloak` on localhost/27017 and it uses already existing data from this DB (no cleanup of existing data during bootstrap). Assumption is that you already have DB running on localhost/27017 . Use system properties to configure things differently:
- mvn exec:java -Pkeycloak-server -Dkeycloak.model.provider=mongo -Dkeycloak.model.mongo.host=localhost -Dkeycloak.model.mongo.port=27017 -Dkeycloak.model.mongo.db=keycloak -Dkeycloak.model.mongo.clearOnStartup=false
+ mvn exec:java -Pkeycloak-server -Dkeycloak.realm.provider=mongo -Dkeycloak.user.provider=mongo -Dkeycloak.audit.provider=mongo -Dkeycloak.connectionsMongo.host=localhost -Dkeycloak.connectionsMongo.port=27017 -Dkeycloak.connectionsMongo.db=keycloak -Dkeycloak.connectionsMongo.clearOnStartup=false
Note that if you are using Mongo model, it would mean that Mongo will be used for audit as well. You may need to use audit related properties for configuration of Mongo if you want to override default ones (For example keycloak.audit.mongo.host, keycloak.audit.mongo.port etc)
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
index c401f0a..4e22fc5 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
@@ -201,6 +201,10 @@ public class KeycloakServer {
return server;
}
+ public KeycloakServerConfig getConfig() {
+ return config;
+ }
+
public void importRealm(InputStream realm) {
RealmRepresentation rep = loadJson(realm, RealmRepresentation.class);
importRealm(rep);
@@ -242,7 +246,7 @@ public class KeycloakServer {
RealmManager manager = new RealmManager(session);
RealmModel adminRealm = manager.getKeycloakAdminstrationRealm();
- UserModel admin = adminRealm.getUser("admin");
+ UserModel admin = session.users().getUserByUsername("admin", adminRealm);
admin.removeRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
session.getTransaction().commit();
diff --git a/testsuite/integration/src/main/resources/log4j.properties b/testsuite/integration/src/main/resources/log4j.properties
index 47ecd4e..050a889 100755
--- a/testsuite/integration/src/main/resources/log4j.properties
+++ b/testsuite/integration/src/main/resources/log4j.properties
@@ -4,4 +4,7 @@ log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
-log4j.logger.org.keycloak=warn
+log4j.logger.org.keycloak=info
+
+log4j.logger.org.xnio=off
+log4j.logger.org.hibernate=info
\ No newline at end of file
diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
index 9204d6f..8e1ef4c 100755
--- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
@@ -1,58 +1,83 @@
-{
- "admin": {
- "realm": "master"
- },
-
- "audit": {
- "provider": "${keycloak.audit.provider,keycloak.model.provider:jpa}",
- "mongo": {
- "host": "${keycloak.audit.mongo.host:127.0.0.1}",
- "port": "${keycloak.audit.mongo.port:27017}",
- "db": "${keycloak.audit.mongo.db:keycloak-audit}",
- "clearOnStartup": "${keycloak.audit.mongo.clearOnStartup:false}"
- }
- },
-
- "model": {
- "provider": "${keycloak.model.provider:jpa}",
- "mongo": {
- "host": "${keycloak.model.mongo.host:127.0.0.1}",
- "port": "${keycloak.model.mongo.port:27017}",
- "db": "${keycloak.model.mongo.db:keycloak}",
- "clearOnStartup": "${keycloak.model.mongo.clearOnStartup:false}"
- }
- },
-
- "modelCache": {
- "provider": "${keycloak.model.cache.provider:simple}"
- },
-
- "timer": {
- "provider": "basic"
- },
-
- "theme": {
- "default": "keycloak",
- "staticMaxAge": 2592000,
- "cacheTemplates": "${keycloak.theme.cacheTemplates:true}",
- "folder": {
- "dir": "${keycloak.theme.dir}"
- }
- },
-
- "login": {
- "provider": "freemarker"
- },
-
- "account": {
- "provider": "freemarker"
- },
-
- "email": {
- "provider": "freemarker"
- },
-
- "scheduled": {
- "interval": 900
- }
+{
+ "admin": {
+ "realm": "master"
+ },
+
+ "audit": {
+ "provider": "${keycloak.audit.provider:jpa}"
+ },
+
+ "realm": {
+ "provider": "${keycloak.realm.provider:jpa}"
+ },
+
+ "user": {
+ "provider": "${keycloak.user.provider:jpa}"
+ },
+
+ "userSessions": {
+ "provider" : "${keycloak.userSessions.provider:mem}"
+ },
+
+ "realmCache": {
+ "provider": "${keycloak.realm.cache.provider:mem}"
+ },
+
+ "userCache": {
+ "provider": "${keycloak.user.cache.provider:mem}",
+ "mem": {
+ "maxSize": 20000
+ }
+ },
+
+ "timer": {
+ "provider": "basic"
+ },
+
+ "theme": {
+ "default": "keycloak",
+ "staticMaxAge": 2592000,
+ "cacheTemplates": "${keycloak.theme.cacheTemplates:true}",
+ "folder": {
+ "dir": "${keycloak.theme.dir}"
+ }
+ },
+
+ "login": {
+ "provider": "freemarker"
+ },
+
+ "account": {
+ "provider": "freemarker"
+ },
+
+ "email": {
+ "provider": "freemarker"
+ },
+
+ "scheduled": {
+ "interval": 900
+ },
+
+ "connectionsJpa": {
+ "default": {
+ "url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test}",
+ "driver": "${keycloak.connectionsJpa.driver:org.h2.Driver}",
+ "driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
+ "user": "${keycloak.connectionsJpa.user:sa}",
+ "password": "${keycloak.connectionsJpa.password:}",
+ "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:create-drop}",
+ "showSql": "${keycloak.connectionsJpa.showSql:false}",
+ "formatSql": "${keycloak.connectionsJpa.formatSql:true}"
+ }
+ },
+
+ "connectionsMongo": {
+ "default": {
+ "host": "${keycloak.connectionsMongo.host:127.0.0.1}",
+ "port": "${keycloak.connectionsMongo.port:27017}",
+ "db": "${keycloak.connectionsMongo.db:keycloak}",
+ "clearOnStartup": "${keycloak.connectionsMongo.clearOnStartup:true}"
+ }
+ }
}
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 770ef84..7c3c3da 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -76,11 +76,11 @@ public class AccountTest {
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
ApplicationModel accountApp = appRealm.getApplicationNameMap().get(org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_APP);
- UserModel user2 = appRealm.addUser("test-user-no-access@localhost");
+ UserModel user2 = manager.getSession().users().addUser(appRealm, "test-user-no-access@localhost");
user2.setEnabled(true);
for (String r : accountApp.getDefaultRoles()) {
user2.deleteRoleMapping(accountApp.getRole(r));
@@ -148,7 +148,7 @@ public class AccountTest {
keycloakRule.update(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
UserCredentialModel cred = new UserCredentialModel();
cred.setType(CredentialRepresentation.PASSWORD);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
index ad78f4a..18ca838 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
@@ -48,7 +48,7 @@ public class ProfileTest {
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
user.setFirstName("First");
user.setLastName("Last");
user.setAttribute("key1", "value1");
@@ -56,7 +56,7 @@ public class ProfileTest {
ApplicationModel accountApp = appRealm.getApplicationByName(org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_APP);
- UserModel user2 = appRealm.addUser("test-user-no-access@localhost");
+ UserModel user2 = manager.getSession().users().addUser(appRealm, "test-user-no-access@localhost");
user2.setEnabled(true);
for (String r : accountApp.getDefaultRoles()) {
user2.deleteRoleMapping(accountApp.getRole(r));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
index 476cdcc..9ca52a3 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
@@ -94,7 +94,7 @@ public class RequiredActionEmailVerificationTest {
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
appRealm.setVerifyEmail(true);
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
user.setEmailVerified(false);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
index 49f97ed..4f9f740 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
@@ -54,7 +54,7 @@ public class RequiredActionMultipleActionsTest {
@Override
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
user.addRequiredAction(RequiredAction.UPDATE_PROFILE);
user.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java
index d4cc32e..e357843 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java
@@ -56,7 +56,7 @@ public class RequiredActionResetPasswordTest {
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
appRealm.setResetPasswordAllowed(true);
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
user.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
index 056e6fd..a5a87c1 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
@@ -72,7 +72,7 @@ public class RequiredActionUpdateProfileTest {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
}
});
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 1cc65ca..3b57ca3 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -21,7 +21,7 @@
*/
package org.keycloak.testsuite.adapter;
-import org.jboss.resteasy.util.BasicAuthHelper;
+import org.keycloak.util.BasicAuthHelper;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
@@ -75,7 +75,7 @@ public class AdapterTest {
@ClassRule
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
@Override
- protected void configure(RealmManager manager, RealmModel adminRealm) {
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/adapter-test/demorealm.json"), RealmRepresentation.class);
RealmModel realm = manager.importRealm(representation);
@@ -99,9 +99,9 @@ public class AdapterTest {
RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
TokenManager tm = new TokenManager();
- UserModel admin = adminRealm.getUser("admin");
- UserSessionModel userSession = adminRealm.createUserSession(admin, null);
- AccessToken token = tm.createClientAccessToken(null, adminRealm, adminConsole, admin, userSession);
+ UserModel admin = session.users().getUserByUsername("admin", adminRealm);
+ UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false);
+ AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
return tm.encodeToken(adminRealm, token);
} finally {
keycloakRule.stopSession(session, true);
@@ -231,7 +231,7 @@ public class AdapterTest {
Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
KeycloakSession session = keycloakRule.startSession();
- RealmModel realm = session.getRealmByName("demo");
+ RealmModel realm = session.realms().getRealmByName("demo");
int originalIdle = realm.getSsoSessionIdleTimeout();
realm.setSsoSessionIdleTimeout(1);
session.getTransaction().commit();
@@ -245,7 +245,7 @@ public class AdapterTest {
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
session = keycloakRule.startSession();
- realm = session.getRealmByName("demo");
+ realm = session.realms().getRealmByName("demo");
realm.setSsoSessionIdleTimeout(originalIdle);
session.getTransaction().commit();
session.close();
@@ -265,7 +265,7 @@ public class AdapterTest {
Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
KeycloakSession session = keycloakRule.startSession();
- RealmModel realm = session.getRealmByName("demo");
+ RealmModel realm = session.realms().getRealmByName("demo");
int originalIdle = realm.getSsoSessionIdleTimeout();
realm.setSsoSessionIdleTimeout(1);
session.getTransaction().commit();
@@ -274,8 +274,8 @@ public class AdapterTest {
Thread.sleep(2000);
session = keycloakRule.startSession();
- realm = session.getRealmByName("demo");
- realm.removeExpiredUserSessions();
+ realm = session.realms().getRealmByName("demo");
+ session.sessions().removeExpiredUserSessions(realm);
session.getTransaction().commit();
session.close();
@@ -284,7 +284,7 @@ public class AdapterTest {
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
session = keycloakRule.startSession();
- realm = session.getRealmByName("demo");
+ realm = session.realms().getRealmByName("demo");
realm.setSsoSessionIdleTimeout(originalIdle);
session.getTransaction().commit();
session.close();
@@ -304,7 +304,7 @@ public class AdapterTest {
Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
KeycloakSession session = keycloakRule.startSession();
- RealmModel realm = session.getRealmByName("demo");
+ RealmModel realm = session.realms().getRealmByName("demo");
int original = realm.getSsoSessionMaxLifespan();
realm.setSsoSessionMaxLifespan(1);
session.getTransaction().commit();
@@ -318,7 +318,7 @@ public class AdapterTest {
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
session = keycloakRule.startSession();
- realm = session.getRealmByName("demo");
+ realm = session.realms().getRealmByName("demo");
realm.setSsoSessionMaxLifespan(original);
session.getTransaction().commit();
session.close();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
index 96af4c1..a9114eb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
@@ -28,6 +28,7 @@ import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@@ -71,7 +72,7 @@ public class RelativeUriAdapterTest {
@ClassRule
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
@Override
- protected void configure(RealmManager manager, RealmModel adminRealm) {
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/adapter-test/demorealm-relative.json"), RealmRepresentation.class);
RealmModel realm = manager.importRealm(representation);
@@ -85,9 +86,9 @@ public class RelativeUriAdapterTest {
deployApplication("product-portal", "/product-portal", ProductServlet.class, url.getPath(), "user");
ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
TokenManager tm = new TokenManager();
- UserModel admin = adminRealm.getUser("admin");
- UserSessionModel session = adminRealm.createUserSession(admin, null);
- AccessToken token = tm.createClientAccessToken(null, adminRealm, adminConsole, admin, session);
+ UserModel admin = session.users().getUserByUsername("admin", adminRealm);
+ UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "user", null, "form", false);
+ AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
adminToken = tm.encodeToken(adminRealm, token);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
new file mode 100755
index 0000000..6b39c49
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -0,0 +1,315 @@
+/*
+ * 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.admin;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.Config;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ApplicationRepresentation;
+import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.managers.TokenManager;
+import org.keycloak.services.resources.admin.AdminRoot;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+import org.keycloak.testutils.KeycloakServer;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests Undertow Adapter
+ *
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class AdminAPITest {
+
+ @ClassRule
+ public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
+ @Override
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+ }
+ };
+
+ private static String createToken() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+
+ RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
+ ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
+ TokenManager tm = new TokenManager();
+ UserModel admin = session.users().getUserByUsername("admin", adminRealm);
+ UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false);
+ AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
+ return tm.encodeToken(adminRealm, token);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+ protected void testCreateRealm(RealmRepresentation rep) {
+ String token = createToken();
+ final String authHeader = "Bearer " + token;
+ ClientRequestFilter authFilter = new ClientRequestFilter() {
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+ }
+ };
+ Client client = ClientBuilder.newBuilder().register(authFilter).build();
+ UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
+ WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
+ String realmName = rep.getRealm();
+ WebTarget realmTarget = adminRealms.path(realmName);
+
+
+ // create with just name, enabled, and id, just like admin console
+ RealmRepresentation newRep = new RealmRepresentation();
+ newRep.setRealm(rep.getRealm());
+ newRep.setEnabled(rep.isEnabled());
+ {
+ Response response = adminRealms.request().post(Entity.json(newRep));
+ Assert.assertEquals(201, response.getStatus());
+ response.close();
+ }
+ // todo test with full import with initial create
+ RealmRepresentation storedRealm = realmTarget.request().get(RealmRepresentation.class);
+ checkRealmRep(newRep, storedRealm);
+
+ Response updateResponse = realmTarget.request().put(Entity.json(rep));
+ Assert.assertEquals(204, updateResponse.getStatus());
+ updateResponse.close();
+ storedRealm = realmTarget.request().get(RealmRepresentation.class);
+ checkRealmRep(rep, storedRealm);
+
+ if (rep.getApplications() != null) {
+ WebTarget applicationsTarget = realmTarget.path("applications");
+ for (ApplicationRepresentation appRep : rep.getApplications()) {
+ ApplicationRepresentation newApp = new ApplicationRepresentation();
+ if (appRep.getId() != null) newApp.setId(appRep.getId());
+ newApp.setName(appRep.getName());
+ if (appRep.getSecret() != null) {
+ newApp.setSecret(appRep.getSecret());
+ }
+ Response appCreateResponse = applicationsTarget.request().post(Entity.json(newApp));
+ Assert.assertEquals(201, appCreateResponse.getStatus());
+ appCreateResponse.close();
+ WebTarget appTarget = applicationsTarget.path(appRep.getName());
+ CredentialRepresentation cred = appTarget.path("client-secret").request().get(CredentialRepresentation.class);
+ if (appRep.getSecret() != null) Assert.assertEquals(appRep.getSecret(), cred.getValue());
+ CredentialRepresentation newCred = appTarget.path("client-secret").request().post(null, CredentialRepresentation.class);
+ Assert.assertNotEquals(newCred.getValue(), cred.getValue());
+
+ Response appUpdateResponse = appTarget.request().put(Entity.json(appRep));
+ Assert.assertEquals(204, appUpdateResponse.getStatus());
+ appUpdateResponse.close();
+
+
+ ApplicationRepresentation storedApp = appTarget.request().get(ApplicationRepresentation.class);
+
+ checkAppUpdate(appRep, storedApp);
+
+ }
+ }
+
+ // delete realm
+ {
+ Response response = adminRealms.path(realmName).request().delete();
+ Assert.assertEquals(204, response.getStatus());
+ response.close();
+
+ }
+ client.close();
+ }
+
+ protected void checkAppUpdate(ApplicationRepresentation appRep, ApplicationRepresentation storedApp) {
+ if (appRep.getName() != null) Assert.assertEquals(appRep.getName(), storedApp.getName());
+ if (appRep.isEnabled() != null) Assert.assertEquals(appRep.isEnabled(), storedApp.isEnabled());
+ if (appRep.isBearerOnly() != null) Assert.assertEquals(appRep.isBearerOnly(), storedApp.isBearerOnly());
+ if (appRep.isPublicClient() != null) Assert.assertEquals(appRep.isPublicClient(), storedApp.isPublicClient());
+ if (appRep.getAdminUrl() != null) Assert.assertEquals(appRep.getAdminUrl(), storedApp.getAdminUrl());
+ if (appRep.getBaseUrl() != null) Assert.assertEquals(appRep.getBaseUrl(), storedApp.getBaseUrl());
+ if (appRep.isSurrogateAuthRequired() != null) Assert.assertEquals(appRep.isSurrogateAuthRequired(), storedApp.isSurrogateAuthRequired());
+
+ if (appRep.getNotBefore() != null) {
+ Assert.assertEquals(appRep.getNotBefore(), storedApp.getNotBefore());
+ }
+ if (appRep.getDefaultRoles() != null) {
+ Set<String> set = new HashSet<String>();
+ for (String val : appRep.getDefaultRoles()) {
+ set.add(val);
+ }
+ Set<String> storedSet = new HashSet<String>();
+ for (String val : storedApp.getDefaultRoles()) {
+ storedSet.add(val);
+ }
+
+ Assert.assertEquals(set, storedSet);
+ }
+
+ List<String> redirectUris = appRep.getRedirectUris();
+ if (redirectUris != null) {
+ Set<String> set = new HashSet<String>();
+ for (String val : appRep.getRedirectUris()) {
+ set.add(val);
+ }
+ Set<String> storedSet = new HashSet<String>();
+ for (String val : storedApp.getRedirectUris()) {
+ storedSet.add(val);
+ }
+
+ Assert.assertEquals(set, storedSet);
+ }
+
+ List<String> webOrigins = appRep.getWebOrigins();
+ if (webOrigins != null) {
+ Set<String> set = new HashSet<String>();
+ for (String val : appRep.getWebOrigins()) {
+ set.add(val);
+ }
+ Set<String> storedSet = new HashSet<String>();
+ for (String val : storedApp.getWebOrigins()) {
+ storedSet.add(val);
+ }
+
+ Assert.assertEquals(set, storedSet);
+ }
+
+ if (appRep.getClaims() != null) {
+ Assert.assertEquals(appRep.getClaims(), storedApp.getClaims());
+ }
+ }
+
+ protected void checkRealmRep(RealmRepresentation rep, RealmRepresentation storedRealm) {
+ if (rep.getId() != null) {
+ Assert.assertEquals(rep.getId(), storedRealm.getId());
+ }
+ if (rep.getRealm() != null) {
+ Assert.assertEquals(rep.getRealm(), storedRealm.getRealm());
+ }
+ if (rep.isEnabled() != null) Assert.assertEquals(rep.isEnabled(), storedRealm.isEnabled());
+ if (rep.isSocial() != null) Assert.assertEquals(rep.isSocial(), storedRealm.isSocial());
+ if (rep.isBruteForceProtected() != null) Assert.assertEquals(rep.isBruteForceProtected(), storedRealm.isBruteForceProtected());
+ if (rep.getMaxFailureWaitSeconds() != null) Assert.assertEquals(rep.getMaxFailureWaitSeconds(), storedRealm.getMaxFailureWaitSeconds());
+ if (rep.getMinimumQuickLoginWaitSeconds() != null) Assert.assertEquals(rep.getMinimumQuickLoginWaitSeconds(), storedRealm.getMinimumQuickLoginWaitSeconds());
+ if (rep.getWaitIncrementSeconds() != null) Assert.assertEquals(rep.getWaitIncrementSeconds(), storedRealm.getWaitIncrementSeconds());
+ if (rep.getQuickLoginCheckMilliSeconds() != null) Assert.assertEquals(rep.getQuickLoginCheckMilliSeconds(), storedRealm.getQuickLoginCheckMilliSeconds());
+ if (rep.getMaxDeltaTimeSeconds() != null) Assert.assertEquals(rep.getMaxDeltaTimeSeconds(), storedRealm.getMaxDeltaTimeSeconds());
+ if (rep.getFailureFactor() != null) Assert.assertEquals(rep.getFailureFactor(), storedRealm.getFailureFactor());
+ if (rep.isPasswordCredentialGrantAllowed() != null) Assert.assertEquals(rep.isPasswordCredentialGrantAllowed(), storedRealm.isPasswordCredentialGrantAllowed());
+ if (rep.isRegistrationAllowed() != null) Assert.assertEquals(rep.isRegistrationAllowed(), storedRealm.isRegistrationAllowed());
+ if (rep.isRememberMe() != null) Assert.assertEquals(rep.isRememberMe(), storedRealm.isRememberMe());
+ if (rep.isVerifyEmail() != null) Assert.assertEquals(rep.isVerifyEmail(), storedRealm.isVerifyEmail());
+ if (rep.isResetPasswordAllowed() != null) Assert.assertEquals(rep.isResetPasswordAllowed(), storedRealm.isResetPasswordAllowed());
+ if (rep.isUpdateProfileOnInitialSocialLogin() != null)
+ Assert.assertEquals(rep.isUpdateProfileOnInitialSocialLogin(), storedRealm.isUpdateProfileOnInitialSocialLogin());
+ if (rep.isSslNotRequired() != null) Assert.assertEquals(rep.isSslNotRequired(), storedRealm.isSslNotRequired());
+ if (rep.getAccessCodeLifespan() != null) Assert.assertEquals(rep.getAccessCodeLifespan(), storedRealm.getAccessCodeLifespan());
+ if (rep.getAccessCodeLifespanUserAction() != null)
+ Assert.assertEquals(rep.getAccessCodeLifespanUserAction(), storedRealm.getAccessCodeLifespanUserAction());
+ if (rep.getNotBefore() != null) Assert.assertEquals(rep.getNotBefore(), storedRealm.getNotBefore());
+ if (rep.getAccessTokenLifespan() != null) Assert.assertEquals(rep.getAccessTokenLifespan(), storedRealm.getAccessTokenLifespan());
+ if (rep.getSsoSessionIdleTimeout() != null) Assert.assertEquals(rep.getSsoSessionIdleTimeout(), storedRealm.getSsoSessionIdleTimeout());
+ if (rep.getSsoSessionMaxLifespan() != null) Assert.assertEquals(rep.getSsoSessionMaxLifespan(), storedRealm.getSsoSessionMaxLifespan());
+ if (rep.getRequiredCredentials() != null) {
+ Assert.assertNotNull(storedRealm.getRequiredCredentials());
+ for (String cred : rep.getRequiredCredentials()) {
+ Assert.assertTrue(storedRealm.getRequiredCredentials().contains(cred));
+ }
+ }
+ if (rep.getLoginTheme() != null) Assert.assertEquals(rep.getLoginTheme(), storedRealm.getLoginTheme());
+ if (rep.getAccountTheme() != null) Assert.assertEquals(rep.getAccountTheme(), storedRealm.getAccountTheme());
+ if (rep.getAdminTheme() != null) Assert.assertEquals(rep.getAdminTheme(), storedRealm.getAdminTheme());
+ if (rep.getEmailTheme() != null) Assert.assertEquals(rep.getEmailTheme(), storedRealm.getEmailTheme());
+
+ if (rep.getPasswordPolicy() != null) Assert.assertEquals(rep.getPasswordPolicy(), storedRealm.getPasswordPolicy());
+
+ if (rep.getDefaultRoles() != null) {
+ Assert.assertNotNull(storedRealm.getDefaultRoles());
+ for (String role : rep.getDefaultRoles()) {
+ Assert.assertTrue(storedRealm.getDefaultRoles().contains(role));
+ }
+ }
+
+ if (rep.getSmtpServer() != null) {
+ Assert.assertEquals(rep.getSmtpServer(), storedRealm.getSmtpServer());
+ }
+
+ if (rep.getSocialProviders() != null) {
+ Assert.assertEquals(rep.getSocialProviders(), storedRealm.getSocialProviders());
+ }
+
+ if (rep.getLdapServer() != null) {
+ Assert.assertEquals(rep.getLdapServer(), storedRealm.getLdapServer());
+ }
+ if (rep.getAuthenticationProviders() != null) {
+ Set<AuthenticationProviderRepresentation> set = new HashSet<AuthenticationProviderRepresentation>();
+ for (AuthenticationProviderRepresentation authRep : rep.getAuthenticationProviders()) {
+ set.add(authRep);
+ }
+ Set<AuthenticationProviderRepresentation> storedSet = new HashSet<AuthenticationProviderRepresentation>();
+ if (storedRealm.getAuthenticationProviders() != null) {
+ for (AuthenticationProviderRepresentation authRep : storedRealm.getAuthenticationProviders()) {
+ storedSet.add(authRep);
+ }
+ }
+ Assert.assertEquals(set, storedSet);
+ }
+ }
+
+ protected void testCreateRealm(String path) {
+ RealmRepresentation rep = KeycloakServer.loadJson(getClass().getResourceAsStream(path), RealmRepresentation.class);
+ Assert.assertNotNull(rep);
+ testCreateRealm(rep);
+ }
+
+ @Test
+ public void testAdminApi() {
+ RealmRepresentation empty = new RealmRepresentation();
+ empty.setEnabled(true);
+ empty.setRealm("empty");
+ testCreateRealm(empty);
+ testCreateRealm("/admin-test/testrealm.json");
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/audit/AuditProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/audit/AuditProviderTest.java
new file mode 100644
index 0000000..6c0c538
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/audit/AuditProviderTest.java
@@ -0,0 +1,134 @@
+package org.keycloak.testsuite.audit;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.audit.AuditProvider;
+import org.keycloak.audit.Event;
+import org.keycloak.audit.EventType;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.testsuite.rule.KeycloakRule;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AuditProviderTest {
+
+ @ClassRule
+ public static KeycloakRule kc = new KeycloakRule();
+
+ private KeycloakSession session;
+
+ private AuditProvider audit;
+
+ @Before
+ public void before() {
+ session = kc.startSession();
+ audit = session.getProvider(AuditProvider.class);
+ }
+
+ @After
+ public void after() {
+ audit.clear();
+ kc.stopSession(session, true);
+ }
+
+ @Test
+ public void save() {
+ audit.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ }
+
+ @Test
+ public void query() {
+ long oldest = System.currentTimeMillis() - 30000;
+ long newest = System.currentTimeMillis() + 30000;
+
+ audit.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId2", "127.0.0.1", "error"));
+ audit.onEvent(create(EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(oldest, EventType.LOGIN, "realmId", "clientId2", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId2", "127.0.0.1", "error"));
+
+ resetSession();
+
+ Assert.assertEquals(5, audit.createQuery().client("clientId").getResultList().size());
+ Assert.assertEquals(5, audit.createQuery().realm("realmId").getResultList().size());
+ Assert.assertEquals(4, audit.createQuery().event(EventType.LOGIN).getResultList().size());
+ Assert.assertEquals(6, audit.createQuery().event(EventType.LOGIN, EventType.REGISTER).getResultList().size());
+ Assert.assertEquals(4, audit.createQuery().user("userId").getResultList().size());
+
+ Assert.assertEquals(1, audit.createQuery().user("userId").event(EventType.REGISTER).getResultList().size());
+
+ Assert.assertEquals(2, audit.createQuery().maxResults(2).getResultList().size());
+ Assert.assertEquals(1, audit.createQuery().firstResult(5).getResultList().size());
+
+ Assert.assertEquals(newest, audit.createQuery().maxResults(1).getResultList().get(0).getTime());
+ Assert.assertEquals(oldest, audit.createQuery().firstResult(5).maxResults(1).getResultList().get(0).getTime());
+ }
+
+ @Test
+ public void clear() {
+ audit.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis() - 20000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
+
+ resetSession();
+
+ audit.clear("realmId");
+
+ Assert.assertEquals(1, audit.createQuery().getResultList().size());
+ }
+
+ @Test
+ public void clearOld() {
+ audit.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis() - 20000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
+ audit.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
+
+ resetSession();
+
+ audit.clear("realmId", System.currentTimeMillis() - 10000);
+
+ Assert.assertEquals(3, audit.createQuery().getResultList().size());
+ }
+
+ private Event create(EventType event, String realmId, String clientId, String userId, String ipAddress, String error) {
+ return create(System.currentTimeMillis(), event, realmId, clientId, userId, ipAddress, error);
+ }
+
+ private Event create(long time, EventType event, String realmId, String clientId, String userId, String ipAddress, String error) {
+ Event e = new Event();
+ e.setTime(time);
+ e.setEvent(event);
+ e.setRealmId(realmId);
+ e.setClientId(clientId);
+ e.setUserId(userId);
+ e.setIpAddress(ipAddress);
+ e.setError(error);
+
+ Map<String, String> details = new HashMap<String, String>();
+ details.put("key1", "value1");
+ details.put("key2", "value2");
+
+ e.setDetails(details);
+
+ return e;
+ }
+
+ private void resetSession() {
+ kc.stopSession(session, true);
+ session = kc.startSession();
+ audit = session.getProvider(AuditProvider.class);
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
index d1f7d7d..a76b42a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
@@ -26,6 +26,7 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -51,7 +52,7 @@ public class CompositeImportRoleTest {
@ClassRule
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
@Override
- protected void configure(RealmManager manager, RealmModel adminRealm) {
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
RealmModel realm = manager.createRealm("Test");
RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/testcomposite.json"), RealmRepresentation.class);
manager.importRealm(representation, realm);
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 eaae048..a255f46 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
@@ -28,10 +28,12 @@ import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.managers.ApplicationManager;
import org.keycloak.services.managers.RealmManager;
@@ -56,9 +58,9 @@ public class CompositeRoleTest {
@ClassRule
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
@Override
- protected void configure(RealmManager manager, RealmModel adminRealm) {
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
RealmModel realm = manager.createRealm("Test");
- manager.generateRealmKeys(realm);
+ KeycloakModelUtils.generateRealmKeys(realm);
realmPublicKey = realm.getPublicKey();
realm.setSsoSessionIdleTimeout(3000);
realm.setAccessTokenLifespan(10000);
@@ -75,12 +77,12 @@ public class CompositeRoleTest {
final RoleModel realmComposite1 = realm.addRole("REALM_COMPOSITE_1");
realmComposite1.addCompositeRole(realmRole1);
- final UserModel realmComposite1User = realm.addUser("REALM_COMPOSITE_1_USER");
+ final UserModel realmComposite1User = session.users().addUser(realm, "REALM_COMPOSITE_1_USER");
realmComposite1User.setEnabled(true);
realmComposite1User.updateCredential(UserCredentialModel.password("password"));
realmComposite1User.grantRole(realmComposite1);
- final UserModel realmRole1User = realm.addUser("REALM_ROLE_1_USER");
+ final UserModel realmRole1User = session.users().addUser(realm, "REALM_ROLE_1_USER");
realmRole1User.setEnabled(true);
realmRole1User.updateCredential(UserCredentialModel.password("password"));
realmRole1User.grantRole(realmRole1);
@@ -114,12 +116,12 @@ public class CompositeRoleTest {
final RoleModel realmAppCompositeRole = realm.addRole("REALM_APP_COMPOSITE_ROLE");
realmAppCompositeRole.addCompositeRole(appRole1);
- final UserModel realmAppCompositeUser = realm.addUser("REALM_APP_COMPOSITE_USER");
+ final UserModel realmAppCompositeUser = session.users().addUser(realm, "REALM_APP_COMPOSITE_USER");
realmAppCompositeUser.setEnabled(true);
realmAppCompositeUser.updateCredential(UserCredentialModel.password("password"));
realmAppCompositeUser.grantRole(realmAppCompositeRole);
- final UserModel realmAppRoleUser = realm.addUser("REALM_APP_ROLE_USER");
+ final UserModel realmAppRoleUser = session.users().addUser(realm, "REALM_APP_ROLE_USER");
realmAppRoleUser.setEnabled(true);
realmAppRoleUser.updateCredential(UserCredentialModel.password("password"));
realmAppRoleUser.grantRole(appRole2);
@@ -137,7 +139,7 @@ public class CompositeRoleTest {
appCompositeRole.addCompositeRole(realmRole3);
appCompositeRole.addCompositeRole(appRole1);
- final UserModel appCompositeUser = realm.addUser("APP_COMPOSITE_USER");
+ final UserModel appCompositeUser = session.users().addUser(realm, "APP_COMPOSITE_USER");
appCompositeUser.setEnabled(true);
appCompositeUser.updateCredential(UserCredentialModel.password("password"));
appCompositeUser.grantRole(realmAppCompositeRole);
@@ -182,6 +184,9 @@ public class CompositeRoleTest {
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+
+ AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
+ Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@@ -205,9 +210,10 @@ public class CompositeRoleTest {
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
- }
-
+ AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
+ Assert.assertEquals(200, refreshResponse.getStatusCode());
+ }
@Test
public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
@@ -230,6 +236,9 @@ public class CompositeRoleTest {
Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+
+ AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
+ Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
@@ -252,6 +261,9 @@ public class CompositeRoleTest {
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+
+ AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
+ Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
@@ -274,6 +286,9 @@ public class CompositeRoleTest {
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+
+ AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
+ Assert.assertEquals(200, refreshResponse.getStatusCode());
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
new file mode 100755
index 0000000..2eaaaa5
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -0,0 +1,385 @@
+package org.keycloak.testsuite.exportimport;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.rules.ExternalResource;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runners.MethodSorters;
+import org.keycloak.Config;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.dir.DirExportProvider;
+import org.keycloak.exportimport.dir.DirExportProviderFactory;
+import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
+import org.keycloak.exportimport.zip.ZipExportProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserProvider;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.rule.KeycloakRule;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ExportImportTest {
+
+ private static SystemPropertiesHelper propsHelper = new SystemPropertiesHelper();
+
+ private static final String JPA_CONNECTION_URL = "keycloak.connectionsJpa.url";
+ private static final String JPA_DB_SCHEMA = "keycloak.connectionsJpa.databaseSchema";
+ private static final String MONGO_CLEAR_ON_STARTUP = "keycloak.connectionsMongo.clearOnStartup";
+
+ // We want data to be persisted among server restarts
+ private static ExternalResource persistenceSetupRule = new ExternalResource() {
+
+ private boolean connectionURLSet = false;
+
+ @Override
+ protected void before() throws Throwable {
+ if (System.getProperty(JPA_CONNECTION_URL) == null) {
+ String baseExportImportDir = getExportImportTestDirectory();
+
+ File oldDBFile = new File(baseExportImportDir, "keycloakDB.h2.db");
+ if (oldDBFile.exists()) {
+ oldDBFile.delete();
+ }
+
+ String dbDir = baseExportImportDir + "/keycloakDB";
+ propsHelper.pushProperty(JPA_CONNECTION_URL, "jdbc:h2:file:" + dbDir + ";DB_CLOSE_DELAY=-1");
+ connectionURLSet = true;
+ }
+ propsHelper.pushProperty(JPA_DB_SCHEMA, "create");
+ }
+
+ @Override
+ protected void after() {
+ if (connectionURLSet) {
+ propsHelper.pullProperty(JPA_CONNECTION_URL);
+ }
+ }
+ };
+
+ private static ExternalResource outerPersistenceSetupRule = new ExternalResource() {
+
+ @Override
+ protected void before() throws Throwable {
+ System.setProperty(JPA_DB_SCHEMA, "update");
+ propsHelper.pushProperty(MONGO_CLEAR_ON_STARTUP, "false");
+ }
+
+ @Override
+ protected void after() {
+ propsHelper.pullProperty(JPA_DB_SCHEMA);
+ propsHelper.pullProperty(MONGO_CLEAR_ON_STARTUP);
+ }
+ };
+
+ private static KeycloakRule keycloakRule = new KeycloakRule( new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ addUser(manager.getSession().users(), appRealm, "user1", "password");
+ addUser(manager.getSession().users(), appRealm, "user2", "password");
+ addUser(manager.getSession().users(), appRealm, "user3", "password");
+ addUser(manager.getSession().users(), adminstrationRealm, "admin2", "admin2");
+ }
+
+
+
+ }) {
+ @Override
+ protected void after() {
+ super.after();
+
+ // Clear export/import properties after test
+ Properties systemProps = System.getProperties();
+ Set<String> propsToRemove = new HashSet<String>();
+
+ for (Object key : systemProps.keySet()) {
+ if (key.toString().startsWith(ExportImportConfig.PREFIX)) {
+ propsToRemove.add(key.toString());
+ }
+ }
+
+ for (String propToRemove : propsToRemove) {
+ systemProps.remove(propToRemove);
+ }
+ }
+ };
+
+ @ClassRule
+ public static TestRule chain = RuleChain
+ .outerRule(persistenceSetupRule)
+ .around(keycloakRule)
+ .around(outerPersistenceSetupRule);
+
+ @Test
+ public void testDirFullExportImport() throws Throwable {
+ ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
+ String targetDirPath = getExportImportTestDirectory() + File.separator + "dirExport";
+ DirExportProvider.recursiveDeleteDir(new File(targetDirPath));
+ ExportImportConfig.setDir(targetDirPath);
+ ExportImportConfig.setUsersPerFile(ExportImportConfig.DEFAULT_USERS_PER_FILE);
+
+ testFullExportImport();
+
+ // There should be 4 files in target directory (2 realm, 2 user)
+ Assert.assertEquals(4, new File(targetDirPath).listFiles().length);
+ }
+
+ @Test
+ public void testDirRealmExportImport() throws Throwable {
+ ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
+ String targetDirPath = getExportImportTestDirectory() + File.separator + "dirRealmExport";
+ DirExportProvider.recursiveDeleteDir(new File(targetDirPath));
+ ExportImportConfig.setDir(targetDirPath);
+ ExportImportConfig.setUsersPerFile(3);
+
+ testRealmExportImport();
+
+ // There should be 3 files in target directory (1 realm, 2 user)
+ Assert.assertEquals(3, new File(targetDirPath).listFiles().length);
+ }
+
+ @Test
+ public void testSingleFileFullExportImport() throws Throwable {
+ ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+ String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-full.json";
+ ExportImportConfig.setFile(targetFilePath);
+
+ testFullExportImport();
+ }
+
+ @Test
+ public void testSingleFileRealmExportImport() throws Throwable {
+ ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+ String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
+ ExportImportConfig.setFile(targetFilePath);
+
+ testRealmExportImport();
+ }
+
+ @Test
+ public void testZipFullExportImport() throws Throwable {
+ ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID);
+ String zipFilePath = getExportImportTestDirectory() + File.separator + "export-full.zip";
+ new File(zipFilePath).delete();
+ ExportImportConfig.setZipFile(zipFilePath);
+ ExportImportConfig.setZipPassword("encPassword");
+ ExportImportConfig.setUsersPerFile(ExportImportConfig.DEFAULT_USERS_PER_FILE);
+
+ testFullExportImport();
+ }
+
+ @Test
+ public void testZipRealmExportImport() throws Throwable {
+ ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID);
+ String zipFilePath = getExportImportTestDirectory() + File.separator + "export-realm.zip";
+ new File(zipFilePath).delete();
+ ExportImportConfig.setZipFile(zipFilePath);
+ ExportImportConfig.setZipPassword("encPassword");
+ ExportImportConfig.setUsersPerFile(3);
+
+ testRealmExportImport();
+ }
+
+ private void testFullExportImport() {
+ ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
+ ExportImportConfig.setRealmName(null);
+
+ // Restart server, which triggers export
+ keycloakRule.restartServer();
+
+ // Delete some realm (and some data in admin realm)
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmProvider realmProvider = session.realms();
+ UserProvider userProvider = session.users();
+ new RealmManager(session).removeRealm(realmProvider.getRealmByName("test"));
+ Assert.assertEquals(1, realmProvider.getRealms().size());
+
+ RealmModel master = realmProvider.getRealmByName(Config.getAdminRealm());
+ UserModel admin2 = session.users().getUserByUsername("admin2", master);
+ session.users().removeUser(master, admin2);
+ assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "user1", "password");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "user2", "password");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "user3", "password");
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ // Configure import
+ ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
+
+ // Restart server, which triggers import
+ keycloakRule.restartServer();
+
+ // Ensure data are imported back
+ session = keycloakRule.startSession();
+ try {
+ RealmProvider model = session.realms();
+ UserProvider userProvider = session.users();
+ Assert.assertEquals(2, model.getRealms().size());
+
+ assertAuthenticated(userProvider, model, Config.getAdminRealm(), "admin2", "admin2");
+ assertAuthenticated(userProvider, model, "test", "test-user@localhost", "password");
+ assertAuthenticated(userProvider, model, "test", "user1", "password");
+ assertAuthenticated(userProvider, model, "test", "user2", "password");
+ assertAuthenticated(userProvider, model, "test", "user3", "password");
+
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+ private void testRealmExportImport() {
+ ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
+ ExportImportConfig.setRealmName("test");
+
+ // Restart server, which triggers export
+ keycloakRule.restartServer();
+
+ // Delete some realm (and some data in admin realm)
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmProvider realmProvider = session.realms();
+ UserProvider userProvider = session.users();
+ new RealmManager(session).removeRealm(realmProvider.getRealmByName("test"));
+ Assert.assertEquals(1, realmProvider.getRealms().size());
+
+ RealmModel master = realmProvider.getRealmByName(Config.getAdminRealm());
+ UserModel admin2 = session.users().getUserByUsername("admin2", master);
+ session.users().removeUser(master, admin2);
+
+ assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "user1", "password");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "user2", "password");
+ assertNotAuthenticated(userProvider, realmProvider, "test", "user3", "password");
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ // Configure import
+ ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
+
+ // Restart server, which triggers import
+ keycloakRule.restartServer();
+
+ // Ensure data are imported back, but just for "test" realm
+ session = keycloakRule.startSession();
+ try {
+ RealmProvider realmProvider = session.realms();
+ UserProvider userProvider = session.users();
+ Assert.assertEquals(2, realmProvider.getRealms().size());
+
+ assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
+ assertAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
+ assertAuthenticated(userProvider, realmProvider, "test", "user1", "password");
+ assertAuthenticated(userProvider, realmProvider, "test", "user2", "password");
+ assertAuthenticated(userProvider, realmProvider, "test", "user3", "password");
+
+ addUser(userProvider, realmProvider.getRealmByName(Config.getAdminRealm()), "admin2", "admin2");
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+ private void assertAuthenticated(UserProvider userProvider, RealmProvider realmProvider, String realmName, String username, String password) {
+ RealmModel realm = realmProvider.getRealmByName(realmName);
+ if (realm == null) {
+ Assert.fail("realm " + realmName + " not found");
+ }
+
+ UserModel user = userProvider.getUserByUsername(username, realm);
+ if (user == null) {
+ Assert.fail("user " + username + " not found");
+ }
+
+ Assert.assertTrue(userProvider.validCredentials(realm, user, UserCredentialModel.password(password)));
+ }
+
+ private void assertNotAuthenticated(UserProvider userProvider, RealmProvider realmProvider, String realmName, String username, String password) {
+ RealmModel realm = realmProvider.getRealmByName(realmName);
+ if (realm == null) {
+ return;
+ }
+
+ UserModel user = userProvider.getUserByUsername(username, realm);
+ if (user == null) {
+ return;
+ }
+
+ Assert.assertFalse(userProvider.validCredentials(realm, user, UserCredentialModel.password(password)));
+ }
+
+ private static void addUser(UserProvider userProvider, RealmModel appRealm, String username, String password) {
+ UserModel user = userProvider.addUser(appRealm, username);
+ user.setEmail(username + "@test.com");
+ user.setEnabled(true);
+
+ UserCredentialModel creds = new UserCredentialModel();
+ creds.setType(CredentialRepresentation.PASSWORD);
+ creds.setValue(password);
+ user.updateCredential(creds);
+ }
+
+ private static String getExportImportTestDirectory() {
+ String dirPath = null;
+ String relativeDirExportImportPath = "testsuite" + File.separator + "integration" + File.separator + "target" + File.separator + "export-import";
+
+ if (System.getProperties().containsKey("maven.home")) {
+ dirPath = System.getProperty("user.dir").replaceFirst("testsuite.integration.*", Matcher.quoteReplacement(relativeDirExportImportPath));
+ } else {
+ for (String c : System.getProperty("java.class.path").split(File.pathSeparator)) {
+ if (c.contains(File.separator + "testsuite" + File.separator + "integration")) {
+ dirPath = c.replaceFirst("testsuite.integration.*", Matcher.quoteReplacement(relativeDirExportImportPath));
+ }
+ }
+ }
+
+ String absolutePath = new File(dirPath).getAbsolutePath();
+ return absolutePath;
+ }
+
+ private static class SystemPropertiesHelper {
+
+ private Map<String,String> previousValues = new HashMap<String,String>();
+
+ private void pushProperty(String name, String value) {
+ String currentValue = System.getProperty(name);
+ if (currentValue != null) {
+ previousValues.put(name, currentValue);
+ }
+ System.setProperty(name, value);
+ }
+
+ private void pullProperty(String name) {
+ String prevValue = previousValues.get(name);
+
+ if (prevValue == null) {
+ System.getProperties().remove(name);
+ } else {
+ System.setProperty(name, prevValue);
+ }
+ }
+
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
index e47c714..49ac620 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
@@ -9,8 +9,9 @@ import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runners.MethodSorters;
import org.keycloak.OAuth2Constants;
-import org.keycloak.model.test.LDAPTestUtils;
+import org.keycloak.testsuite.LDAPTestUtils;
import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
@@ -47,8 +48,8 @@ public class AuthProvidersIntegrationTest {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
- addUser(appRealm, "mary", "mary@test.com", "password-app");
- addUser(adminstrationRealm, "mary-admin", "mary@admin.com", "password-admin");
+ addUser(manager.getSession(), appRealm, "mary", "mary@test.com", "password-app");
+ addUser(manager.getSession(), adminstrationRealm, "mary-admin", "mary@admin.com", "password-admin");
AuthenticationProviderModel modelProvider = new AuthenticationProviderModel(AuthProviderConstants.PROVIDER_NAME_MODEL, false, Collections.EMPTY_MAP);
AuthenticationProviderModel picketlinkProvider = new AuthenticationProviderModel(AuthProviderConstants.PROVIDER_NAME_PICKETLINK, true, Collections.EMPTY_MAP);
@@ -95,8 +96,8 @@ public class AuthProvidersIntegrationTest {
@WebResource
protected AccountPasswordPage changePasswordPage;
- private static UserModel addUser(RealmModel realm, String username, String email, String password) {
- UserModel user = realm.addUser(username);
+ private static UserModel addUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
+ UserModel user = session.users().addUser(realm, username);
user.setEmail(email);
user.setEnabled(true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
new file mode 100755
index 0000000..353aae2
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
@@ -0,0 +1,179 @@
+package org.keycloak.testsuite.forms;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runners.MethodSorters;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.testsuite.LDAPEmbeddedServer;
+import org.keycloak.testsuite.LDAPTestUtils;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.pages.AccountPasswordPage;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.RegisterPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.LDAPRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.openqa.selenium.WebDriver;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class FederationProvidersIntegrationTest {
+
+ private static LDAPRule ldapRule = new LDAPRule();
+
+ private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ addUser(manager.getSession(), appRealm, "mary", "mary@test.com", "password-app");
+ addUser(manager.getSession(), adminstrationRealm, "mary-admin", "mary@admin.com", "password-admin");
+
+ LDAPEmbeddedServer ldapServer = ldapRule.getEmbeddedServer();
+ Map<String,String> ldapConfig = new HashMap<String,String>();
+ ldapConfig.put(LDAPConstants.CONNECTION_URL, ldapServer.getConnectionUrl());
+ ldapConfig.put(LDAPConstants.BASE_DN, ldapServer.getBaseDn());
+ ldapConfig.put(LDAPConstants.BIND_DN, ldapServer.getBindDn());
+ ldapConfig.put(LDAPConstants.BIND_CREDENTIAL, ldapServer.getBindCredential());
+ ldapConfig.put(LDAPConstants.USER_DN_SUFFIX, ldapServer.getUserDnSuffix());
+ ldapConfig.put(LDAPConstants.VENDOR, ldapServer.getVendor());
+
+
+ UserFederationProviderModel ldapProvider = new UserFederationProviderModel(null, LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig);
+ appRealm.setUserFederationProviders(Arrays.asList(ldapProvider));
+
+ // Configure LDAP
+ ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
+ LDAPTestUtils.setLdapPassword(session, appRealm, "johnkeycloak", "password");
+ }
+ });
+
+ @ClassRule
+ public static TestRule chain = RuleChain
+ .outerRule(ldapRule)
+ .around(keycloakRule);
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+
+ @WebResource
+ protected OAuthClient oauth;
+
+ @WebResource
+ protected WebDriver driver;
+
+ @WebResource
+ protected AppPage appPage;
+
+ @WebResource
+ protected RegisterPage registerPage;
+
+ @WebResource
+ protected LoginPage loginPage;
+
+ @WebResource
+ protected AccountUpdateProfilePage profilePage;
+
+ @WebResource
+ protected AccountPasswordPage changePasswordPage;
+
+ private static UserModel addUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
+ UserModel user = session.users().addUser(realm, username);
+ user.setEmail(email);
+ user.setEnabled(true);
+
+ UserCredentialModel creds = new UserCredentialModel();
+ creds.setType(CredentialRepresentation.PASSWORD);
+ creds.setValue(password);
+
+ user.updateCredential(creds);
+ return user;
+ }
+
+ @Test
+ public void loginClassic() {
+ loginPage.open();
+ loginPage.login("mary", "password-app");
+
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+ }
+
+ @Test
+ public void loginLdap() {
+ loginPage.open();
+ loginPage.login("johnkeycloak", "password");
+
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+ profilePage.open();
+ Assert.assertEquals("John", profilePage.getFirstName());
+ Assert.assertEquals("Doe", profilePage.getLastName());
+ Assert.assertEquals("john@email.org", profilePage.getEmail());
+ }
+
+ @Test
+ public void passwordChangeLdap() throws Exception {
+ changePasswordPage.open();
+ loginPage.login("johnkeycloak", "password");
+ changePasswordPage.changePassword("password", "new-password", "new-password");
+
+ Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
+
+ changePasswordPage.logout();
+
+ loginPage.open();
+ loginPage.login("johnkeycloak", "password");
+ Assert.assertEquals("Invalid username or password.", loginPage.getError());
+
+ loginPage.open();
+ loginPage.login("johnkeycloak", "new-password");
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ }
+
+ @Test
+ public void registerExistingLdapUser() {
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+
+ registerPage.register("firstName", "lastName", "email", "existing", "password", "password");
+
+ registerPage.assertCurrent();
+ Assert.assertEquals("Username already exists", registerPage.getError());
+ }
+
+ @Test
+ public void registerUserLdapSuccess() {
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+
+ registerPage.register("firstName", "lastName", "email2", "registerUserSuccess2", "password", "password");
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
index 277bc7a..ad0a4c1 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
@@ -51,7 +51,7 @@ public class LoginTest {
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
- UserModel user = appRealm.addUser("login-test");
+ UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
user.setEmail("login@test.com");
user.setEnabled(true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
index eadf367..b680d09 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
@@ -58,7 +58,7 @@ public class LoginTotpTest {
@Override
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
- UserModel user = appRealm.getUser("test-user@localhost");
+ UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
UserCredentialModel credentials = new UserCredentialModel();
credentials.setType(CredentialRepresentation.TOTP);
@@ -128,4 +128,19 @@ public class LoginTotpTest {
events.expectLogin().assertEvent();
}
+ @Test
+ public void loginWithTotpInvalidPassword() throws Exception {
+ loginPage.open();
+ loginPage.login("test-user@localhost", "invalid");
+
+ loginTotpPage.assertCurrent();
+
+ loginTotpPage.login(totp.generate("totpSecret"));
+
+ loginPage.assertCurrent();
+ Assert.assertEquals("Invalid username or password.", loginPage.getError());
+
+ events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).session((String) null).assertEvent();
+ }
+
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index de74d0a..9972c48 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -62,7 +62,7 @@ public class ResetPasswordTest {
public static KeycloakRule keycloakRule = new KeycloakRule((new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
- UserModel user = appRealm.addUser("login-test");
+ UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
user.setEmail("login@test.com");
user.setEnabled(true);
@@ -126,7 +126,7 @@ public class ResetPasswordTest {
resetPasswordPage.assertCurrent();
- events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, username).session((String) null).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+ 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());
@@ -143,16 +143,15 @@ public class ResetPasswordTest {
updatePasswordPage.changePassword("resetPassword", "resetPassword");
- events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session((String) null).detail(Details.USERNAME, username).assertEvent();
+ events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
- Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, username).assertEvent();
- String sessionId = loginEvent.getSessionId();
+ events.expectLogin().user(userId).detail(Details.USERNAME, username).session(sessionId).assertEvent();
oauth.openLogout();
- events.expectLogout(loginEvent.getSessionId()).user(userId).session(sessionId).assertEvent();
+ events.expectLogout(sessionId).user(userId).session(sessionId).assertEvent();
loginPage.open();
@@ -210,7 +209,7 @@ public class ResetPasswordTest {
String body = (String) message.getContent();
String changePasswordUrl = MailUtil.getLink(body);
- events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").session((String) null).assertEvent();
+ String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
driver.navigate().to(changePasswordUrl.trim());
@@ -222,16 +221,15 @@ public class ResetPasswordTest {
updatePasswordPage.changePassword("resetPasswordWithPasswordPolicy", "resetPasswordWithPasswordPolicy");
- events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session((String) null).detail(Details.USERNAME, "login-test").assertEvent();
+ events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, "login-test").assertEvent();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
- Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
- String sessionId = loginEvent.getSessionId();
+ events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").session(sessionId).assertEvent();
oauth.openLogout();
- events.expectLogout(loginEvent.getSessionId()).user(userId).session(sessionId).assertEvent();
+ events.expectLogout(sessionId).user(userId).session(sessionId).assertEvent();
loginPage.open();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java
index 16c9d36..7e9abfc 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java
@@ -58,7 +58,6 @@ public class SSOTest {
@WebResource
protected OAuthClient oauth;
-
@WebResource
protected WebDriver driver;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
new file mode 100755
index 0000000..c089781
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -0,0 +1,218 @@
+package org.keycloak.testsuite.model;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.util.Time;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionProviderTest {
+
+ @ClassRule
+ public static KeycloakRule kc = new KeycloakRule();
+
+ private KeycloakSession session;
+ private RealmModel realm;
+
+ @Before
+ public void before() {
+ session = kc.startSession();
+ realm = session.realms().getRealm("test");
+ session.users().addUser(realm, "user1");
+ session.users().addUser(realm, "user2");
+ }
+
+ @After
+ public void after() {
+ resetSession();
+ session.sessions().removeUserSessions(realm);
+ UserModel user1 = session.users().getUserByUsername("user1", realm);
+ UserModel user2 = session.users().getUserByUsername("user2", realm);
+ session.users().removeUser(realm, user1);
+ session.users().removeUser(realm, user2);
+ kc.stopSession(session, true);
+ }
+
+ @Test
+ public void testCreateSessions() {
+ int started = Time.currentTime();
+ UserSessionModel[] sessions = createSessions();
+
+ assertSession(session.sessions().getUserSession(realm, sessions[0].getId()), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
+ assertSession(session.sessions().getUserSession(realm, sessions[1].getId()), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
+ assertSession(session.sessions().getUserSession(realm, sessions[2].getId()), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, started);
+ }
+
+ @Test
+ public void testGetUserSessions() {
+ UserSessionModel[] sessions = createSessions();
+
+ assertSessions(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)), sessions[0], sessions[1]);
+ assertSessions(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)), sessions[2]);
+ }
+
+ @Test
+ public void testRemoveUserSessionsByUser() {
+ createSessions();
+ session.sessions().removeUserSessions(realm, session.users().getUserByUsername("user1", realm));
+ resetSession();
+
+ assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)).isEmpty());
+ assertFalse(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
+ }
+
+ @Test
+ public void testRemoveUserSessionsByRealm() {
+ createSessions();
+ session.sessions().removeUserSessions(realm);
+ resetSession();
+
+ assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)).isEmpty());
+ assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
+ }
+
+ @Test
+ public void testRemoveUserSessionsByExpired() {
+ UserSessionModel[] sessions = createSessions();
+
+ session.sessions().getUserSession(realm, sessions[0].getId()).setStarted(Time.currentTime() - realm.getSsoSessionMaxLifespan() - 1);
+ session.sessions().getUserSession(realm, sessions[1].getId()).setLastSessionRefresh(Time.currentTime() - realm.getSsoSessionIdleTimeout() - 1);
+
+ resetSession();
+
+ session.sessions().removeExpiredUserSessions(realm);
+ resetSession();
+
+ assertNull(session.sessions().getUserSession(realm, sessions[0].getId()));
+ assertNull(session.sessions().getUserSession(realm, sessions[1].getId()));
+ assertNotNull(session.sessions().getUserSession(realm, sessions[2].getId()));
+ }
+
+ @Test
+ public void testGetByClient() {
+ UserSessionModel[] sessions = createSessions();
+
+ assertSessions(session.sessions().getUserSessions(realm, realm.findClient("test-app")), sessions[0], sessions[1]);
+ assertSessions(session.sessions().getUserSessions(realm, realm.findClient("third-party")), sessions[0]);
+ }
+
+ @Test
+ public void testGetByClientPaginated() {
+ for (int i = 0; i < 25; i++) {
+ UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false);
+ userSession.setStarted(Time.currentTime() + i);
+ userSession.associateClient(realm.findClient("test-app"));
+ }
+
+ resetSession();
+
+ assertPaginatedSession(realm, realm.findClient("test-app"), 0, 1, 1);
+ assertPaginatedSession(realm, realm.findClient("test-app"), 0, 10, 10);
+ assertPaginatedSession(realm, realm.findClient("test-app"), 10, 10, 10);
+ assertPaginatedSession(realm, realm.findClient("test-app"), 20, 10, 5);
+ assertPaginatedSession(realm, realm.findClient("test-app"), 30, 10, 0);
+ }
+
+ private void assertPaginatedSession(RealmModel realm, ClientModel client, int start, int max, int expectedSize) {
+ List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, client, start, max);
+ String[] actualIps = new String[sessions.size()];
+ for (int i = 0; i < actualIps.length; i++) {
+ actualIps[i] = sessions.get(i).getIpAddress();
+ }
+
+ String[] expectedIps = new String[expectedSize];
+ for (int i = 0; i < expectedSize; i++) {
+ expectedIps[i] = "127.0.0." + (i + start);
+ }
+
+ assertArrayEquals(expectedIps, actualIps);
+ }
+
+
+
+ @Test
+ public void testGetCountByClient() {
+ createSessions();
+
+ assertEquals(2, session.sessions().getActiveUserSessions(realm, realm.findClient("test-app")));
+ assertEquals(1, session.sessions().getActiveUserSessions(realm, realm.findClient("third-party")));
+ }
+
+ private UserSessionModel[] createSessions() {
+ UserSessionModel[] sessions = new UserSessionModel[4];
+ sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true);
+ sessions[0].associateClient(realm.findClient("test-app"));
+ sessions[0].associateClient(realm.findClient("third-party"));
+
+ sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true);
+ sessions[1].associateClient(realm.findClient("test-app"));
+
+ sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true);
+
+ resetSession();
+
+ return sessions;
+ }
+
+ private void resetSession() {
+ kc.stopSession(session, true);
+ session = kc.startSession();
+ realm = session.realms().getRealm("test");
+ }
+
+ public void assertSessions(List<UserSessionModel> actualSessions, UserSessionModel... expectedSessions) {
+ String[] expected = new String[expectedSessions.length];
+ for (int i = 0; i < expected.length; i++) {
+ expected[i] = expectedSessions[i].getId();
+ }
+
+ String[] actual = new String[actualSessions.size()];
+ for (int i = 0; i < actual.length; i++) {
+ actual[i] = actualSessions.get(i).getId();
+ }
+
+ Arrays.sort(expected);
+ Arrays.sort(actual);
+
+ assertArrayEquals(expected, actual);
+ }
+
+ public void assertSession(UserSessionModel session, UserModel user, String ipAddress, int started, int lastRefresh, String... clients) {
+ assertEquals(user.getId(), session.getUser().getId());
+ assertEquals(ipAddress, session.getIpAddress());
+ assertEquals(user.getUsername(), session.getLoginUsername());
+ assertEquals("form", session.getAuthMethod());
+ assertEquals(true, session.isRememberMe());
+ assertTrue(session.getStarted() >= started - 1 && session.getStarted() <= started + 1);
+ assertTrue(session.getLastSessionRefresh() >= lastRefresh - 1 && session.getLastSessionRefresh() <= lastRefresh + 1);
+
+ String[] actualClients = new String[session.getClientAssociations().size()];
+ for (int i = 0; i < actualClients.length; i++) {
+ actualClients[i] = session.getClientAssociations().get(i).getClientId();
+ }
+
+ Arrays.sort(clients);
+ Arrays.sort(actualClients);
+
+ assertArrayEquals(clients, actualClients);
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
index 03b9086..947551e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
@@ -91,7 +91,9 @@ public class OAuthGrantTest {
OAuthClient.AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
- AccessToken token = oauth.verifyToken(accessToken.getAccessToken());
+ String tokenString = accessToken.getAccessToken();
+ Assert.assertNotNull(tokenString);
+ AccessToken token = oauth.verifyToken(tokenString);
assertEquals(sessionId, token.getSessionState());
AccessToken.Access realmAccess = token.getRealmAccess();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
index 43f386e..a0203b0 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
@@ -183,8 +183,8 @@ public class RefreshTokenTest {
String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
KeycloakSession session = keycloakRule.startSession();
- RealmModel realm = session.getRealmByName("test");
- UserSessionModel userSession = realm.getUserSession(sessionId);
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
int last = userSession.getLastSessionRefresh();
session.getTransaction().commit();
session.close();
@@ -199,8 +199,8 @@ public class RefreshTokenTest {
Assert.assertEquals(200, tokenResponse.getStatusCode());
session = keycloakRule.startSession();
- realm = session.getRealmByName("test");
- userSession = realm.getUserSession(sessionId);
+ realm = session.realms().getRealmByName("test");
+ userSession = session.sessions().getUserSession(realm, sessionId);
int next = userSession.getLastSessionRefresh();
session.getTransaction().commit();
session.close();
@@ -211,7 +211,7 @@ public class RefreshTokenTest {
session = keycloakRule.startSession();
- realm = session.getRealmByName("test");
+ realm = session.realms().getRealmByName("test");
int lastAccessTokenLifespan = realm.getAccessTokenLifespan();
realm.setAccessTokenLifespan(100000);
session.getTransaction().commit();
@@ -221,8 +221,8 @@ public class RefreshTokenTest {
tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
session = keycloakRule.startSession();
- realm = session.getRealmByName("test");
- userSession = realm.getUserSession(sessionId);
+ realm = session.realms().getRealmByName("test");
+ userSession = session.sessions().getUserSession(realm, sessionId);
next = userSession.getLastSessionRefresh();
session.getTransaction().commit();
session.close();
@@ -231,7 +231,7 @@ public class RefreshTokenTest {
Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 6)));
session = keycloakRule.startSession();
- realm = session.getRealmByName("test");
+ realm = session.realms().getRealmByName("test");
int originalIdle = realm.getSsoSessionIdleTimeout();
realm.setSsoSessionIdleTimeout(1);
session.getTransaction().commit();
@@ -249,7 +249,7 @@ public class RefreshTokenTest {
events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN);
session = keycloakRule.startSession();
- realm = session.getRealmByName("test");
+ realm = session.realms().getRealmByName("test");
realm.setSsoSessionIdleTimeout(originalIdle);
realm.setAccessTokenLifespan(lastAccessTokenLifespan);
session.getTransaction().commit();
@@ -274,7 +274,7 @@ public class RefreshTokenTest {
String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
KeycloakSession session = keycloakRule.startSession();
- RealmModel realm = session.getRealmByName("test");
+ RealmModel realm = session.realms().getRealmByName("test");
int maxLifespan = realm.getSsoSessionMaxLifespan();
realm.setSsoSessionMaxLifespan(1);
session.getTransaction().commit();
@@ -289,7 +289,7 @@ public class RefreshTokenTest {
assertNull(tokenResponse.getRefreshToken());
session = keycloakRule.startSession();
- realm = session.getRealmByName("test");
+ realm = session.realms().getRealmByName("test");
realm.setSsoSessionMaxLifespan(maxLifespan);
session.getTransaction().commit();
session.close();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index 1a4bf74..4d7c211 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -21,7 +21,6 @@
*/
package org.keycloak.testsuite;
-import net.iharder.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
@@ -32,21 +31,18 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
-import org.jboss.resteasy.security.PemUtils;
import org.json.JSONObject;
import org.junit.Assert;
import org.keycloak.OAuth2Constants;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
-import org.keycloak.audit.Details;
-import org.keycloak.audit.Event;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.AccessToken;
-import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.RefreshToken;
import org.keycloak.services.resources.TokenService;
import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.PemUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index 2db2269..da334fa 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -8,12 +8,14 @@ import io.undertow.servlet.api.WebResourceCollection;
import org.junit.rules.ExternalResource;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.Retry;
import org.keycloak.testutils.KeycloakServer;
import org.keycloak.util.JsonSerialization;
@@ -21,6 +23,7 @@ import javax.servlet.Servlet;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.Socket;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -40,8 +43,11 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
KeycloakSession session = server.getSessionFactory().create();
session.getTransaction().begin();
try {
- UserModel user = session.getRealmByName(realm).getUser(name);
- return user != null ? ModelToRepresentation.toRepresentation(user) : null;
+ RealmModel realmByName = session.realms().getRealmByName(realm);
+ UserModel user = session.users().getUserByUsername(name, realmByName);
+ UserRepresentation userRep = user != null ? ModelToRepresentation.toRepresentation(user) : null;
+ session.getTransaction().commit();
+ return userRep;
} finally {
session.close();
}
@@ -51,7 +57,10 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
KeycloakSession session = server.getSessionFactory().create();
session.getTransaction().begin();
try {
- return ModelToRepresentation.toRepresentation(session.getRealmByName(realm).getUserById(id));
+ RealmModel realmByName = session.realms().getRealmByName(realm);
+ UserRepresentation userRep = ModelToRepresentation.toRepresentation(session.users().getUserById(id, realmByName));
+ session.getTransaction().commit();
+ return userRep;
} finally {
session.close();
}
@@ -66,7 +75,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
RealmModel adminstrationRealm = manager.getRealm(Config.getAdminRealm());
- configure(manager, adminstrationRealm);
+ configure(session, manager, adminstrationRealm);
session.getTransaction().commit();
} finally {
@@ -74,7 +83,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
}
}
- protected void configure(RealmManager manager, RealmModel adminRealm) {
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
}
@@ -112,15 +121,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
@Override
protected void after() {
- server.stop();
-
- // Add some variable delay (Some windows envs have issues as server is not stopped immediately after server.stop)
- try {
- int sleepInterval = Integer.parseInt(System.getProperty("testsuite.delay", "0"));
- Thread.sleep(sleepInterval);
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- }
+ stopServer();
}
public RealmRepresentation loadJson(String path) throws IOException {
@@ -146,4 +147,36 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
}
session.close();
}
+
+ public void restartServer() {
+ try {
+ stopServer();
+ server.start();
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ private void stopServer() {
+ server.stop();
+
+ // Add some variable delay (Some windows envs have issues as server is not stopped immediately after server.stop)
+ try {
+ Retry.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ Socket s = new Socket(server.getConfig().getHost(), server.getConfig().getPort());
+ s.close();
+ throw new IllegalStateException("Server still running");
+ } catch (IOException expected) {
+ }
+ }
+
+ }, 10, 500);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
index d502c6e..1e30a4b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
@@ -100,10 +100,10 @@ public class KeycloakRule extends AbstractKeycloakRule {
public void removeUserSession(String sessionId) {
KeycloakSession session = startSession();
- RealmModel realm = session.getRealm("test");
- UserSessionModel userSession = realm.getUserSession(sessionId);
+ RealmModel realm = session.realms().getRealm("test");
+ UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
assertNotNull(userSession);
- realm.removeUserSession(userSession);
+ session.sessions().removeUserSession(realm, userSession);
stopSession(session, true);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
index 3409b0e..b2b4aae 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
@@ -1,7 +1,7 @@
package org.keycloak.testsuite.rule;
import org.junit.rules.ExternalResource;
-import org.keycloak.model.test.LDAPEmbeddedServer;
+import org.keycloak.testsuite.LDAPEmbeddedServer;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm.json b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
index d27b3ec..68b3c6d 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
@@ -22,7 +22,11 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "account": [ "manage-account" ]
+ }
}
],
"roles" : {
@@ -37,12 +41,6 @@
}
]
},
- "roleMappings": [
- {
- "username": "bburke@redhat.com",
- "roles": ["user"]
- }
- ],
"scopeMappings": [
{
"client": "third-party",
@@ -109,14 +107,5 @@
],
"secret": "password"
}
- ],
- "applicationRoleMappings": {
- "account": [
- {
- "username": "bburke@redhat.com",
- "roles": ["manage-account"]
- }
- ]
- }
-
+ ]
}
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json b/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json
index 80132e0..c1855c6 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json
@@ -21,7 +21,11 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "account": [ "manage-account" ]
+ }
}
],
"roles" : {
@@ -36,12 +40,6 @@
}
]
},
- "roleMappings": [
- {
- "username": "bburke@redhat.com",
- "roles": ["user"]
- }
- ],
"scopeMappings": [
{
"client": "third-party",
@@ -107,14 +105,5 @@
],
"secret": "password"
}
- ],
- "applicationRoleMappings": {
- "account": [
- {
- "username": "bburke@redhat.com",
- "roles": ["manage-account"]
- }
- ]
- }
-
+ ]
}
diff --git a/testsuite/integration/src/test/resources/admin-test/testrealm.json b/testsuite/integration/src/test/resources/admin-test/testrealm.json
new file mode 100755
index 0000000..2af419a
--- /dev/null
+++ b/testsuite/integration/src/test/resources/admin-test/testrealm.json
@@ -0,0 +1,98 @@
+{
+ "realm": "admin-test-1",
+ "enabled": true,
+ "sslNotRequired": true,
+ "registrationAllowed": true,
+ "resetPasswordAllowed": true,
+ "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" ],
+ "defaultRoles": [ "user" ],
+ "smtpServer": {
+ "from": "auto@keycloak.org",
+ "host": "localhost",
+ "port":"3025"
+ },
+ "users" : [
+ {
+ "username" : "test-user@localhost",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["user"],
+ "applicationRoles": {
+ "test-app": [ "customer-user" ],
+ "account": [ "view-profile", "manage-account" ]
+ }
+ }
+ ],
+ "oauthClients" : [
+ {
+ "name" : "third-party",
+ "enabled": true,
+ "redirectUris": [
+ "http://localhost:8081/app/*"
+ ],
+ "secret": "password"
+ }
+ ],
+ "scopeMappings": [
+ {
+ "client": "third-party",
+ "roles": ["user"]
+ },
+ {
+ "client": "test-app",
+ "roles": ["user"]
+ }
+ ],
+ "applications": [
+ {
+ "name": "test-app",
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "redirectUris": [
+ "http://localhost:8081/app/*"
+ ],
+ "adminUrl": "http://localhost:8081/app/logout",
+ "secret": "password"
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "Have User privileges"
+ },
+ {
+ "name": "admin",
+ "description": "Have Administrator privileges"
+ }
+ ],
+ "application" : {
+ "test-app" : [
+ {
+ "name": "customer-user",
+ "description": "Have Customer User privileges"
+ },
+ {
+ "name": "customer-admin",
+ "description": "Have Customer Admin privileges"
+ }
+ ]
+ }
+
+ },
+
+ "applicationScopeMappings": {
+ "test-app": [
+ {
+ "client": "third-party",
+ "roles": ["customer-user"]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/testcomposite.json b/testsuite/integration/src/test/resources/testcomposite.json
index 6e01de3..4b5e4c5 100755
--- a/testsuite/integration/src/test/resources/testcomposite.json
+++ b/testsuite/integration/src/test/resources/testcomposite.json
@@ -22,7 +22,8 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": [ "REALM_COMPOSITE_1" ]
},
{
"username" : "REALM_ROLE_1_USER",
@@ -31,7 +32,8 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": ["REALM_ROLE_1"]
},
{
"username" : "REALM_APP_COMPOSITE_USER",
@@ -40,7 +42,8 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": ["REALM_APP_COMPOSITE_ROLE"]
},
{
"username" : "REALM_APP_ROLE_USER",
@@ -49,7 +52,10 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "applicationRoles": {
+ "APP_ROLE_APPLICATION": [ "APP_ROLE_2" ]
+ }
},
{
"username" : "APP_COMPOSITE_USER",
@@ -58,7 +64,8 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": ["REALM_APP_COMPOSITE_ROLE", "REALM_COMPOSITE_1"]
}
],
"oauthClients" : [
@@ -68,24 +75,6 @@
"secret": "password"
}
],
- "roleMappings": [
- {
- "username": "REALM_COMPOSITE_1_USER",
- "roles": ["REALM_COMPOSITE_1"]
- },
- {
- "username": "REALM_ROLE_1_USER",
- "roles": ["REALM_ROLE_1"]
- },
- {
- "username": "REALM_APP_COMPOSITE_USER",
- "roles": ["REALM_APP_COMPOSITE_ROLE"]
- },
- {
- "username": "APP_COMPOSITE_USER",
- "roles": ["REALM_APP_COMPOSITE_ROLE", "REALM_COMPOSITE_1"]
- }
- ],
"scopeMappings": [
{
"client": "REALM_COMPOSITE_1_APPLICATION",
@@ -199,14 +188,6 @@
},
- "applicationRoleMappings": {
- "APP_ROLE_APPLICATION": [
- {
- "username": "REALM_APP_ROLE_USER",
- "roles": ["APP_ROLE_2"]
- }
- ]
- },
"applicationScopeMappings": {
"APP_ROLE_APPLICATION": [
{
diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json
index 8c889e0..b937db3 100755
--- a/testsuite/integration/src/test/resources/testrealm.json
+++ b/testsuite/integration/src/test/resources/testrealm.json
@@ -21,8 +21,13 @@
"email" : "test-user@localhost",
"credentials" : [
{ "type" : "password",
- "value" : "password" }
- ]
+ "value" : "password" }
+ ],
+ "realmRoles": ["user"],
+ "applicationRoles": {
+ "test-app": [ "customer-user" ],
+ "account": [ "view-profile", "manage-account" ]
+ }
}
],
"oauthClients" : [
@@ -35,12 +40,6 @@
"secret": "password"
}
],
- "roleMappings": [
- {
- "username": "test-user@localhost",
- "roles": ["user"]
- }
- ],
"scopeMappings": [
{
"client": "third-party",
@@ -89,14 +88,6 @@
},
- "applicationRoleMappings": {
- "test-app": [
- {
- "username": "test-user@localhost",
- "roles": ["customer-user"]
- }
- ]
- },
"applicationScopeMappings": {
"test-app": [
{
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java
index ed5c98e..ab6dcbe 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java
@@ -40,7 +40,7 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
try {
String adminRealmName = Config.getAdminRealm();
- if (keycloakSession.getRealm(adminRealmName) == null) {
+ if (keycloakSession.realms().getRealm(adminRealmName) == null) {
RealmManager manager = new RealmManager(keycloakSession);
manager.setContextPath(contextPath);
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java
index 8deadb7..14a106c 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java
@@ -6,6 +6,7 @@ import org.apache.log.Logger;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
@@ -77,9 +78,9 @@ public class CreateRealmsWorker implements Worker {
// Add required credentials
if (createRequiredCredentials) {
- realmManager.addRequiredCredential(realm, CredentialRepresentation.PASSWORD);
- realmManager.addRequiredCredential(realm, CredentialRepresentation.TOTP);
- realmManager.addRequiredCredential(realm, CredentialRepresentation.CLIENT_CERT);
+ RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.PASSWORD);
+ RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.TOTP);
+ RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.CLIENT_CERT);
}
log.info("Finished creation of realm " + realmName);
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java
index ce2f08a..48e5672 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java
@@ -5,7 +5,6 @@ import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
@@ -59,7 +58,7 @@ public class CreateUsersWorker implements Worker {
@Override
public void run(SampleResult result, KeycloakSession session) {
// We need to obtain realm first
- RealmModel realm = session.getRealm(realmId);
+ RealmModel realm = session.realms().getRealm(realmId);
if (realm == null) {
throw new IllegalStateException("Realm '" + realmId + "' not found");
}
@@ -69,7 +68,7 @@ public class CreateUsersWorker implements Worker {
String username = PerfTestUtils.getUsername(userNumber);
- UserModel user = realm.addUser(username);
+ UserModel user = session.users().addUser(realm, username);
// Add basic user attributes (NOTE: Actually backend is automatically upgraded during each setter call)
if (addBasicUserAttributes) {
@@ -98,7 +97,7 @@ public class CreateUsersWorker implements Worker {
}
SocialLinkModel socialLink = new SocialLinkModel(socialProvider, username, username);
- realm.addSocialLink(user, socialLink);
+ session.users().addSocialLink(realm, user, socialLink);
}
log.info("Finished creation of user " + username + " in realm: " + realm.getId());
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
index 35616ac..d9c36d5 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
@@ -3,10 +3,10 @@ package org.keycloak.testsuite.performance;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
-import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import java.util.concurrent.atomic.AtomicInteger;
@@ -68,7 +68,7 @@ public class ReadUsersWorker implements Worker {
@Override
public void run(SampleResult result, KeycloakSession session) {
// We need to obtain realm first
- RealmModel realm = session.getRealm(realmId);
+ RealmModel realm = session.realms().getRealm(realmId);
if (realm == null) {
throw new IllegalStateException("Realm '" + realmId + "' not found");
}
@@ -87,7 +87,7 @@ public class ReadUsersWorker implements Worker {
String username = PerfTestUtils.getUsername(userCounterInRealm);
lastUsername = username;
- UserModel user = realm.getUser(username);
+ UserModel user = session.users().getUserByUsername(username, realm);
// Read roles of user in realm
if (readRoles) {
@@ -102,18 +102,18 @@ public class ReadUsersWorker implements Worker {
// Validate password (shoould be same as username)
if (readPassword) {
- realm.validatePassword(user, username);
+ session.users().validCredentials(realm, user, UserCredentialModel.password(username));
}
// Read socialLinks of user
if (readSocialLinks) {
- realm.getSocialLinks(user);
+ session.users().getSocialLinks(user, realm);
}
// Try to search by social links
if (searchBySocialLinks) {
SocialLinkModel socialLink = new SocialLinkModel("facebook", username, username);
- realm.getUserBySocialLink(socialLink);
+ session.users().getUserBySocialLink(socialLink, realm);
}
}
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java
index 20f0fd6..915ded9 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java
@@ -32,7 +32,7 @@ public class RemoveUsersWorker implements Worker {
int realmNumber = realmsOffset + workerId;
String realmId = PerfTestUtils.getRealmName(realmNumber);
- realm = session.getRealm(realmId);
+ realm = session.realms().getRealm(realmId);
if (realm == null) {
throw new IllegalStateException("Realm '" + realmId + "' not found");
}
testsuite/performance-web/pom.xml 4(+2 -2)
diff --git a/testsuite/performance-web/pom.xml b/testsuite/performance-web/pom.xml
index e2d61db..b9fee7c 100644
--- a/testsuite/performance-web/pom.xml
+++ b/testsuite/performance-web/pom.xml
@@ -216,11 +216,11 @@
<preserveDirectories>false</preserveDirectories>
<requestGroups>
- <requestGroup>
+ <requestGroup implementation="com.lazerycode.jmeter.analyzer.config.RequestGroup">
<name>aggregatedRequests</name>
<pattern>* request</pattern>
</requestGroup>
- <requestGroup>
+ <requestGroup implementation="com.lazerycode.jmeter.analyzer.config.RequestGroup">
<name>codes</name>
<pattern>**/perf-app/perf-servlet?code=*</pattern>
</requestGroup>
testsuite/performance-web/README.md 14(+12 -2)
diff --git a/testsuite/performance-web/README.md b/testsuite/performance-web/README.md
index 950e90c..7546378 100644
--- a/testsuite/performance-web/README.md
+++ b/testsuite/performance-web/README.md
@@ -53,10 +53,20 @@ http://localhost:8081/keycloak-tools/perf/perf-realm/get-users-count?prefix=user
For adding 10000 new users into your database (will start from last added user, so you don't need to explicitly check how many users to create are needed:
```shell
-http://localhost:8081/keycloak-tools/perf/perf-realm/create-available-users?prefix=user&count=10000&batch=100&roles=user
+http://localhost:8081/keycloak-tools/perf/perf-realm/create-available-users?prefix=user&count=10000&batch=100&async=true&roles=role-0,role-1
````
-Seeing progress of job for creating users
+For update role mappings of all users:
+```shell
+http://localhost:8081/keycloak-tools/perf/perf-realm/update-all-users?prefix=user&async=true&roles=role-3,perf-app:approle-3,perf-app:approle-4
+````
+
+For deleting all users:
+```shell
+http://localhost:8081/keycloak-tools/perf/perf-realm/delete-all-users?prefix=user
+````
+
+Seeing progress of job for creating/updating/deleting users
```shell
http://localhost:8081/keycloak-tools/perf/jobs
````
diff --git a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java
index 5dde02e..cb9e239 100644
--- a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java
+++ b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java
@@ -85,6 +85,7 @@ public class KeycloakPerfServer {
ServletInfo servlet = new ServletInfo("PerfAppServlet", PerfAppServlet.class);
servlet.addMapping("/perf-servlet/*");
+ servlet.addInitParam(PerfAppServlet.BASE_URL_INIT_PARAM, "http://" + keycloakServer.getConfig().getHost() + ":" + keycloakServer.getConfig().getPort());
deploymentInfo.addServlet(servlet);
diff --git a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java
index bb6e0c1..40409ad 100644
--- a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java
+++ b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java
@@ -42,7 +42,7 @@ import org.keycloak.util.BasicAuthHelper;
*/
public class OAuthClient {
- private String baseUrl = "http://localhost:8081/auth";
+ private String baseUrl;
private String realm = "perf-realm";
@@ -52,16 +52,19 @@ public class OAuthClient {
private String clientId = "perf-app";
- private String redirectUri = "http://localhost:8081/perf-app/perf-servlet";
+ private String redirectUri;
private String state = "123";
private PublicKey realmPublicKey;
- public OAuthClient() {
+ public OAuthClient(String baseUrl) {
try {
JSONObject realmJson = new JSONObject(IOUtils.toString(getClass().getResourceAsStream("/perfrealm.json")));
realmPublicKey = PemUtils.decodePublicKey(realmJson.getString("publicKey"));
+
+ this.baseUrl = (baseUrl != null) ? baseUrl + "/auth" : "http://localhost:8081/auth";
+ this.redirectUri = baseUrl + "/perf-app/perf-servlet";
} catch (Exception e) {
throw new RuntimeException("Failed to retrieve realm public key", e);
}
diff --git a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java
index 52cf1fa..dbab2da 100644
--- a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java
+++ b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java
@@ -25,6 +25,8 @@ import org.keycloak.util.Time;
*/
public class PerfAppServlet extends HttpServlet {
+ public static final String BASE_URL_INIT_PARAM = "baseUrl";
+
private Template indexTemplate;
private OAuthClient oauthClient;
@@ -35,7 +37,8 @@ public class PerfAppServlet extends HttpServlet {
cfg.setTemplateLoader(new ClassTemplateLoader(getClass(), "/"));
indexTemplate = cfg.getTemplate("perf-app-resources/index.ftl");
- oauthClient = new OAuthClient();
+ String baseUrl = getInitParameter(BASE_URL_INIT_PARAM);
+ oauthClient = new OAuthClient(baseUrl);
} catch (IOException ioe) {
throw new ServletException(ioe);
}
diff --git a/testsuite/performance-web/src/main/resources/perfrealm.json b/testsuite/performance-web/src/main/resources/perfrealm.json
index 1fab9a5..dc4c959 100644
--- a/testsuite/performance-web/src/main/resources/perfrealm.json
+++ b/testsuite/performance-web/src/main/resources/perfrealm.json
@@ -8,7 +8,6 @@
"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" ],
- "defaultRoles": [ "user" ],
"smtpServer": {
"from": "auto@keycloak.org",
"host": "localhost",
@@ -22,7 +21,12 @@
"credentials" : [
{ "type" : "password",
"value" : "password" }
- ]
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "perf-app": [ "customer-user" ],
+ "account": [ "view-account", "manage-account" ]
+ }
}
],
"oauthClients" : [
@@ -30,17 +34,11 @@
"name" : "third-party",
"enabled": true,
"redirectUris": [
- "http://localhost:8081/app/*"
+ "/app/*"
],
"secret": "password"
}
],
- "roleMappings": [
- {
- "username": "test@localhost",
- "roles": ["user"]
- }
- ],
"scopeMappings": [
{
"client": "third-party",
@@ -48,55 +46,61 @@
},
{
"client": "perf-app",
- "roles": ["user"]
+ "roles": [ "role-0", "role-1", "role-2", "role-3", "role-4" ]
}
],
"applications": [
{
"name": "perf-app",
"enabled": true,
- "baseUrl": "http://localhost:8081/perf-app",
+ "baseUrl": "/perf-app",
"redirectUris": [
- "http://localhost:8081/perf-app/*"
+ "/perf-app/*"
],
- "adminUrl": "http://localhost:8081/perf-app/perf-servlet",
+ "adminUrl": "/perf-app/perf-servlet",
"secret": "password"
}
],
"roles" : {
"realm" : [
{
- "name": "user",
- "description": "Have User privileges"
+ "name": "role-0"
+ },
+ {
+ "name": "role-1"
+ },
+ {
+ "name": "role-2"
+ },
+ {
+ "name": "role-3"
},
{
- "name": "admin",
- "description": "Have Administrator privileges"
+ "name": "role-4"
}
],
"application" : {
"perf-app" : [
{
- "name": "customer-user",
- "description": "Have Customer User privileges"
+ "name": "approle-0"
+ },
+ {
+ "name": "approle-1"
+ },
+ {
+ "name": "approle-2"
+ },
+ {
+ "name": "approle-3"
},
{
- "name": "customer-admin",
- "description": "Have Customer Admin privileges"
+ "name": "approle-4"
}
]
}
},
- "applicationRoleMappings": {
- "perf-app": [
- {
- "username": "test@localhost",
- "roles": ["customer-user"]
- }
- ]
- },
"applicationScopeMappings": {
"perf-app": [
{
diff --git a/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx b/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx
index 07074aa..07f16ef 100644
--- a/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx
+++ b/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<jmeterTestPlan version="1.2" properties="2.6" jmeter="2.11 r1554548">
+<jmeterTestPlan version="1.2" properties="2.5" jmeter="2.10 r1533061">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
@@ -166,7 +166,7 @@
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
- <stringProp name="HTTPSampler.path">/auth/realms/perf-realm/tokens/auth/request/login?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8081%2Fperf-app%2Fperf-servlet&state=123&client_id=perf-app</stringProp>
+ <stringProp name="HTTPSampler.path">/auth/realms/perf-realm/tokens/auth/request/login?response_type=code&redirect_uri=http%3A%2F%2F${host}%3A${port}%2Fperf-app%2Fperf-servlet&state=123&client_id=perf-app</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
@@ -322,6 +322,32 @@
<bytes>true</bytes>
</value>
</objProp>
+ <objProp>
+ <value class="SampleSaveConfiguration">
+ <time>true</time>
+ <latency>true</latency>
+ <timestamp>true</timestamp>
+ <success>true</success>
+ <label>true</label>
+ <code>true</code>
+ <message>true</message>
+ <threadName>true</threadName>
+ <dataType>true</dataType>
+ <encoding>false</encoding>
+ <assertions>true</assertions>
+ <subresults>true</subresults>
+ <responseData>false</responseData>
+ <samplerData>false</samplerData>
+ <xml>false</xml>
+ <fieldNames>false</fieldNames>
+ <responseHeaders>false</responseHeaders>
+ <requestHeaders>false</requestHeaders>
+ <responseDataOnError>false</responseDataOnError>
+ <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+ <assertionsResultsToSave>0</assertionsResultsToSave>
+ <bytes>true</bytes>
+ </value>
+ </objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
testsuite/tools/pom.xml 275(+16 -259)
diff --git a/testsuite/tools/pom.xml b/testsuite/tools/pom.xml
index c630624..aea718f 100755
--- a/testsuite/tools/pom.xml
+++ b/testsuite/tools/pom.xml
@@ -15,282 +15,39 @@
<description/>
<dependencies>
-
- <dependency>
- <groupId>com.icegreen</groupId>
- <artifactId>greenmail</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
-
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>net.iharder</groupId>
- <artifactId>base64</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-services</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.zxing</groupId>
- <artifactId>javase</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-invalidation-cache-model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-jboss-logging</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-email</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- social -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-github</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-google</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-twitter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.twitter4j</groupId>
- <artifactId>twitter4j-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-facebook</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- forms -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.freemarker</groupId>
- <artifactId>freemarker</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-forms-common-themes</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-account-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-email-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-login-freemarker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-js-adapter</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- authentication api -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-model</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-authentication-picketlink</artifactId>
+ <artifactId>keycloak-dependencies-server-all</artifactId>
<version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-idm-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-idm-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-idm-simple-schema</artifactId>
+ <type>pom</type>
</dependency>
- <!-- timer -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-timer-basic</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <!-- picketlink -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-picketlink-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-picketlink-realm</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.jboss.spec.javax.servlet</groupId>
- <artifactId>jboss-servlet-api_3.0_spec</artifactId>
- <scope>provided</scope>
- </dependency>
- <!-- resteasy -->
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-multipart-provider</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>async-http-servlet-3.0</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
+ <version>${resteasy.version.latest}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jackson-provider</artifactId>
- <version>${resteasy.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <!-- Mongo dependencies -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-mongo</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-audit-mongo</artifactId>
- <version>${project.version}</version>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <version>${resteasy.version.latest}</version>
</dependency>
<dependency>
- <groupId>org.mongodb</groupId>
- <artifactId>mongo-java-driver</artifactId>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
</dependency>
- <!-- export/import -->
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-api</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-export-import-impl</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>de.idyl</groupId>
- <artifactId>winzipaes</artifactId>
+ <groupId>com.icegreen</groupId>
+ <artifactId>greenmail</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
-
</dependencies>
<build>
diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsersJob.java
new file mode 100755
index 0000000..32599b5
--- /dev/null
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsersJob.java
@@ -0,0 +1,50 @@
+package org.keycloak.test.tools.jobs;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class CreateUsersJob extends UsersJob {
+
+ private String[] roles;
+
+ public CreateUsersJob(String[] roles) {
+ this.roles = roles;
+ }
+
+ @Override
+ protected void before(KeycloakSession session) {
+ }
+
+ @Override
+ protected void runIteration(KeycloakSession session, RealmModel realm, Map<String, ApplicationModel> apps, Set<RoleModel> realmRoles, Map<String, Set<RoleModel>> appRoles, int counter) {
+ String username = prefix + "-" + counter;
+ UserModel user = session.users().addUser(realm, username);
+ user.setEnabled(true);
+ user.setFirstName("First");
+ user.setLastName("Last");
+ user.setEmail(username + "@localhost");
+
+ UserCredentialModel password = new UserCredentialModel();
+ password.setType(UserCredentialModel.PASSWORD);
+ password.setValue("password");
+
+ user.updateCredential(password);
+
+ for (String r : roles) {
+ grantRole(user, r, realmRoles, appRoles);
+ }
+ }
+
+}
diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/DeleteUsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/DeleteUsersJob.java
new file mode 100755
index 0000000..03ee710
--- /dev/null
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/DeleteUsersJob.java
@@ -0,0 +1,37 @@
+package org.keycloak.test.tools.jobs;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.RealmManager;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class DeleteUsersJob extends UsersJob {
+
+ private Iterator<UserModel> users;
+
+ @Override
+ protected void before(KeycloakSession session) {
+ RealmModel realm = new RealmManager(session).getRealmByName(realmName);
+
+ // TODO: pagination
+ List<UserModel> users = (prefix==null) ? session.users().getUsers(realm) : session.users().searchForUser(prefix, realm);
+ users = users.subList(start, start + count);
+
+ this.users = users.iterator();
+ }
+
+ @Override
+ protected void runIteration(KeycloakSession session, RealmModel realm, Map<String, ApplicationModel> apps, Set<RoleModel> realmRoles, Map<String, Set<RoleModel>> appRoles, int counter) {
+ session.users().removeUser(realm, users.next());
+ }
+}
diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UpdateUsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UpdateUsersJob.java
new file mode 100755
index 0000000..37e61a5
--- /dev/null
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UpdateUsersJob.java
@@ -0,0 +1,54 @@
+package org.keycloak.test.tools.jobs;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.RealmManager;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UpdateUsersJob extends UsersJob {
+
+ private String[] roles;
+ private Iterator<UserModel> users;
+
+ public UpdateUsersJob(String[] roles) {
+ this.roles = roles;
+ }
+
+ @Override
+ protected void before(KeycloakSession session) {
+ RealmModel realm = new RealmManager(session).getRealmByName(realmName);
+
+ // TODO: pagination
+ List<UserModel> users = (prefix==null) ? session.users().getUsers(realm) : session.users().searchForUser(prefix, realm);
+ users = users.subList(start, start + count);
+
+ this.users = users.iterator();
+ }
+
+ @Override
+ protected void runIteration(KeycloakSession session, RealmModel realm, Map<String, ApplicationModel> apps, Set<RoleModel> realmRoles, Map<String, Set<RoleModel>> appRoles, int counter) {
+ String username = users.next().getUsername();
+
+ // Remove all role mappings first
+ UserModel user = session.users().getUserByUsername(username, realm);
+ Set<RoleModel> currRoles = user.getRoleMappings();
+ for (RoleModel role : currRoles) {
+ user.deleteRoleMapping(role);
+ }
+
+ // Add new roles now
+ for (String r : roles) {
+ grantRole(user, r, realmRoles, appRoles);
+ }
+ }
+}
diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJob.java
new file mode 100755
index 0000000..5b7fc55
--- /dev/null
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJob.java
@@ -0,0 +1,126 @@
+package org.keycloak.test.tools.jobs;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.test.tools.PerfTools;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class UsersJob implements Runnable {
+
+ protected PerfTools.JobRepresentation job;
+ protected KeycloakSessionFactory sessionFactory;
+ protected String realmName;
+ protected int start;
+ protected int count;
+ protected String prefix;
+ protected CountDownLatch latch;
+
+ public void init(PerfTools.JobRepresentation job, KeycloakSessionFactory sessionFactory, String realmName, int start, int count, String prefix, CountDownLatch latch) {
+ this.sessionFactory = sessionFactory;
+ this.realmName = realmName;
+ this.start = start;
+ this.count = count;
+ this.prefix = prefix;
+ this.job = job;
+ this.latch = latch;
+
+ KeycloakSession session = sessionFactory.create();
+ try {
+ session.getTransaction().begin();
+
+ before(session);
+
+ session.getTransaction().commit();
+ } catch (Throwable t) {
+ handleThrowable(t, session);
+ } finally {
+ session.close();
+ }
+ }
+
+ @Override
+ public void run() {
+ job.start();
+
+ KeycloakSession session = sessionFactory.create();
+ try {
+ session.getTransaction().begin();
+
+ RealmModel realm = new RealmManager(session).getRealmByName(realmName);
+ Map<String, ApplicationModel> apps = realm.getApplicationNameMap();
+
+ Set<RoleModel> realmRoles = realm.getRoles();
+ Map<String, Set<RoleModel>> appRoles = new HashMap<String, Set<RoleModel>>();
+ for (Map.Entry<String, ApplicationModel> appEntry : apps.entrySet()) {
+ appRoles.put(appEntry.getKey(), appEntry.getValue().getRoles());
+ }
+
+ for (int i = start; i < (start + count); i++) {
+ runIteration(session, realm, apps, realmRoles, appRoles, i);
+ job.increment();
+ }
+
+ session.getTransaction().commit();
+ } catch (Throwable t) {
+ handleThrowable(t, session);
+ } finally {
+ latch.countDown();
+ session.close();
+ }
+
+ }
+
+ protected abstract void before(KeycloakSession keycloakSession);
+
+ protected abstract void runIteration(KeycloakSession session, RealmModel realm, Map<String, ApplicationModel> apps, Set<RoleModel> realmRoles, Map<String, Set<RoleModel>> appRoles, int counter);
+
+ protected RoleModel findRole(Set<RoleModel> roles, String roleName) {
+ for (RoleModel role : roles) {
+ if (role.getName().equals(roleName)) {
+ return role;
+ }
+ }
+
+ return null;
+ }
+
+ protected void grantRole(UserModel user, String roleName, Set<RoleModel> realmRoles, Map<String, Set<RoleModel>> appRoles) {
+ if (roleName.indexOf(':') == -1) {
+ // We expect "realmRoleName"
+ RoleModel realmRole = findRole(realmRoles, roleName);
+ user.grantRole(realmRole);
+ } else {
+ // We expect "appName:appRoleName"
+ String[] parts = roleName.split(":");
+ Set<RoleModel> currentAppRoles = appRoles.get(parts[0]);
+ if (currentAppRoles == null) {
+ throw new IllegalStateException("Application '" + parts[0] + "' not found");
+ }
+
+ RoleModel appRole = findRole(currentAppRoles, parts[1]);
+ user.grantRole(appRole);
+ }
+ }
+
+ private void handleThrowable(Throwable t, KeycloakSession session) {
+ StringWriter sw = new StringWriter();
+ t.printStackTrace(new PrintWriter(sw));
+ job.setError(sw.toString());
+ session.getTransaction().rollback();
+ }
+
+}
diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/KeycloakTestApplication.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/KeycloakTestApplication.java
index 7184978..a42c618 100644
--- a/testsuite/tools/src/main/java/org/keycloak/test/tools/KeycloakTestApplication.java
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/KeycloakTestApplication.java
@@ -5,8 +5,6 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.resources.KeycloakApplication;
import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import java.util.HashSet;
diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java
old mode 100644
new mode 100755
index 32a1fdc..51ff6f3
--- a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java
@@ -1,28 +1,29 @@
package org.keycloak.test.tools;
import org.keycloak.exportimport.ExportImportConfig;
-import org.keycloak.exportimport.ExportImportProvider;
+import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.test.tools.jobs.CreateUsers;
-import org.keycloak.util.ProviderLoader;
+import org.keycloak.test.tools.jobs.CreateUsersJob;
+import org.keycloak.test.tools.jobs.DeleteUsersJob;
+import org.keycloak.test.tools.jobs.UpdateUsersJob;
+import org.keycloak.test.tools.jobs.UsersJob;
+import org.keycloak.test.tools.jobs.UsersJobInitializer;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
-import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
-import java.util.HashMap;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
@@ -41,7 +42,7 @@ public class PerfTools {
@Context
private KeycloakSession session;
- private List<Job> jobs = new LinkedList<Job>();
+ private List<JobRepresentation> jobs = new LinkedList<JobRepresentation>();
public PerfTools(KeycloakSessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
@@ -50,16 +51,16 @@ public class PerfTools {
@GET
@Path("jobs")
@Produces("application/json")
- public List<Job> jobs() {
+ public List<JobRepresentation> jobs() {
return jobs;
}
@GET
@Path("delete-jobs")
public void deleteJobs() {
- Iterator<Job> itr = jobs.iterator();
+ Iterator<JobRepresentation> itr = jobs.iterator();
while(itr.hasNext()) {
- Job j = itr.next();
+ JobRepresentation j = itr.next();
if (j.getError() != null || j.getCount() == j.getTotal()) {
itr.remove();
}
@@ -68,38 +69,94 @@ public class PerfTools {
@GET
@Path("{realm}/create-users")
- public void createUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix, @QueryParam("roles") String roles) throws InterruptedException {
- if (count == null) {
- count = 1;
- }
- if (batch == null) {
- batch = 1000;
- }
- if (start == null) {
- start = 0;
+ public void createUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count,
+ @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix,
+ @QueryParam("async") Boolean async, @QueryParam("roles") String roles) throws InterruptedException {
+ final String[] rolesArray = roles != null ? roles.split(",") : new String[0];
+
+ createAndRunJob(realmName, count, batch, start, prefix, async, "Create users", new UsersJobInitializer() {
+
+ @Override
+ public UsersJob instantiateJob() {
+ return new CreateUsersJob(rolesArray);
+ }
+
+ });
+ }
+
+ // Same as createUsers, but dynamically compute "start" (Next available user)
+ @GET
+ @Path("{realm}/create-available-users")
+ public void createAvailableUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count,
+ @QueryParam("batch") Integer batch, @QueryParam("prefix") String prefix,
+ @QueryParam("async") Boolean async, @QueryParam("roles") String roles) throws InterruptedException {
+ int start = getUsersCount(realmName, prefix);
+ createUsers(realmName, count, batch, start, prefix, async, roles);
+ }
+
+ @GET
+ @Path("{realm}/delete-users")
+ public void deleteUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count,
+ @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix,
+ @QueryParam("async") Boolean async) throws InterruptedException {
+ createAndRunJob(realmName, count, batch, start, prefix, async, "Delete users", new UsersJobInitializer() {
+
+ @Override
+ public UsersJob instantiateJob() {
+ return new DeleteUsersJob();
+ }
+
+ });
+ }
+
+ @GET
+ @Path("{realm}/delete-all-users")
+ public void deleteUsers(@PathParam("realm") String realmName, @QueryParam("prefix") String prefix, @QueryParam("async") Boolean async) throws InterruptedException {
+ int count = getUsersCount(realmName, prefix);
+ if (count == 0) {
+ return;
}
- if (prefix == null) {
- prefix = String.valueOf(System.currentTimeMillis());
+
+ int batch = count / 10;
+ if (batch == 0) {
+ batch = 1;
}
- String[] rolesArray = roles != null ? roles.split(",") : new String[0];
+ deleteUsers(realmName, count, batch, 0, prefix, async);
+ }
- Job job = new Job("Create users " + prefix + "-" + start + " to " + prefix + "-" + (start + count), count);
- jobs.add(job);
+ @GET
+ @Path("{realm}/update-users")
+ public void updateUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count,
+ @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix,
+ @QueryParam("async") Boolean async, @QueryParam("roles") String roles) throws InterruptedException {
+ final String[] rolesArray = roles != null ? roles.split(",") : new String[0];
- for (int s = start; s < (start + count); s += batch) {
- int c = s + batch <= (start + count) ? batch : (start + count) - s;
- executor.submit(new CreateUsers(job, sessionFactory, realmName, s, c, prefix, rolesArray));
- }
+ createAndRunJob(realmName, count, batch, start, prefix, async, "Update users", new UsersJobInitializer() {
+
+ @Override
+ public UsersJob instantiateJob() {
+ return new UpdateUsersJob(rolesArray);
+ }
+
+ });
}
@GET
- @Path("{realm}/delete-users")
- public void deleteUsers(@PathParam("realm") String realmName) {
- RealmModel realm = session.getRealmByName(realmName);
- for (UserModel user : realm.getUsers()) {
- realm.removeUser(user.getLoginName());
+ @Path("{realm}/update-all-users")
+ public void updateAllUsers(@PathParam("realm") String realmName, @QueryParam("prefix") String prefix, @QueryParam("async") Boolean async,
+ @QueryParam("roles") String roles) throws InterruptedException {
+ int count = getUsersCount(realmName, prefix);
+ if (count == 0) {
+ return;
}
+
+ int batch = count / 10;
+ if (batch == 0) {
+ batch = 1;
+ }
+
+ updateUsers(realmName, count, batch, 0, prefix, async, roles);
}
@@ -110,22 +167,58 @@ public class PerfTools {
return Response.ok(String.valueOf(usersCount)).build();
}
- // Same as createUsers, but dynamically compute "start" (Next available user)
- @GET
- @Path("{realm}/create-available-users")
- public void createAvailableUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, @QueryParam("batch") Integer batch, @QueryParam("prefix") String prefix, @QueryParam("roles") String roles) throws InterruptedException {
- int start = getUsersCount(realmName, prefix);
- createUsers(realmName, count, batch, start, prefix, roles);
- }
-
private int getUsersCount(String realmName, String prefix) {
- RealmModel realm = session.getRealmByName(realmName);
+ RealmModel realm = session.realms().getRealmByName(realmName);
// TODO: method for count on model
if (prefix == null) {
- return realm.getUsers().size();
+ return session.users().getUsers(realm).size();
} else {
- return realm.searchForUser(prefix).size();
+ return session.users().searchForUser(prefix, realm).size();
+ }
+ }
+
+ private void createAndRunJob(String realmName, Integer count, Integer batch, Integer start, String prefix, Boolean async, String jobName, UsersJobInitializer initializer) throws InterruptedException {
+ if (count == null) {
+ count = 1;
+ }
+ if (batch == null) {
+ batch = 1000;
+ }
+ if (start == null) {
+ start = 0;
+ }
+ if (prefix == null) {
+ prefix = String.valueOf(System.currentTimeMillis());
+ }
+ if (async == null) {
+ async = true;
+ }
+
+ int executorsCount = count / batch;
+ if (count % batch > 0) {
+ executorsCount++;
+ }
+ CountDownLatch latch = new CountDownLatch(executorsCount);
+
+ JobRepresentation job = new JobRepresentation(jobName + " " + prefix + "-" + start + " to " + prefix + "-" + (start + count), count);
+ jobs.add(job);
+
+ List<UsersJob> usersJobs = new ArrayList<UsersJob>();
+ for (int s = start; s < (start + count); s += batch) {
+ int c = s + batch <= (start + count) ? batch : (start + count) - s;
+ UsersJob usersJob = initializer.instantiateJob();
+ usersJob.init(job, sessionFactory, realmName, s, c, prefix, latch);
+ usersJobs.add(usersJob);
+ }
+
+ // Run executors once all are initialized
+ for (UsersJob usersJob : usersJobs) {
+ executor.submit(usersJob);
+ }
+
+ if (!async) {
+ latch.await();
}
}
@@ -136,17 +229,10 @@ public class PerfTools {
ExportImportConfig.setProvider("dir");
ExportImportConfig.setDir(dir);
- Iterator<ExportImportProvider> providers = ProviderLoader.load(ExportImportProvider.class).iterator();
-
- if (providers.hasNext()) {
- ExportImportProvider exportImport = providers.next();
- exportImport.checkExportImport(sessionFactory);
- } else {
- throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
- }
+ new ExportImportManager().checkExportImport(sessionFactory);
}
- public class Job {
+ public static class JobRepresentation {
private final String description;
private final int total;
private AtomicInteger count = new AtomicInteger();
@@ -154,7 +240,7 @@ public class PerfTools {
private AtomicLong started = new AtomicLong();
private AtomicLong completed = new AtomicLong();
- public Job(String description, int total) {
+ public JobRepresentation(String description, int total) {
this.description = description;
this.total = total;
}
diff --git a/testsuite/tools/src/main/resources/META-INF/keycloak-server.json b/testsuite/tools/src/main/resources/META-INF/keycloak-server.json
index 7e3f246..fd02151 100755
--- a/testsuite/tools/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/tools/src/main/resources/META-INF/keycloak-server.json
@@ -14,6 +14,10 @@
"provider": "jpa"
},
+ "userSessions": {
+ "provider" : "mem"
+ },
+
"modelCache": {
"provider": "${keycloak.model.cache.provider:}"
},
@@ -27,7 +31,7 @@
"staticMaxAge": 2592000,
"cacheTemplates": true,
"folder": {
- "dir": "${jboss.server.config.dir}/themes"
+ "dir": "${jboss.server.config.dir}/themes"
}
},