keycloak-uncached
Changes
authz/policy/common/pom.xml 5(+5 -0)
authz/policy/drools/pom.xml 5(+5 -0)
dependencies/server-min/pom.xml 4(+4 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml 4(+0 -4)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml 4(+0 -4)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml 4(+0 -4)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi-private/main/module.xml 40(+40 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml 2(+2 -0)
examples/providers/rest/pom.xml 5(+5 -0)
examples/providers/rest/README.md 2(+1 -1)
examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java 4(+2 -2)
federation/kerberos/pom.xml 5(+5 -0)
federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java 2(+1 -1)
federation/ldap/pom.xml 5(+5 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java 3(+2 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java 5(+3 -2)
federation/sssd/pom.xml 5(+5 -0)
model/infinispan/pom.xml 4(+4 -0)
model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java 4(+1 -3)
model/jpa/pom.xml 4(+4 -0)
model/mongo/pom.xml 4(+4 -0)
pom.xml 10(+8 -2)
server-spi/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java 126(+0 -126)
server-spi/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java 26(+0 -26)
server-spi/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java 40(+0 -40)
server-spi/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java 51(+0 -51)
server-spi/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java 91(+0 -91)
server-spi/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java 92(+0 -92)
server-spi/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java 92(+0 -92)
server-spi/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java 73(+0 -73)
server-spi/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java 73(+0 -73)
server-spi/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java 73(+0 -73)
server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java 28(+0 -28)
server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java 30(+0 -30)
server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java 8(+4 -4)
server-spi/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory 18(+0 -18)
server-spi/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory 18(+0 -18)
server-spi/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory 28(+0 -28)
server-spi-private/pom.xml 98(+98 -0)
server-spi-private/src/main/java/org/keycloak/authentication/AbstractAuthenticationFlowContext.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticationFlowContext.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/package-info.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderDataMarshaller.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/broker/social/SocialIdentityProviderFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientProvider.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/mappers/FederationConfigValidationException.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java 126(+126 -0)
server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionAdapter.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java 79(+79 -0)
server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java 54(+54 -0)
server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java 26(+26 -0)
server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java 48(+48 -0)
server-spi-private/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java 40(+40 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java 25(+25 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java 0(+0 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java 57(+57 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java 43(+43 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java 42(+42 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java 179(+179 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java 88(+88 -0)
server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java 80(+80 -0)
server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java 51(+51 -0)
server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java 73(+73 -0)
server-spi-private/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java 91(+91 -0)
server-spi-private/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java 89(+89 -0)
server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java 90(+90 -0)
server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java 74(+74 -0)
server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java 73(+73 -0)
server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java 63(+63 -0)
server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java 73(+73 -0)
server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java 59(+59 -0)
server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java 73(+73 -0)
server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java 27(+27 -0)
server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java 67(+67 -0)
server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java 73(+73 -0)
server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java 63(+63 -0)
server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java 73(+73 -0)
server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java 63(+63 -0)
server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java 73(+73 -0)
server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java 38(+38 -0)
server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java 28(+28 -0)
server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java 69(+69 -0)
server-spi-private/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java 121(+121 -0)
server-spi-private/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java 33(+33 -0)
server-spi-private/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java 37(+37 -0)
server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java 27(+27 -0)
server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java 49(+49 -0)
server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java 38(+38 -0)
server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java 30(+30 -0)
server-spi-private/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java 43(+43 -0)
server-spi-private/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java 49(+49 -0)
server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory 18(+18 -0)
server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory 18(+18 -0)
server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory 28(+28 -0)
services/pom.xml 5(+5 -0)
services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java 4(+2 -2)
services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java 2(+1 -1)
testsuite/integration/pom.xml 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java 2(+1 -1)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml 1(+1 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java 8(+0 -8)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java 9(+0 -9)
wildfly/adduser/pom.xml 4(+4 -0)
wildfly/extensions/pom.xml 5(+5 -0)
wildfly/server-subsystem/pom.xml 2(+1 -1)
Details
authz/policy/common/pom.xml 5(+5 -0)
diff --git a/authz/policy/common/pom.xml b/authz/policy/common/pom.xml
index 6149e3a..31df304 100644
--- a/authz/policy/common/pom.xml
+++ b/authz/policy/common/pom.xml
@@ -46,6 +46,11 @@
<artifactId>keycloak-server-spi</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
authz/policy/drools/pom.xml 5(+5 -0)
diff --git a/authz/policy/drools/pom.xml b/authz/policy/drools/pom.xml
index 7dc1b45..1594329 100644
--- a/authz/policy/drools/pom.xml
+++ b/authz/policy/drools/pom.xml
@@ -25,6 +25,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
dependencies/server-min/pom.xml 4(+4 -0)
diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml
index 6beae84..1c17f64 100755
--- a/dependencies/server-min/pom.xml
+++ b/dependencies/server-min/pom.xml
@@ -60,6 +60,10 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-js-adapter</artifactId>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml
index 09d9479..4c5af2f 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml
@@ -31,6 +31,7 @@
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="com.thoughtworks.xstream"/>
<module name="org.antlr" slot="3.5"/>
<module name="org.kie"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml
index 8440c1d..849fc35 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml
@@ -31,6 +31,7 @@
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
</dependencies>
</module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml
index 422c683..a8ab099 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml
@@ -29,6 +29,7 @@
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.keycloak.keycloak-services"/>
</dependencies>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml
index cf36e0a..60dc78a 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml
@@ -28,6 +28,7 @@
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.keycloak.keycloak-services"/>
<module name="org.kie"/>
</dependencies>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml
index 4a3d370..7b024b1 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml
@@ -16,10 +16,6 @@
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-common">
- <properties>
- <property name="jboss.api" value="private"/>
- </properties>
-
<resources>
<artifact name="${org.keycloak:keycloak-common}"/>
</resources>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml
index 1afc30d..659a055 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml
@@ -16,10 +16,6 @@
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-core">
- <properties>
- <property name="jboss.api" value="private"/>
- </properties>
-
<resources>
<artifact name="${org.keycloak:keycloak-core}"/>
</resources>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml
index d0b9730..ebb263c 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml
@@ -28,6 +28,7 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="javax.ws.rs.api"/>
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
<module name="org.jboss.logging"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
index 43a1d64..dcca1bf 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
@@ -28,6 +28,7 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.keycloak.keycloak-kerberos-federation"/>
<module name="javax.ws.rs.api"/>
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml
index 91f423a..a80a008 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml
@@ -28,6 +28,7 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.infinispan"/>
<module name="org.jboss.logging"/>
<module name="javax.api"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml
index 94c80a2..88cae4f 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml
@@ -29,6 +29,7 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="javax.persistence.api"/>
<module name="org.jboss.logging"/>
<module name="org.liquibase"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml
index 20cff7c..3bd99ae 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml
@@ -29,6 +29,7 @@
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-services"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.mongodb.mongo-java-driver"/>
<module name="org.jboss.logging"/>
<module name="javax.api"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml
index 8b5632e..daed0af 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml
@@ -16,10 +16,6 @@
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-server-spi">
- <properties>
- <property name="jboss.api" value="private"/>
- </properties>
-
<resources>
<artifact name="${org.keycloak:keycloak-server-spi}"/>
</resources>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi-private/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi-private/main/module.xml
new file mode 100755
index 0000000..0f136fd
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi-private/main/module.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-server-spi-private">
+ <properties>
+ <property name="jboss.api" value="private"/>
+ </properties>
+
+ <resources>
+ <artifact name="${org.keycloak:keycloak-server-spi-private}"/>
+ </resources>
+
+ <dependencies>
+ <module name="org.jboss.logging"/>
+ <module name="org.keycloak.keycloak-common"/>
+ <module name="org.keycloak.keycloak-core"/>
+ <module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.bouncycastle" />
+ <module name="javax.api"/>
+ <module name="javax.ws.rs.api"/>
+ <module name="org.apache.httpcomponents"/>
+ <module name="org.jboss.resteasy.resteasy-jaxrs"/>
+ <module name="javax.transaction.api"/>
+ </dependencies>
+</module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
index 4f351fd..9db480c 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
@@ -32,6 +32,7 @@
<module name="org.keycloak.keycloak-ldap-federation" services="import"/>
<module name="org.keycloak.keycloak-sssd-federation" services="import"/>
<module name="org.keycloak.keycloak-server-spi" services="import"/>
+ <module name="org.keycloak.keycloak-server-spi-private" services="import"/>
<module name="org.keycloak.keycloak-model-jpa" services="import"/>
<module name="org.keycloak.keycloak-model-mongo" services="import"/>
<module name="org.keycloak.keycloak-model-infinispan" services="import"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml
index ad16f3c..58939eb 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml
@@ -28,5 +28,6 @@
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-core" />
<module name="org.keycloak.keycloak-server-spi" />
+ <module name="org.keycloak.keycloak-server-spi-private"/>
</dependencies>
</module>
\ No newline at end of file
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml
index 4ee5c50..178470b 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml
@@ -30,6 +30,7 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.jboss.aesh"/>
<module name="org.jboss.as.domain-management"/>
<module name="com.fasterxml.jackson.core.jackson-core"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml
index d785cb7..89aa901 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml
@@ -28,6 +28,7 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.keycloak.keycloak-services"/>
<module name="org.jboss.modules"/>
</dependencies>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml
index 242b508..9b41daa 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml
@@ -32,11 +32,13 @@
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.slf4j"/>
<module name="org.apache.commons.logging"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="com.sun.xml.bind"/>
<module name="com.thoughtworks.xstream"/>
<module name="org.apache.ant"/>
diff --git a/examples/providers/authenticator/pom.xml b/examples/providers/authenticator/pom.xml
index 4e24bf3..0992966 100755
--- a/examples/providers/authenticator/pom.xml
+++ b/examples/providers/authenticator/pom.xml
@@ -42,6 +42,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
diff --git a/examples/providers/domain-extension/pom.xml b/examples/providers/domain-extension/pom.xml
index 43e5cf7..d31ccbf 100755
--- a/examples/providers/domain-extension/pom.xml
+++ b/examples/providers/domain-extension/pom.xml
@@ -48,6 +48,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<scope>provided</scope>
</dependency>
diff --git a/examples/providers/domain-extension/README.md b/examples/providers/domain-extension/README.md
index 9ec5e63..465551b 100644
--- a/examples/providers/domain-extension/README.md
+++ b/examples/providers/domain-extension/README.md
@@ -3,7 +3,7 @@ Example Domain Extension
To run, deploy as a module by running:
- $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist,org.liquibase"
+ $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist,org.liquibase"
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
diff --git a/examples/providers/event-listener-sysout/pom.xml b/examples/providers/event-listener-sysout/pom.xml
index 83f348b..3d61dfd 100755
--- a/examples/providers/event-listener-sysout/pom.xml
+++ b/examples/providers/event-listener-sysout/pom.xml
@@ -40,6 +40,11 @@
<artifactId>keycloak-server-spi</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/examples/providers/event-listener-sysout/README.md b/examples/providers/event-listener-sysout/README.md
index 8e1a085..5540d9b 100644
--- a/examples/providers/event-listener-sysout/README.md
+++ b/examples/providers/event-listener-sysout/README.md
@@ -3,7 +3,7 @@ Example Event Listener that prints events to System.out
To deploy copy target/event-listener-sysout-example.jar to providers directory. Alternatively you can deploy as a module by running:
- KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
+ KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
diff --git a/examples/providers/event-store-mem/pom.xml b/examples/providers/event-store-mem/pom.xml
index 16db181..2e48c2d 100755
--- a/examples/providers/event-store-mem/pom.xml
+++ b/examples/providers/event-store-mem/pom.xml
@@ -40,6 +40,11 @@
<artifactId>keycloak-server-spi</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/examples/providers/event-store-mem/README.md b/examples/providers/event-store-mem/README.md
index 682ff42..2c55109 100644
--- a/examples/providers/event-store-mem/README.md
+++ b/examples/providers/event-store-mem/README.md
@@ -3,7 +3,7 @@ Example Event Store that stores events in memory
To deploy copy target/event-store-mem-example.jar to providers directory. Alternatively you can deploy as a module by running:
- KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-inmem --resources=target/event-store-mem-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
+ KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-inmem --resources=target/event-store-mem-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
diff --git a/examples/providers/federation-provider/pom.xml b/examples/providers/federation-provider/pom.xml
index bd4e1c8..be02cc7 100755
--- a/examples/providers/federation-provider/pom.xml
+++ b/examples/providers/federation-provider/pom.xml
@@ -42,6 +42,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
diff --git a/examples/providers/federation-provider/README.md b/examples/providers/federation-provider/README.md
index c90e791..ae02fc8 100755
--- a/examples/providers/federation-provider/README.md
+++ b/examples/providers/federation-provider/README.md
@@ -4,7 +4,7 @@ Example User Federation Provider
This is an example of user federation backed by a simple properties file. This properties file only contains username/password
key pairs. To deploy, build this directory then take the jar and copy it to providers directory. Alternatively you can deploy as a module by running:
- KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
+ KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
examples/providers/rest/pom.xml 5(+5 -0)
diff --git a/examples/providers/rest/pom.xml b/examples/providers/rest/pom.xml
index 416acfc..b94cf43 100755
--- a/examples/providers/rest/pom.xml
+++ b/examples/providers/rest/pom.xml
@@ -42,6 +42,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
</dependency>
examples/providers/rest/README.md 2(+1 -1)
diff --git a/examples/providers/rest/README.md b/examples/providers/rest/README.md
index 5ee9327..d78e9bc 100644
--- a/examples/providers/rest/README.md
+++ b/examples/providers/rest/README.md
@@ -3,7 +3,7 @@ Example Realm REST Resource provider
To deploy copy target/hello-rest-example.jar to providers directory. Alternatively you can deploy as a module by running:
- $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,javax.ws.rs.api"
+ $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,javax.ws.rs.api"
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java
index 837f2b6..86b2a30 100644
--- a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java
@@ -30,7 +30,6 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.user.UserLookupProvider;
@@ -49,6 +48,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -139,7 +139,7 @@ public class EjbExampleUserStorageProvider implements UserStorageProvider,
@Override
public UserModel addUser(RealmModel realm, String username) {
UserEntity entity = new UserEntity();
- entity.setId(KeycloakModelUtils.generateId());
+ entity.setId(UUID.randomUUID().toString());
entity.setUsername(username);
em.persist(entity);
logger.info("added user: " + username);
federation/kerberos/pom.xml 5(+5 -0)
diff --git a/federation/kerberos/pom.xml b/federation/kerberos/pom.xml
index 75d3ab4..da453d6 100755
--- a/federation/kerberos/pom.xml
+++ b/federation/kerberos/pom.xml
@@ -41,6 +41,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
index d656a0d..b5f4709 100755
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
@@ -33,7 +33,7 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import java.util.Collections;
import java.util.HashMap;
federation/ldap/pom.xml 5(+5 -0)
diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml
index 4187ef6..38fa19d 100755
--- a/federation/ldap/pom.xml
+++ b/federation/ldap/pom.xml
@@ -42,6 +42,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-kerberos-federation</artifactId>
<scope>provided</scope>
</dependency>
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
index 880f0f7..67cfb45 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -47,7 +47,7 @@ import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import javax.naming.AuthenticationException;
import java.util.ArrayList;
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java
index 460ab62..d135dfa 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java
@@ -38,6 +38,7 @@ import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationSyncResult;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import org.keycloak.models.utils.UserModelDelegate;
import java.util.Collection;
@@ -563,7 +564,7 @@ public class GroupLDAPFederationMapper extends AbstractLDAPFederationMapper impl
@Override
public boolean hasRole(RoleModel role) {
- return super.hasRole(role) || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
+ return super.hasRole(role) || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java
index 9d6e2a1..b45f348 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java
@@ -38,6 +38,7 @@ import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationSyncResult;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import org.keycloak.models.utils.UserModelDelegate;
import java.util.Collection;
@@ -348,8 +349,8 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper imple
@Override
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
- return KeycloakModelUtils.hasRole(roles, role)
- || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
+ return RoleUtils.hasRole(roles, role)
+ || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
}
@Override
federation/sssd/pom.xml 5(+5 -0)
diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml
index 8430e7c..29113f7 100644
--- a/federation/sssd/pom.xml
+++ b/federation/sssd/pom.xml
@@ -61,6 +61,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java
index 2820947..44662de 100755
--- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java
+++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java
@@ -34,7 +34,7 @@ import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import java.util.Collections;
import java.util.HashSet;
model/infinispan/pom.xml 4(+4 -0)
diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml
index 08aef53..f10fa60 100755
--- a/model/infinispan/pom.xml
+++ b/model/infinispan/pom.xml
@@ -38,6 +38,10 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
index 6030a66..1a52aa9 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
@@ -27,6 +27,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.infinispan.entities.CachedUser;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import java.util.Collections;
import java.util.HashSet;
@@ -301,7 +302,7 @@ public class UserAdapter implements CachedUserModel {
for (RoleModel mapping: mappings) {
if (mapping.hasRole(role)) return true;
}
- return KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
+ return RoleUtils.hasRoleFromGroup(getGroups(), role, true);
}
@Override
@@ -368,7 +369,7 @@ public class UserAdapter implements CachedUserModel {
if (updated != null) return updated.isMemberOf(group);
if (cached.getGroups().contains(group.getId())) return true;
Set<GroupModel> roles = getGroups();
- return KeycloakModelUtils.isMember(roles, group);
+ return RoleUtils.isMember(roles, group);
}
@Override
model/jpa/pom.xml 4(+4 -0)
diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
index 13f3ae0..0b2ae4d 100755
--- a/model/jpa/pom.xml
+++ b/model/jpa/pom.xml
@@ -50,6 +50,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
index 6ea87c5..3dc7bcc 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
@@ -27,6 +27,7 @@ import org.keycloak.models.jpa.entities.GroupAttributeEntity;
import org.keycloak.models.jpa.entities.GroupEntity;
import org.keycloak.models.jpa.entities.GroupRoleMappingEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
@@ -228,7 +229,7 @@ public class GroupAdapter implements GroupModel , JpaModel<GroupEntity> {
@Override
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
- return KeycloakModelUtils.hasRole(roles, role);
+ return RoleUtils.hasRole(roles, role);
}
protected TypedQuery<GroupRoleMappingEntity> getGroupRoleMappingEntityTypedQuery(RoleModel role) {
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 5ee5490..a95548b 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
@@ -31,6 +31,7 @@ import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
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.RoleUtils;
import javax.persistence.EntityManager;
import javax.persistence.Query;
@@ -338,7 +339,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
@Override
public boolean isMemberOf(GroupModel group) {
Set<GroupModel> roles = getGroups();
- return KeycloakModelUtils.isMember(roles, group);
+ return RoleUtils.isMember(roles, group);
}
protected TypedQuery<UserGroupMembershipEntity> getUserGroupMappingQuery(GroupModel group) {
@@ -352,8 +353,8 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
@Override
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
- return KeycloakModelUtils.hasRole(roles, role)
- || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
+ return RoleUtils.hasRole(roles, role)
+ || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
}
protected TypedQuery<UserRoleMappingEntity> getUserRoleMappingEntityTypedQuery(RoleModel role) {
model/mongo/pom.xml 4(+4 -0)
diff --git a/model/mongo/pom.xml b/model/mongo/pom.xml
index d31fa69..054d320 100755
--- a/model/mongo/pom.xml
+++ b/model/mongo/pom.xml
@@ -50,6 +50,10 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java
index 724dd69..d4ad3af 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java
@@ -28,6 +28,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.mongo.keycloak.entities.MongoGroupEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -146,7 +147,7 @@ public class GroupAdapter extends AbstractMongoAdapter<MongoGroupEntity> impleme
@Override
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
- return KeycloakModelUtils.hasRole(roles, role);
+ return RoleUtils.hasRole(roles, role);
}
@Override
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 972f440..9d5ad7c 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
@@ -27,6 +27,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
import org.keycloak.models.mongo.utils.MongoModelUtils;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -262,14 +263,14 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
public boolean isMemberOf(GroupModel group) {
if (user.getGroupIds().contains(group.getId())) return true;
Set<GroupModel> groups = getGroups();
- return KeycloakModelUtils.isMember(groups, group);
+ return RoleUtils.isMember(groups, group);
}
@Override
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
- return KeycloakModelUtils.hasRole(roles, role)
- || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
+ return RoleUtils.hasRole(roles, role)
+ || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
}
@Override
pom.xml 10(+8 -2)
diff --git a/pom.xml b/pom.xml
index 8a61e5b..7918c6d 100755
--- a/pom.xml
+++ b/pom.xml
@@ -120,7 +120,7 @@
<osgi.bundle.plugin.version>2.3.7</osgi.bundle.plugin.version>
<wildfly.plugin.version>1.0.1.Final</wildfly.plugin.version>
<nexus.staging.plugin.version>1.6.5</nexus.staging.plugin.version>
-
+
<!-- Surefire Settings -->
<surefire.memory.settings>-Xms512m -Xmx2048m -XX:MetaspaceSize=96m -XX:MaxMetaspaceSize=256m</surefire.memory.settings>
</properties>
@@ -175,6 +175,7 @@
<module>core</module>
<module>dependencies</module>
<module>server-spi</module>
+ <module>server-spi-private</module>
<module>saml-core-api</module>
<module>saml-core</module>
<module>proxy</module>
@@ -940,6 +941,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version>
</dependency>
@@ -1483,7 +1489,7 @@
</plugins>
</build>
</profile>
-
+
<profile>
<id>nexus-staging</id>
<build>
diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java b/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java
index e644a80..be5f551 100755
--- a/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java
+++ b/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java
@@ -18,7 +18,6 @@
package org.keycloak.models;
import org.keycloak.common.ClientConnection;
-import org.keycloak.models.utils.RealmImporter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
@@ -52,8 +51,6 @@ public interface KeycloakContext {
void setConnection(ClientConnection connection);
- RealmImporter getRealmManager();
-
Locale resolveLocale(UserModel user);
}
diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
index cb10891..f3242eb 100755
--- a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
+++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
@@ -20,7 +20,6 @@ package org.keycloak.models;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.cache.UserCache;
import org.keycloak.provider.Provider;
-import org.keycloak.scripting.ScriptingProvider;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import java.util.Set;
@@ -157,8 +156,4 @@ public interface KeycloakSession {
*/
KeyManager keys();
- /**
- * Keycloak scripting support.
- */
- ScriptingProvider scripting();
}
diff --git a/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java b/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java
index 95dfc53..8833c8a 100755
--- a/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java
+++ b/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java
@@ -17,10 +17,6 @@
package org.keycloak.models;
-import org.keycloak.policy.ForceExpiredPasswordPolicyProviderFactory;
-import org.keycloak.policy.HashAlgorithmPasswordPolicyProviderFactory;
-import org.keycloak.policy.HashIterationsPasswordPolicyProviderFactory;
-import org.keycloak.policy.HistoryPasswordPolicyProviderFactory;
import org.keycloak.policy.PasswordPolicyProvider;
import java.io.Serializable;
@@ -33,6 +29,18 @@ import java.util.Set;
*/
public class PasswordPolicy implements Serializable {
+ public static final String HASH_ALGORITHM_ID = "hashAlgorithm";
+
+ public static final String HASH_ALGORITHM_DEFAULT = "pbkdf2";
+
+ public static final String HASH_ITERATIONS_ID = "hashIterations";
+
+ public static final int HASH_ITERATIONS_DEFAULT = 20000;
+
+ public static final String PASSWORD_HISTORY_ID = "passwordHistory";
+
+ public static final String FORCE_EXPIRED_ID = "forceExpiredPasswordChange";
+
private String policyString;
private Map<String, Object> policyConfig;
@@ -84,32 +92,32 @@ public class PasswordPolicy implements Serializable {
}
public String getHashAlgorithm() {
- if (policyConfig.containsKey(HashAlgorithmPasswordPolicyProviderFactory.ID)) {
- return getPolicyConfig(HashAlgorithmPasswordPolicyProviderFactory.ID);
+ if (policyConfig.containsKey(HASH_ALGORITHM_ID)) {
+ return getPolicyConfig(HASH_ALGORITHM_ID);
} else {
- return HashAlgorithmPasswordPolicyProviderFactory.DEFAULT_VALUE;
+ return HASH_ALGORITHM_DEFAULT;
}
}
public int getHashIterations() {
- if (policyConfig.containsKey(HashIterationsPasswordPolicyProviderFactory.ID)) {
- return getPolicyConfig(HashIterationsPasswordPolicyProviderFactory.ID);
+ if (policyConfig.containsKey(HASH_ITERATIONS_ID)) {
+ return getPolicyConfig(HASH_ITERATIONS_ID);
} else {
- return HashIterationsPasswordPolicyProviderFactory.DEFAULT_VALUE;
+ return HASH_ITERATIONS_DEFAULT;
}
}
public int getExpiredPasswords() {
- if (policyConfig.containsKey(HistoryPasswordPolicyProviderFactory.ID)) {
- return getPolicyConfig(HistoryPasswordPolicyProviderFactory.ID);
+ if (policyConfig.containsKey(PASSWORD_HISTORY_ID)) {
+ return getPolicyConfig(PASSWORD_HISTORY_ID);
} else {
return -1;
}
}
public int getDaysToExpirePassword() {
- if (policyConfig.containsKey(ForceExpiredPasswordPolicyProviderFactory.ID)) {
- return getPolicyConfig(ForceExpiredPasswordPolicyProviderFactory.ID);
+ if (policyConfig.containsKey(FORCE_EXPIRED_ID)) {
+ return getPolicyConfig(FORCE_EXPIRED_ID);
} else {
return -1;
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
index 1765bad..707b96a 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -19,8 +19,6 @@ package org.keycloak.models;
import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.services.managers.UserManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -74,7 +72,8 @@ public class UserFederationManager implements UserProvider {
}
public UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
- return KeycloakModelUtils.getFederationProviderInstance(session, model);
+ UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, model.getProviderName());
+ return factory.getInstance(session, model);
}
public UserFederationProvider getFederationLink(RealmModel realm, UserModel user) {
@@ -122,7 +121,7 @@ public class UserFederationManager implements UserProvider {
}
protected void deleteInvalidUser(final RealmModel realm, final UserModel user) {
- KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
+ runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
@@ -136,6 +135,29 @@ public class UserFederationManager implements UserProvider {
});
}
+ private static void runJobInTransaction(KeycloakSessionFactory factory, KeycloakSessionTask task) {
+ KeycloakSession session = factory.create();
+ KeycloakTransaction tx = session.getTransactionManager();
+ try {
+ tx.begin();
+ task.run(session);
+
+ if (tx.isActive()) {
+ if (tx.getRollbackOnly()) {
+ tx.rollback();
+ } else {
+ tx.commit();
+ }
+ }
+ } catch (RuntimeException re) {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ throw re;
+ } finally {
+ session.close();
+ }
+ }
protected UserModel validateAndProxyUser(RealmModel realm, UserModel user) {
UserModel managed = managedUsers.get(user.getId());
diff --git a/server-spi/src/main/java/org/keycloak/models/UserManager.java b/server-spi/src/main/java/org/keycloak/models/UserManager.java
new file mode 100755
index 0000000..81b2b51
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/UserManager.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models;
+
+import org.keycloak.models.session.UserSessionPersisterProvider;
+
+/**
+ * @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) {
+ return removeUser(realm, user, session.users());
+ }
+
+ public boolean removeUser(RealmModel realm, UserModel user, UserProvider userProvider) {
+ UserSessionProvider sessions = session.sessions();
+ if (sessions != null) {
+ sessions.onUserRemoved(realm, user);
+ }
+
+ UserSessionPersisterProvider sessionsPersister = session.getProvider(UserSessionPersisterProvider.class);
+ if (sessionsPersister != null) {
+ sessionsPersister.onUserRemoved(realm, user);
+ }
+
+ if (userProvider.removeUser(realm, user)) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java b/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java
new file mode 100644
index 0000000..a03a55d
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.RoleModel;
+
+import java.util.Set;
+import java.util.stream.StreamSupport;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RoleUtils {
+
+ /**
+ *
+ * @param groups
+ * @param targetGroup
+ * @return true if targetGroup is in groups (directly or indirectly via parent child relationship)
+ */
+ public static boolean isMember(Set<GroupModel> groups, GroupModel targetGroup) {
+ if (groups.contains(targetGroup)) return true;
+
+ for (GroupModel mapping : groups) {
+ GroupModel child = mapping;
+ while(child.getParent() != null) {
+ if (child.getParent().equals(targetGroup)) return true;
+ child = child.getParent();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param roles
+ * @param targetRole
+ * @return true if targetRole is in roles (directly or indirectly via composite role)
+ */
+ public static boolean hasRole(Set<RoleModel> roles, RoleModel targetRole) {
+ if (roles.contains(targetRole)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(targetRole)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether the {@code targetRole} is contained in the given group or its parents
+ * (if requested)
+ * @param group Group to check role for
+ * @param targetRole
+ * @param checkParentGroup When {@code true}, also parent group is recursively checked for role
+ * @return true if targetRole is in roles (directly or indirectly via composite role)
+ */
+ public static boolean hasRoleFromGroup(GroupModel group, RoleModel targetRole, boolean checkParentGroup) {
+ if (group.hasRole(targetRole))
+ return true;
+
+ if (checkParentGroup) {
+ GroupModel parent = group.getParent();
+ return parent != null && hasRoleFromGroup(parent, targetRole, true);
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the {@code targetRole} is contained in any of the {@code groups} or their parents
+ * (if requested)
+ * @param groups
+ * @param targetRole
+ * @param checkParentGroup When {@code true}, also parent group is recursively checked for role
+ * @return true if targetRole is in roles (directly or indirectly via composite role)
+ */
+ public static boolean hasRoleFromGroup(Iterable<GroupModel> groups, RoleModel targetRole, boolean checkParentGroup) {
+ if (groups == null) {
+ return false;
+ }
+
+ return StreamSupport.stream(groups.spliterator(), false)
+ .anyMatch(group -> hasRoleFromGroup(group, targetRole, checkParentGroup));
+ }
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/provider/Spi.java b/server-spi/src/main/java/org/keycloak/provider/Spi.java
index b9c47f8..8043bcf 100644
--- a/server-spi/src/main/java/org/keycloak/provider/Spi.java
+++ b/server-spi/src/main/java/org/keycloak/provider/Spi.java
@@ -17,9 +17,6 @@
package org.keycloak.provider;
-import java.util.Collections;
-import java.util.List;
-
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
index df6c37a..c159020 100644
--- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
+++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
@@ -26,7 +26,7 @@ import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.DefaultRoles;
-import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.StorageId;
import java.util.Collections;
@@ -135,7 +135,7 @@ public abstract class AbstractUserAdapter implements UserModel {
@Override
public boolean isMemberOf(GroupModel group) {
Set<GroupModel> roles = getGroups();
- return KeycloakModelUtils.isMember(roles, group);
+ return RoleUtils.isMember(roles, group);
}
@Override
@@ -172,8 +172,8 @@ public abstract class AbstractUserAdapter implements UserModel {
@Override
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
- return KeycloakModelUtils.hasRole(roles, role)
- || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
+ return RoleUtils.hasRole(roles, role)
+ || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
}
@Override
diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
index 865f454..c93dac4 100644
--- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
@@ -25,7 +25,7 @@ import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.DefaultRoles;
-import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
@@ -140,7 +140,7 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel {
@Override
public boolean isMemberOf(GroupModel group) {
Set<GroupModel> roles = getGroups();
- return KeycloakModelUtils.isMember(roles, group);
+ return RoleUtils.isMember(roles, group);
}
@Override
@@ -177,8 +177,8 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel {
@Override
public boolean hasRole(RoleModel role) {
Set<RoleModel> roles = getRoleMappings();
- return KeycloakModelUtils.hasRole(roles, role)
- || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
+ return RoleUtils.hasRole(roles, role)
+ || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
}
@Override
diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index bbd588e..c7ee486 100755
--- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -15,54 +15,21 @@
# limitations under the License.
#
-org.keycloak.models.UserFederationSpi
-org.keycloak.storage.UserStorageProviderSpi
-org.keycloak.storage.federated.UserFederatedStorageProviderSpi
-org.keycloak.mappers.UserFederationMapperSpi
-org.keycloak.models.RealmSpi
-org.keycloak.models.UserSessionSpi
-org.keycloak.models.UserSpi
-org.keycloak.models.session.UserSessionPersisterSpi
-org.keycloak.models.dblock.DBLockSpi
-org.keycloak.migration.MigrationSpi
-org.keycloak.events.EventListenerSpi
-org.keycloak.events.EventStoreSpi
-org.keycloak.exportimport.ExportSpi
-org.keycloak.exportimport.ImportSpi
-org.keycloak.timer.TimerSpi
-org.keycloak.scripting.ScriptingSpi
-org.keycloak.services.managers.BruteForceProtectorSpi
-org.keycloak.services.resource.RealmResourceSPI
-org.keycloak.protocol.ClientInstallationSpi
-org.keycloak.protocol.LoginProtocolSpi
-org.keycloak.protocol.ProtocolMapperSpi
-org.keycloak.broker.provider.IdentityProviderSpi
-org.keycloak.broker.provider.IdentityProviderMapperSpi
-org.keycloak.broker.social.SocialProviderSpi
-org.keycloak.forms.account.AccountSpi
-org.keycloak.forms.login.LoginFormsSpi
-org.keycloak.email.EmailSenderSpi
-org.keycloak.email.EmailTemplateSpi
-org.keycloak.theme.ThemeSpi
-org.keycloak.truststore.TruststoreSpi
-org.keycloak.connections.httpclient.HttpClientSpi
-org.keycloak.models.cache.CacheRealmProviderSpi
-org.keycloak.models.cache.CacheUserProviderSpi
-org.keycloak.authentication.AuthenticatorSpi
-org.keycloak.authentication.ClientAuthenticatorSpi
-org.keycloak.authentication.RequiredActionSpi
-org.keycloak.authentication.FormAuthenticatorSpi
-org.keycloak.authentication.FormActionSpi
-org.keycloak.cluster.ClusterSpi
-org.keycloak.authorization.policy.provider.PolicySpi
-org.keycloak.authorization.store.StoreFactorySpi
-org.keycloak.authorization.AuthorizationSpi
-org.keycloak.models.cache.authorization.CachedStoreFactorySpi
-org.keycloak.protocol.oidc.TokenIntrospectionSpi
-org.keycloak.policy.PasswordPolicySpi
-org.keycloak.policy.PasswordPolicyManagerSpi
-org.keycloak.transaction.TransactionManagerLookupSpi
-org.keycloak.credential.hash.PasswordHashSpi
-org.keycloak.credential.CredentialSpi
-org.keycloak.keys.PublicKeyStorageSpi
-org.keycloak.keys.KeySpi
\ No newline at end of file
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.storage.UserStorageProviderSpi
\ No newline at end of file
server-spi-private/pom.xml 98(+98 -0)
diff --git a/server-spi-private/pom.xml b/server-spi-private/pom.xml
new file mode 100755
index 0000000..a1d3df2
--- /dev/null
+++ b/server-spi-private/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<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>2.4.0.CR1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <name>Keycloak Server Private SPI</name>
+ <description/>
+
+ <properties>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.transaction</groupId>
+ <artifactId>jboss-transaction-api_1.2_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</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/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java
new file mode 100644
index 0000000..c41d371
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.dblock;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.RealmProviderFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class DBLockManager {
+
+ protected static final Logger logger = Logger.getLogger(DBLockManager.class);
+
+ private final KeycloakSession session;
+
+ public DBLockManager(KeycloakSession session) {
+ this.session = session;
+ }
+
+
+ public void checkForcedUnlock() {
+ if (Boolean.getBoolean("keycloak.dblock.forceUnlock")) {
+ DBLockProvider lock = getDBLock();
+ if (lock.supportsForcedUnlock()) {
+ logger.warn("Forced release of DB lock at startup requested by System property. Make sure to not use this in production environment! And especially when more cluster nodes are started concurrently.");
+ lock.releaseLock();
+ } else {
+ throw new IllegalStateException("Forced unlock requested, but provider " + lock + " doesn't support it");
+ }
+ }
+ }
+
+
+ // Try to detect ID from realmProvider
+ public DBLockProvider getDBLock() {
+ String realmProviderId = getRealmProviderId();
+ return session.getProvider(DBLockProvider.class, realmProviderId);
+ }
+
+ public DBLockProviderFactory getDBLockFactory() {
+ String realmProviderId = getRealmProviderId();
+ return (DBLockProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(DBLockProvider.class, realmProviderId);
+ }
+
+ private String getRealmProviderId() {
+ RealmProviderFactory realmProviderFactory = (RealmProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(RealmProvider.class);
+ return realmProviderFactory.getId();
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
new file mode 100644
index 0000000..d7d6053
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.dblock;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * Global database lock to ensure that some actions in DB can be done just be one cluster node at a time.
+ *
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface DBLockProvider extends Provider {
+
+
+ /**
+ * Try to retrieve DB lock or wait if retrieve was unsuccessful. Throw exception if lock can't be retrieved within specified timeout (900 seconds by default)
+ */
+ void waitForLock();
+
+
+ /**
+ * Release previously acquired lock
+ */
+ void releaseLock();
+
+ /**
+ * Check if I have lock
+ *
+ * @return
+ */
+ boolean hasLock();
+
+
+ /**
+ * @return true if provider supports forced unlock at startup
+ */
+ boolean supportsForcedUnlock();
+
+
+ /**
+ * Will destroy whole state of DB lock (drop table/collection to track locking).
+ * */
+ void destroyLockInfo();
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java
new file mode 100644
index 0000000..747a168
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.dblock;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface DBLockProviderFactory extends ProviderFactory<DBLockProvider> {
+
+ /**
+ * Useful for testing to override provided configuration
+ */
+ void setTimeouts(long lockRecheckTimeMillis, long lockWaitTimeoutMillis);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java
new file mode 100644
index 0000000..cd78f0f
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.dblock;
+
+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 DBLockSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "dblock";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return DBLockProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return DBLockProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java b/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java
new file mode 100644
index 0000000..f5e58d3
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.session;
+
+import org.keycloak.Config;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Persistence of userSessions is disabled . Useful just if you never need survive of userSessions/clientSessions
+ * among server restart. Offline sessions / offline tokens will be invalid after server restart as well,
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class DisabledUserSessionPersisterProvider implements UserSessionPersisterProviderFactory, UserSessionPersisterProvider {
+
+ public static final String ID = "disabled";
+
+ @Override
+ public UserSessionPersisterProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public void createUserSession(UserSessionModel userSession, boolean offline) {
+
+ }
+
+ @Override
+ public void createClientSession(ClientSessionModel clientSession, boolean offline) {
+
+ }
+
+ @Override
+ public void updateUserSession(UserSessionModel userSession, boolean offline) {
+
+ }
+
+ @Override
+ public void removeUserSession(String userSessionId, boolean offline) {
+
+ }
+
+ @Override
+ public void removeClientSession(String clientSessionId, boolean offline) {
+
+ }
+
+ @Override
+ public void onRealmRemoved(RealmModel realm) {
+
+ }
+
+ @Override
+ public void onClientRemoved(RealmModel realm, ClientModel client) {
+
+ }
+
+ @Override
+ public void onUserRemoved(RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public void clearDetachedUserSessions() {
+
+ }
+
+ @Override
+ public void updateAllTimestamps(int time) {
+
+ }
+
+ @Override
+ public List<UserSessionModel> loadUserSessions(int firstResult, int maxResults, boolean offline) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int getUserSessionsCount(boolean offline) {
+ return 0;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java
new file mode 100644
index 0000000..5990eea
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.session;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PersistentClientSessionModel {
+
+ private String clientSessionId;
+ private String userSessionId;
+ private String clientId;
+ private String userId;
+ private int timestamp;
+ private String data;
+
+ public String getClientSessionId() {
+ return clientSessionId;
+ }
+
+ public void setClientSessionId(String clientSessionId) {
+ this.clientSessionId = clientSessionId;
+ }
+
+ public String getUserSessionId() {
+ return userSessionId;
+ }
+
+ public void setUserSessionId(String userSessionId) {
+ this.userSessionId = userSessionId;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public int getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(int timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java
new file mode 100644
index 0000000..d7e0a04
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.session;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PersistentUserSessionModel {
+
+ private String userSessionId;
+ private int lastSessionRefresh;
+
+ private String data;
+
+ public String getUserSessionId() {
+ return userSessionId;
+ }
+
+ public void setUserSessionId(String userSessionId) {
+ this.userSessionId = userSessionId;
+ }
+
+ public int getLastSessionRefresh() {
+ return lastSessionRefresh;
+ }
+
+ public void setLastSessionRefresh(int lastSessionRefresh) {
+ this.lastSessionRefresh = lastSessionRefresh;
+ }
+
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java
new file mode 100644
index 0000000..2c0b98c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.session;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface UserSessionPersisterProviderFactory extends ProviderFactory<UserSessionPersisterProvider> {
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java
new file mode 100644
index 0000000..7e9dc4f
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.session;
+
+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 UserSessionPersisterSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "userSessionPersister";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return UserSessionPersisterProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return UserSessionPersisterProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ComponentUtil.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ComponentUtil.java
new file mode 100644
index 0000000..5efbb1c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ComponentUtil.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.component.ComponentFactory;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.ComponentRepresentation;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ComponentUtil {
+
+ public static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, ComponentRepresentation component) {
+ return getComponentConfigProperties(session, component.getProviderType(), component.getProviderId());
+ }
+
+ public static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, ComponentModel component) {
+ return getComponentConfigProperties(session, component.getProviderType(), component.getProviderId());
+ }
+
+ public static ComponentFactory getComponentFactory(KeycloakSession session, ComponentRepresentation component) {
+ return getComponentFactory(session, component.getProviderType(), component.getProviderId());
+ }
+
+ public static ComponentFactory getComponentFactory(KeycloakSession session, ComponentModel component) {
+ return getComponentFactory(session, component.getProviderType(), component.getProviderId());
+ }
+
+ private static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, String providerType, String providerId) {
+ try {
+ ComponentFactory componentFactory = getComponentFactory(session, providerType, providerId);
+ List<ProviderConfigProperty> l = componentFactory.getConfigProperties();
+ Map<String, ProviderConfigProperty> properties = new HashMap<>();
+ for (ProviderConfigProperty p : l) {
+ properties.put(p.getName(), p);
+ }
+ List<ProviderConfigProperty> common = componentFactory.getCommonProviderConfigProperties();
+ for (ProviderConfigProperty p : common) {
+ properties.put(p.getName(), p);
+ }
+
+ return properties;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static ComponentFactory getComponentFactory(KeycloakSession session, String providerType, String providerId) {
+ Class<? extends Provider> provider = session.getProviderClass(providerType);
+ if (provider == null) {
+ throw new IllegalArgumentException("Invalid provider type '" + providerType + "'");
+ }
+
+ ProviderFactory<? extends Provider> f = session.getKeycloakSessionFactory().getProviderFactory(provider, providerId);
+ if (f == null) {
+ throw new IllegalArgumentException("No such provider '" + providerId + "'");
+ }
+
+ ComponentFactory cf = (ComponentFactory) f;
+ return cf;
+ }
+
+ public static void notifyCreated(KeycloakSession session, RealmModel realm, ComponentModel model) {
+ ComponentFactory factory = getComponentFactory(session, model);
+ factory.onCreate(session, realm, model);
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/CredentialValidation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/CredentialValidation.java
new file mode 100755
index 0000000..598e3e9
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/CredentialValidation.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.models.OTPPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CredentialValidation {
+
+ public static boolean validOTP(RealmModel realm, String token, String secret) {
+ OTPPolicy policy = realm.getOTPPolicy();
+ if (policy.getType().equals(UserCredentialModel.TOTP)) {
+ TimeBasedOTP validator = new TimeBasedOTP(policy.getAlgorithm(), policy.getDigits(), policy.getPeriod(), policy.getLookAheadWindow());
+ return validator.validateTOTP(token, secret.getBytes());
+ } else {
+ HmacOTP validator = new HmacOTP(policy.getDigits(), policy.getAlgorithm(), policy.getLookAheadWindow());
+ int c = validator.validateHOTP(token, secret, policy.getInitialCounter());
+ return c > -1;
+ }
+
+ }
+
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultKeyProviders.java b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultKeyProviders.java
new file mode 100644
index 0000000..30ff7d7
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultKeyProviders.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.keys.KeyProvider;
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultKeyProviders {
+
+ public static void createProviders(RealmModel realm) {
+ ComponentModel generated = new ComponentModel();
+ generated.setName("rsa-generated");
+ generated.setParentId(realm.getId());
+ generated.setProviderId("rsa-generated");
+ generated.setProviderType(KeyProvider.class.getName());
+
+ MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
+ config.putSingle("priority", "100");
+ generated.setConfig(config);
+
+ realm.addComponentModel(generated);
+ }
+
+ public static void createProviders(RealmModel realm, String privateKeyPem, String certificatePem) {
+ ComponentModel rsa = new ComponentModel();
+ rsa.setName("rsa");
+ rsa.setParentId(realm.getId());
+ rsa.setProviderId("rsa");
+ rsa.setProviderType(KeyProvider.class.getName());
+
+ MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
+ config.putSingle("priority", "100");
+ config.putSingle("privateKey", privateKeyPem);
+ if (certificatePem != null) {
+ config.putSingle("certificate", certificatePem);
+ }
+ rsa.setConfig(config);
+
+ realm.addComponentModel(rsa);
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultRequiredActions.java b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultRequiredActions.java
new file mode 100755
index 0000000..db38b64
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultRequiredActions.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredActionProviderModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class DefaultRequiredActions {
+ public static void addActions(RealmModel realm) {
+ if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.VERIFY_EMAIL.name()) == null) {
+ RequiredActionProviderModel verifyEmail = new RequiredActionProviderModel();
+ verifyEmail.setEnabled(true);
+ verifyEmail.setAlias(UserModel.RequiredAction.VERIFY_EMAIL.name());
+ verifyEmail.setName("Verify Email");
+ verifyEmail.setProviderId(UserModel.RequiredAction.VERIFY_EMAIL.name());
+ verifyEmail.setDefaultAction(false);
+ realm.addRequiredActionProvider(verifyEmail);
+
+ }
+
+ if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_PROFILE.name()) == null) {
+ RequiredActionProviderModel updateProfile = new RequiredActionProviderModel();
+ updateProfile.setEnabled(true);
+ updateProfile.setAlias(UserModel.RequiredAction.UPDATE_PROFILE.name());
+ updateProfile.setName("Update Profile");
+ updateProfile.setProviderId(UserModel.RequiredAction.UPDATE_PROFILE.name());
+ updateProfile.setDefaultAction(false);
+ realm.addRequiredActionProvider(updateProfile);
+ }
+
+ if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name()) == null) {
+ RequiredActionProviderModel totp = new RequiredActionProviderModel();
+ totp.setEnabled(true);
+ totp.setAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name());
+ totp.setName("Configure OTP");
+ totp.setProviderId(UserModel.RequiredAction.CONFIGURE_TOTP.name());
+ totp.setDefaultAction(false);
+ realm.addRequiredActionProvider(totp);
+ }
+
+ if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_PASSWORD.name()) == null) {
+ RequiredActionProviderModel updatePassword = new RequiredActionProviderModel();
+ updatePassword.setEnabled(true);
+ updatePassword.setAlias(UserModel.RequiredAction.UPDATE_PASSWORD.name());
+ updatePassword.setName("Update Password");
+ updatePassword.setProviderId(UserModel.RequiredAction.UPDATE_PASSWORD.name());
+ updatePassword.setDefaultAction(false);
+ realm.addRequiredActionProvider(updatePassword);
+ }
+
+ if (realm.getRequiredActionProviderByAlias("terms_and_conditions") == null) {
+ RequiredActionProviderModel termsAndConditions = new RequiredActionProviderModel();
+ termsAndConditions.setEnabled(false);
+ termsAndConditions.setAlias("terms_and_conditions");
+ termsAndConditions.setName("Terms and Conditions");
+ termsAndConditions.setProviderId("terms_and_conditions");
+ termsAndConditions.setDefaultAction(false);
+ realm.addRequiredActionProvider(termsAndConditions);
+ }
+
+
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/FormMessage.java b/server-spi-private/src/main/java/org/keycloak/models/utils/FormMessage.java
new file mode 100755
index 0000000..e3b6a23
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/FormMessage.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.utils;
+
+import java.util.Arrays;
+
+/**
+ * Message (eg. error) to be shown in form.
+ *
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public class FormMessage {
+
+ /**
+ * Value used for {@link #field} if message is global (not tied to any specific form field)
+ */
+ public static final String GLOBAL = "global";
+
+ private String field;
+ private String message;
+ private Object[] parameters;
+
+ public FormMessage() {
+ }
+
+ /**
+ * Create message.
+ *
+ * @param field this message is for. {@link #GLOBAL} is used if null
+ * @param message key for the message
+ * @param parameters to be formatted into message
+ */
+ public FormMessage(String field, String message, Object... parameters) {
+ this(field, message);
+ this.parameters = parameters;
+ }
+
+ public FormMessage(String message, Object...parameters) {
+ this(null, message, parameters);
+ }
+
+ /**
+ * Create message without parameters.
+ *
+ * @param field this message is for. {@link #GLOBAL} is used if null
+ * @param message key for the message
+ */
+ public FormMessage(String field, String message) {
+ super();
+ if (field == null)
+ field = GLOBAL;
+ this.field = field;
+ this.message = message;
+ }
+
+ public String getField() {
+ return field;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ @Override
+ public String toString() {
+ return "FormMessage [field=" + field + ", message=" + message + ", parameters=" + Arrays.toString(parameters) + "]";
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/PostMigrationEvent.java b/server-spi-private/src/main/java/org/keycloak/models/utils/PostMigrationEvent.java
new file mode 100644
index 0000000..73889e0
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/PostMigrationEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.provider.ProviderEvent;
+
+/**
+ * Executed at startup after model migration is finished
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PostMigrationEvent implements ProviderEvent {
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java
new file mode 100644
index 0000000..e2c3401
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+
+/**
+ * Helper interface used just because RealmManager is in keycloak-services and not accessible for ImportUtils
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface RealmImporter {
+
+ RealmModel importRealm(RealmRepresentation rep);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RealmInfoUtil.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RealmInfoUtil.java
new file mode 100644
index 0000000..e55e065
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RealmInfoUtil.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RealmInfoUtil {
+
+ public static int getDettachedClientSessionLifespan(RealmModel realm) {
+ int lifespan = realm.getAccessCodeLifespanLogin();
+ if (realm.getAccessCodeLifespanUserAction() > lifespan) {
+ lifespan = realm.getAccessCodeLifespanUserAction();
+ }
+ if (realm.getAccessCodeLifespan() > lifespan) {
+ lifespan = realm.getAccessCodeLifespan();
+ }
+ return lifespan;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java
new file mode 100644
index 0000000..2dbec10
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+/**
+ * A criteria that matches a property based on its annotations
+ *
+ * @see PropertyCriteria
+ */
+public class AnnotatedPropertyCriteria implements PropertyCriteria {
+ private final Class<? extends Annotation> annotationClass;
+
+ public AnnotatedPropertyCriteria(Class<? extends Annotation> annotationClass) {
+ this.annotationClass = annotationClass;
+ }
+
+ @Override
+ public boolean methodMatches(Method m) {
+ return m.isAnnotationPresent(annotationClass);
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java
new file mode 100644
index 0000000..fac6a89
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.lang.reflect.Method;
+
+public interface MethodProperty<V> extends Property<V> {
+
+ Method getAnnotatedElement();
+}
\ No newline at end of file
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java
new file mode 100644
index 0000000..e81734a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.beans.Introspector;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * A criteria that matches a property based on name
+ *
+ * @see PropertyCriteria
+ */
+public class NamedPropertyCriteria implements PropertyCriteria {
+ private final String[] propertyNames;
+
+ public NamedPropertyCriteria(String... propertyNames) {
+ this.propertyNames = propertyNames;
+ }
+
+ public boolean fieldMatches(Field f) {
+ for (String propertyName : propertyNames) {
+ if (propertyName.equals(f.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean methodMatches(Method m) {
+ String[] validPrefix = {"get", "is"};
+ for (String propertyName : propertyNames) {
+ for (String prefix : validPrefix) {
+ if (m.getName().startsWith(prefix) &&
+ Introspector.decapitalize(m.getName().substring(prefix.length())).equals(propertyName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Properties.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Properties.java
new file mode 100755
index 0000000..ebb1381
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Properties.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.lang.reflect.Method;
+
+/**
+ * Utility class for working with JavaBean style properties
+ *
+ * @see Property
+ */
+public class Properties {
+
+ private Properties() {
+ }
+
+ /**
+ * Create a JavaBean style property from the specified method
+ *
+ * @param <V>
+ * @param method
+ *
+ * @return
+ *
+ * @throws IllegalArgumentException if the method does not match JavaBean conventions
+ * @see http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html
+ */
+ public static <V> MethodProperty<V> createProperty(Method method) {
+ return new MethodPropertyImpl<V>(method);
+ }
+
+ /**
+ * Indicates whether this method is a valid property method.
+ */
+ public static <V> boolean isProperty(Method method) {
+ try {
+ new MethodPropertyImpl<V>(method);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+}
+
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Property.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Property.java
new file mode 100644
index 0000000..fcf6fe4
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Property.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Member;
+import java.lang.reflect.Type;
+
+/**
+ * A representation of a JavaBean style property
+ *
+ * @param <V> the type of the properties value
+ *
+ * @see Properties
+ */
+public interface Property<V> {
+
+ /**
+ * Returns the name of the property. If the property is a field, then the field name is returned. Otherwise, if the
+ * property is a method, then the name that is returned is the getter method name without the "get" or "is" prefix,
+ * and a lower case first letter.
+ *
+ * @return The name of the property
+ */
+ String getName();
+
+ /**
+ * Returns the property type
+ *
+ * @return The property type
+ */
+ Type getBaseType();
+
+ /**
+ * Returns the property type
+ *
+ * @return The property type
+ */
+ Class<V> getJavaClass();
+
+ /**
+ * Get the element responsible for retrieving the property value
+ *
+ * @return
+ */
+ AnnotatedElement getAnnotatedElement();
+
+ /**
+ * Get the member responsible for retrieving the property value
+ *
+ * @return
+ */
+ Member getMember();
+
+ /**
+ * Returns the property value for the specified bean. The property to be returned is either a field or getter
+ * method.
+ *
+ * @param bean The bean to read the property from
+ *
+ * @return The property value
+ *
+ * @throws ClassCastException if the value is not of the type V
+ */
+ V getValue(Object instance);
+
+ /**
+ * This method sets the property value for a specified bean to the specified value. The property to be set is either
+ * a field or setter method.
+ *
+ * @param bean The bean containing the property to set
+ * @param value The new property value
+ */
+ void setValue(Object instance, V value);
+
+ /**
+ * Returns the class that declares the property
+ *
+ * @return
+ */
+ Class<?> getDeclaringClass();
+
+ /**
+ * Indicates whether this is a read-only property
+ *
+ * @return
+ */
+ boolean isReadOnly();
+
+ /**
+ * Calls the setAccessible method on the underlying member(s).
+ * <p/>
+ * The operation should be performed within a {@link PrivilegedAction}
+ */
+ void setAccessible();
+
+ /**
+ * Indicates whether the given <code>annotation</code> is defined for this property. This method will consider
+ * the annotations present in both field and accessor method.
+ *
+ * @param annotation The Annotation to check.
+ *
+ * @return True if the annotation is defined. Otherwise is false.
+ */
+ boolean isAnnotationPresent(Class<? extends Annotation> annotation);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java
new file mode 100755
index 0000000..2f3b8b1
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.lang.reflect.Method;
+
+/**
+ * <p> A property criteria can be used to filter the properties found by a {@link PropertyQuery} </p> <p/> <p>
+ * DeltaSpike provides a number of property queries ( {@link TypedPropertyCriteria}, {@link NamedPropertyCriteria} and
+ * {@link AnnotatedPropertyCriteria}), or you can create a custom query by implementing this interface. </p>
+ *
+ * @see PropertyQuery#addCriteria(PropertyCriteria)
+ * @see PropertyQueries
+ * @see TypedPropertyCriteria
+ * @see AnnotatedPropertyCriteria
+ * @see NamedPropertyCriteria
+ */
+public interface PropertyCriteria {
+
+ /**
+ * Tests whether the specified method matches the criteria
+ *
+ * @param m
+ *
+ * @return true if the method matches
+ */
+ boolean methodMatches(Method m);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java
new file mode 100644
index 0000000..debabb7
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+/**
+ * Utilities for working with property queries
+ *
+ * @see PropertyQuery
+ */
+public class PropertyQueries {
+
+ private PropertyQueries() {
+ }
+
+ /**
+ * Create a new {@link PropertyQuery}
+ *
+ * @param <V>
+ * @param targetClass
+ *
+ * @return
+ */
+ public static <V> PropertyQuery<V> createQuery(Class<?> targetClass) {
+ return new PropertyQuery<V>(targetClass);
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java
new file mode 100644
index 0000000..5d7cbf5
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p> Queries a target class for properties that match certain criteria. A property may either be a private or public
+ * field, declared by the target class or inherited from a superclass, or a public method declared by the target class
+ * or inherited from any of its superclasses. For properties that are exposed via a method, the property must be a
+ * JavaBean style property, i.e. it must provide both an accessor and mutator method according to the JavaBean
+ * specification. </p> <p/> <p> This class is not thread-safe, however the result returned by the getResultList() method
+ * is. </p>
+ *
+ * @see PropertyQueries
+ * @see PropertyCriteria
+ */
+public class PropertyQuery<V> {
+ private final Class<?> targetClass;
+ private final List<PropertyCriteria> criteria;
+
+ PropertyQuery(Class<?> targetClass) {
+ if (targetClass == null) {
+ throw new IllegalArgumentException("targetClass parameter may not be null");
+ }
+
+ this.targetClass = targetClass;
+ this.criteria = new ArrayList<PropertyCriteria>();
+ }
+
+ /**
+ * Add a criteria to query
+ *
+ * @param criteria the criteria to add
+ */
+ public PropertyQuery<V> addCriteria(PropertyCriteria criteria) {
+ this.criteria.add(criteria);
+ return this;
+ }
+
+ /**
+ * Get the first result from the query, causing the query to be run.
+ *
+ * @return the first result, or null if there are no results
+ */
+ public Property<V> getFirstResult() {
+ Map<String, Property<V>> results = getResultList();
+ return results.isEmpty() ? null : results.values().iterator().next();
+ }
+
+ /**
+ * Get the first result from the query that is not marked as read only, causing the query to be run.
+ *
+ * @return the first writable result, or null if there are no results
+ */
+ public Property<V> getFirstWritableResult() {
+ Map<String, Property<V>> results = getWritableResultList();
+ return results.isEmpty() ? null : results.values().iterator().next();
+ }
+
+ /**
+ * Get a single result from the query, causing the query to be run. An exception is thrown if the query does not
+ * return exactly one result.
+ *
+ * @return the single result
+ *
+ * @throws RuntimeException if the query does not return exactly one result
+ */
+ public Property<V> getSingleResult() {
+ Map<String, Property<V>> results = getResultList();
+ if (results.size() == 1) {
+ return results.values().iterator().next();
+ } else if (results.isEmpty()) {
+ throw new RuntimeException(
+ "Expected one property match, but the criteria did not match any properties on " +
+ targetClass.getName());
+ } else {
+ throw new RuntimeException("Expected one property match, but the criteria matched " + results.size() +
+ " properties on " + targetClass.getName());
+ }
+ }
+
+ /**
+ * Get a single result from the query that is not marked as read only, causing the query to be run. An exception is
+ * thrown if the query does not return exactly one result.
+ *
+ * @return the single writable result
+ *
+ * @throws RuntimeException if the query does not return exactly one result
+ */
+ public Property<V> getWritableSingleResult() {
+ Map<String, Property<V>> results = getWritableResultList();
+ if (results.size() == 1) {
+ return results.values().iterator().next();
+ } else if (results.isEmpty()) {
+ throw new RuntimeException(
+ "Expected one property match, but the criteria did not match any properties on " +
+ targetClass.getName());
+ } else {
+ throw new RuntimeException("Expected one property match, but the criteria matched " +
+ results.size() + " properties on " + targetClass.getName());
+ }
+ }
+
+ /**
+ * Get the result from the query, causing the query to be run.
+ *
+ * @return the results, or an empty list if there are no results
+ */
+ public Map<String, Property<V>> getResultList() {
+ return getResultList(false);
+ }
+
+ /**
+ * Get the non read only results from the query, causing the query to be run.
+ *
+ * @return the results, or an empty list if there are no results
+ */
+ public Map<String, Property<V>> getWritableResultList() {
+ return getResultList(true);
+ }
+
+ /**
+ * Get the result from the query, causing the query to be run.
+ *
+ * @param writable if this query should only return properties that are not read only
+ *
+ * @return the results, or an empty list if there are no results
+ */
+ private Map<String, Property<V>> getResultList(boolean writable) {
+ Map<String, Property<V>> properties = new HashMap<String, Property<V>>();
+
+ // First check public accessor methods (we ignore private methods)
+ for (Method method : targetClass.getMethods()) {
+ if (!(method.getName().startsWith("is") || method.getName().startsWith("get"))) {
+ continue;
+ }
+
+ boolean match = true;
+ for (PropertyCriteria c : criteria) {
+ if (!c.methodMatches(method)) {
+ match = false;
+ break;
+ }
+ }
+
+ if (match) {
+ MethodProperty<V> property = Properties.<V>createProperty(method);
+
+ if (!writable || !property.isReadOnly()) {
+ properties.put(property.getName(), property);
+ }
+ }
+ }
+
+ return Collections.unmodifiableMap(properties);
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java
new file mode 100644
index 0000000..1fbafe1
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * A criteria that matches a property based on its type
+ *
+ * @see PropertyCriteria
+ */
+public class TypedPropertyCriteria implements PropertyCriteria {
+
+ /**
+ * <p> Different options can be used to match a specific property based on its type. Regardless of the option
+ * chosen, if the property type equals the <code>propertyClass</code> it will be selected. <p/> <ul> <li>SUB_TYPE:
+ * Also consider properties where its type is a subtype of <code>propertyClass</code>. .</li> <li>SUPER_TYPE: Also
+ * consider properties where its type is a superclass or superinterface of <code>propertyClass</code>. .</li> </ul>
+ * </p>
+ */
+ public static enum MatchOption {
+ SUB_TYPE, SUPER_TYPE, ALL
+ }
+
+ private final Class<?> propertyClass;
+ private final MatchOption matchOption;
+
+ public TypedPropertyCriteria(Class<?> propertyClass) {
+ this(propertyClass, null);
+ }
+
+ public TypedPropertyCriteria(Class<?> propertyClass, MatchOption matchOption) {
+ if (propertyClass == null) {
+ throw new IllegalArgumentException("Property class can not be null.");
+ }
+ this.propertyClass = propertyClass;
+ this.matchOption = matchOption;
+ }
+
+ public boolean fieldMatches(Field f) {
+ return match(f.getType());
+ }
+
+ public boolean methodMatches(Method m) {
+ return match(m.getReturnType());
+ }
+
+ private boolean match(Class<?> type) {
+ if (propertyClass.equals(type)) {
+ return true;
+ } else {
+ boolean matchSubType = propertyClass.isAssignableFrom(type);
+
+ if (MatchOption.SUB_TYPE == this.matchOption) {
+ return matchSubType;
+ }
+
+ boolean matchSuperType = type.isAssignableFrom(propertyClass);
+
+ if (MatchOption.SUPER_TYPE == this.matchOption) {
+ return matchSuperType;
+ }
+
+ if (MatchOption.ALL == this.matchOption) {
+ return matchSubType || matchSuperType;
+ }
+ }
+
+ return false;
+ }
+}
+
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java b/server-spi-private/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java
new file mode 100755
index 0000000..8b4c25a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.common.util.Base64;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
+/**
+ * <p>
+ * Password that uses SHA to encode passwords. You can always change the SHA strength by specifying a valid
+ * integer when creating a new instance.
+ * </p>
+ * <p>Passwords are returned with a Base64 encoding.</p>
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Silva</a>
+ *
+ */
+public class SHAPasswordEncoder {
+
+ private int strength;
+
+ public SHAPasswordEncoder(int strength) {
+ this.strength = strength;
+ }
+
+ public String encode(String rawPassword) {
+ MessageDigest messageDigest = getMessageDigest();
+
+ String encodedPassword = null;
+
+ try {
+ byte[] digest = messageDigest.digest(rawPassword.getBytes("UTF-8"));
+ encodedPassword = Base64.encodeBytes(digest);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("Credential could not be encoded");
+ }
+
+ return encodedPassword;
+ }
+
+ public boolean verify(String rawPassword, String encodedPassword) {
+ return encode(rawPassword).equals(encodedPassword);
+ }
+
+ protected final MessageDigest getMessageDigest() throws IllegalArgumentException {
+ String algorithm = "SHA-" + this.strength;
+
+ try {
+ return MessageDigest.getInstance(algorithm);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("invalid credential encoding algorithm");
+ }
+ }
+
+ public int getStrength() {
+ return this.strength;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/StripSecretsUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/StripSecretsUtils.java
new file mode 100644
index 0000000..26f5adc
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/StripSecretsUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.idm.ComponentRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class StripSecretsUtils {
+
+ public static ComponentRepresentation strip(KeycloakSession session, ComponentRepresentation rep) {
+ Map<String, ProviderConfigProperty> configProperties = ComponentUtil.getComponentConfigProperties(session, rep);
+ if (rep.getConfig() == null) {
+ return rep;
+ }
+
+ Iterator<Map.Entry<String, List<String>>> itr = rep.getConfig().entrySet().iterator();
+ while (itr.hasNext()) {
+ Map.Entry<String, List<String>> next = itr.next();
+ ProviderConfigProperty configProperty = configProperties.get(next.getKey());
+ if (configProperty != null) {
+ if (configProperty.isSecret()) {
+ next.setValue(Collections.singletonList(ComponentRepresentation.SECRET_VALUE));
+ }
+ } else {
+ itr.remove();
+ }
+ }
+ return rep;
+ }
+
+ public static RealmRepresentation strip(RealmRepresentation rep) {
+ if (rep.getSmtpServer() != null && rep.getSmtpServer().containsKey("password")) {
+ rep.getSmtpServer().put("password", ComponentRepresentation.SECRET_VALUE);
+ }
+ return rep;
+ }
+
+ public static IdentityProviderRepresentation strip(IdentityProviderRepresentation rep) {
+ if (rep.getConfig() != null && rep.getConfig().containsKey("clientSecret")) {
+ rep.getConfig().put("clientSecret", ComponentRepresentation.SECRET_VALUE);
+ }
+ return rep;
+ }
+
+}
\ No newline at end of file
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java b/server-spi-private/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java
new file mode 100755
index 0000000..d02bc55
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+/**
+ * TOTP: Time-based One-time Password Algorithm Based on http://tools.ietf.org/html/draft-mraihi-totp-timebased-06
+ *
+ * @author anil saldhana
+ * @since Sep 20, 2010
+ */
+public class TimeBasedOTP extends HmacOTP {
+
+ public static final int DEFAULT_INTERVAL_SECONDS = 30;
+ public static final int DEFAULT_DELAY_WINDOW = 1;
+
+ private Clock clock;
+
+ public TimeBasedOTP() {
+ this(DEFAULT_ALGORITHM, DEFAULT_NUMBER_DIGITS, DEFAULT_INTERVAL_SECONDS, DEFAULT_DELAY_WINDOW);
+ }
+
+ /**
+ * @param algorithm the encryption algorithm
+ * @param numberDigits the number of digits for tokens
+ * @param timeIntervalInSeconds the number of seconds a token is valid
+ * @param lookAheadWindow the number of previous intervals that should be used to validate tokens.
+ */
+ public TimeBasedOTP(String algorithm, int numberDigits, int timeIntervalInSeconds, int lookAheadWindow) {
+ super(numberDigits, algorithm, lookAheadWindow);
+ this.clock = new Clock(timeIntervalInSeconds);
+ }
+
+ /**
+ * <p>Generates a token.</p>
+ *
+ * @param secretKey the secret key to derive the token from.
+ */
+ public String generateTOTP(String secretKey) {
+ long T = this.clock.getCurrentInterval();
+
+ String steps = Long.toHexString(T).toUpperCase();
+
+ // Just get a 16 digit string
+ while (steps.length() < 16)
+ steps = "0" + steps;
+
+ return generateOTP(secretKey, steps, this.numberDigits, this.algorithm);
+ }
+
+ /**
+ * <p>Validates a token using a secret key.</p>
+ *
+ * @param token OTP string to validate
+ * @param secret Shared secret
+ * @return
+ */
+ public boolean validateTOTP(String token, byte[] secret) {
+ long currentInterval = this.clock.getCurrentInterval();
+
+ for (int i = this.lookAheadWindow; i >= 0; --i) {
+ String steps = Long.toHexString(currentInterval - i).toUpperCase();
+
+ // Just get a 16 digit string
+ while (steps.length() < 16)
+ steps = "0" + steps;
+
+ String candidate = generateOTP(new String(secret), steps, this.numberDigits, this.algorithm);
+
+ if (candidate.equals(token)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void setCalendar(Calendar calendar) {
+ this.clock.setCalendar(calendar);
+ }
+
+ private class Clock {
+
+ private final int interval;
+ private Calendar calendar;
+
+ public Clock(int interval) {
+ this.interval = interval;
+ }
+
+ public long getCurrentInterval() {
+ Calendar currentCalendar = this.calendar;
+
+ if (currentCalendar == null) {
+ currentCalendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
+ }
+
+ return (currentCalendar.getTimeInMillis() / 1000) / this.interval;
+ }
+
+ public void setCalendar(Calendar calendar) {
+ this.calendar = calendar;
+ }
+ }
+}
\ No newline at end of file
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/UserModelDelegate.java b/server-spi-private/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
new file mode 100755
index 0000000..a08e18a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+
+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 UserModelDelegate implements UserModel {
+ protected UserModel delegate;
+
+ public UserModelDelegate(UserModel delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public String getId() {
+ return delegate.getId();
+ }
+
+ @Override
+ public String getUsername() {
+ return delegate.getUsername();
+ }
+
+ @Override
+ public void setUsername(String username) {
+ delegate.setUsername(username);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return delegate.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ delegate.setEnabled(enabled);
+ }
+
+ @Override
+ public void setSingleAttribute(String name, String value) {
+ delegate.setSingleAttribute(name, value);
+ }
+
+ @Override
+ public void setAttribute(String name, List<String> values) {
+ delegate.setAttribute(name, values);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ delegate.removeAttribute(name);
+ }
+
+ @Override
+ public String getFirstAttribute(String name) {
+ return delegate.getFirstAttribute(name);
+ }
+
+ @Override
+ public List<String> getAttribute(String name) {
+ return delegate.getAttribute(name);
+ }
+
+ @Override
+ public Map<String, List<String>> getAttributes() {
+ return delegate.getAttributes();
+ }
+
+ @Override
+ public Set<String> getRequiredActions() {
+ return delegate.getRequiredActions();
+ }
+
+ @Override
+ public void addRequiredAction(String action) {
+ delegate.addRequiredAction(action);
+ }
+
+ @Override
+ public void removeRequiredAction(String action) {
+ delegate.removeRequiredAction(action);
+ }
+
+ @Override
+ public void addRequiredAction(RequiredAction action) {
+ delegate.addRequiredAction(action);
+ }
+
+ @Override
+ public void removeRequiredAction(RequiredAction action) {
+ delegate.removeRequiredAction(action);
+ }
+
+ @Override
+ public String getFirstName() {
+ return delegate.getFirstName();
+ }
+
+ @Override
+ public void setFirstName(String firstName) {
+ delegate.setFirstName(firstName);
+ }
+
+ @Override
+ public String getLastName() {
+ return delegate.getLastName();
+ }
+
+ @Override
+ public void setLastName(String lastName) {
+ delegate.setLastName(lastName);
+ }
+
+ @Override
+ public String getEmail() {
+ return delegate.getEmail();
+ }
+
+ @Override
+ public void setEmail(String email) {
+ delegate.setEmail(email);
+ }
+
+ @Override
+ public boolean isEmailVerified() {
+ return delegate.isEmailVerified();
+ }
+
+ @Override
+ public void setEmailVerified(boolean verified) {
+ delegate.setEmailVerified(verified);
+ }
+
+ @Override
+ public Set<RoleModel> getRealmRoleMappings() {
+ return delegate.getRealmRoleMappings();
+ }
+
+ @Override
+ public Set<RoleModel> getClientRoleMappings(ClientModel app) {
+ return delegate.getClientRoleMappings(app);
+ }
+
+ @Override
+ public boolean hasRole(RoleModel role) {
+ return delegate.hasRole(role);
+ }
+
+ @Override
+ public void grantRole(RoleModel role) {
+ delegate.grantRole(role);
+ }
+
+ @Override
+ public Set<RoleModel> getRoleMappings() {
+ return delegate.getRoleMappings();
+ }
+
+ @Override
+ public void deleteRoleMapping(RoleModel role) {
+ delegate.deleteRoleMapping(role);
+ }
+
+ @Override
+ public String getFederationLink() {
+ return delegate.getFederationLink();
+ }
+
+ @Override
+ public void setFederationLink(String link) {
+ delegate.setFederationLink(link);
+ }
+
+ @Override
+ public String getServiceAccountClientLink() {
+ return delegate.getServiceAccountClientLink();
+ }
+
+ @Override
+ public void setServiceAccountClientLink(String clientInternalId) {
+ delegate.setServiceAccountClientLink(clientInternalId);
+ }
+
+ public UserModel getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public Long getCreatedTimestamp(){
+ return delegate.getCreatedTimestamp();
+ }
+
+ @Override
+ public void setCreatedTimestamp(Long timestamp){
+ delegate.setCreatedTimestamp(timestamp);
+ }
+
+ @Override
+ public Set<GroupModel> getGroups() {
+ return delegate.getGroups();
+ }
+
+ @Override
+ public void joinGroup(GroupModel group) {
+ delegate.joinGroup(group);
+
+ }
+
+ @Override
+ public void leaveGroup(GroupModel group) {
+ delegate.leaveGroup(group);
+
+ }
+
+ @Override
+ public boolean isMemberOf(GroupModel group) {
+ return delegate.isMemberOf(group);
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java
new file mode 100644
index 0000000..b1bfc0c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultPasswordPolicyManagerProvider implements PasswordPolicyManagerProvider {
+
+ private KeycloakSession session;
+
+ public DefaultPasswordPolicyManagerProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ for (PasswordPolicyProvider p : getProviders(realm, session)) {
+ PolicyError policyError = p.validate(realm, user, password);
+ if (policyError != null) {
+ return policyError;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public PolicyError validate(String user, String password) {
+ for (PasswordPolicyProvider p : getProviders(session)) {
+ PolicyError policyError = p.validate(user, password);
+ if (policyError != null) {
+ return policyError;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ private List<PasswordPolicyProvider> getProviders(KeycloakSession session) {
+ return getProviders(session.getContext().getRealm(), session);
+
+ }
+
+ private List<PasswordPolicyProvider> getProviders(RealmModel realm, KeycloakSession session) {
+ LinkedList<PasswordPolicyProvider> list = new LinkedList<>();
+ PasswordPolicy policy = realm.getPasswordPolicy();
+ for (String id : policy.getPolicies()) {
+ PasswordPolicyProvider provider = session.getProvider(PasswordPolicyProvider.class, id);
+ list.add(provider);
+ }
+ return list;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java
new file mode 100644
index 0000000..b8aabd4
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultPasswordPolicyManagerProviderFactory implements PasswordPolicyManagerProviderFactory {
+
+ @Override
+ public PasswordPolicyManagerProvider create(KeycloakSession session) {
+ return new DefaultPasswordPolicyManagerProvider(session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return "default";
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProvider.java
new file mode 100644
index 0000000..391317e
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DigitsPasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final String ERROR_MESSAGE = "invalidPasswordMinDigitsMessage";
+
+ private KeycloakContext context;
+
+ public DigitsPasswordPolicyProvider(KeycloakContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ int min = context.getRealm().getPasswordPolicy().getPolicyConfig(DigitsPasswordPolicyProviderFactory.ID);
+ int count = 0;
+ for (char c : password.toCharArray()) {
+ if (Character.isDigit(c)) {
+ count++;
+ }
+ }
+ return count < min ? new PolicyError(ERROR_MESSAGE, min) : null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return validate(user.getUsername(), password);
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : 1;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..d7fce9c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DigitsPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ static final String ID = "digits";
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new DigitsPasswordPolicyProvider(session.getContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Digits";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.INT_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return "1";
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..b9f6f4c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ForceExpiredPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory, PasswordPolicyProvider {
+
+ public static final int DEFAULT_VALUE = 365;
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return PasswordPolicy.FORCE_EXPIRED_ID;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return null;
+ }
+
+ @Override
+ public PolicyError validate(String user, String password) {
+ return null;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Expire Password";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.STRING_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return String.valueOf(DEFAULT_VALUE);
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : DEFAULT_VALUE;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..303ba79
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HashAlgorithmPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory, PasswordPolicyProvider {
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return PasswordPolicy.HASH_ALGORITHM_ID;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return null;
+ }
+
+ @Override
+ public PolicyError validate(String user, String password) {
+ return null;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Hashing Algorithm";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.STRING_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return PasswordPolicy.HASH_ALGORITHM_DEFAULT;
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? value : PasswordPolicy.HASH_ALGORITHM_DEFAULT;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..695ab28
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HashIterationsPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
+
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public String getId() {
+ return PasswordPolicy.HASH_ITERATIONS_ID;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return null;
+ }
+
+ @Override
+ public PolicyError validate(String user, String password) {
+ return null;
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : PasswordPolicy.HASH_ITERATIONS_DEFAULT;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Hashing Iterations";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.INT_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return String.valueOf(PasswordPolicy.HASH_ITERATIONS_DEFAULT);
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java
new file mode 100644
index 0000000..004d540
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.jboss.logging.Logger;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.credential.hash.PasswordHashProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HistoryPasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final Logger logger = Logger.getLogger(HistoryPasswordPolicyProvider.class);
+ private static final String ERROR_MESSAGE = "invalidPasswordHistoryMessage";
+
+ private KeycloakSession session;
+
+ public HistoryPasswordPolicyProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ return null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ PasswordPolicy policy = session.getContext().getRealm().getPasswordPolicy();
+ int passwordHistoryPolicyValue = policy.getPolicyConfig(PasswordPolicy.PASSWORD_HISTORY_ID);
+ if (passwordHistoryPolicyValue != -1) {
+ List<CredentialModel> storedPasswords = session.userCredentialManager().getStoredCredentialsByType(realm, user, CredentialModel.PASSWORD);
+ for (CredentialModel cred : storedPasswords) {
+ PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, cred.getAlgorithm());
+ if (hash == null) continue;
+ if (hash.verify(password, cred)) {
+ return new PolicyError(ERROR_MESSAGE, passwordHistoryPolicyValue);
+ }
+ }
+ List<CredentialModel> passwordHistory = session.userCredentialManager().getStoredCredentialsByType(realm, user, CredentialModel.PASSWORD_HISTORY);
+ for (CredentialModel cred : passwordHistory) {
+ PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, cred.getAlgorithm());
+ if (hash.verify(password, cred)) {
+ return new PolicyError(ERROR_MESSAGE, passwordHistoryPolicyValue);
+ }
+
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : HistoryPasswordPolicyProviderFactory.DEFAULT_VALUE;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..5f8a9d1
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.PasswordPolicy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HistoryPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ public static final Integer DEFAULT_VALUE = 3;
+
+ @Override
+ public String getId() {
+ return PasswordPolicy.PASSWORD_HISTORY_ID;
+ }
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new HistoryPasswordPolicyProvider(session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Not Recently Used";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.INT_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return String.valueOf(HistoryPasswordPolicyProviderFactory.DEFAULT_VALUE);
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProvider.java
new file mode 100644
index 0000000..5ba71fe
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LengthPasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final String ERROR_MESSAGE = "invalidPasswordMinLengthMessage";
+
+ private KeycloakContext context;
+
+ public LengthPasswordPolicyProvider(KeycloakContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ int min = context.getRealm().getPasswordPolicy().getPolicyConfig(LengthPasswordPolicyProviderFactory.ID);
+ return password.length() < min ? new PolicyError(ERROR_MESSAGE, min) : null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return validate(user.getUsername(), password);
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : 8;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..a60c250
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LengthPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ static final String ID = "length";
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new LengthPasswordPolicyProvider(session.getContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Minimum Length";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.INT_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return "8";
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java
new file mode 100644
index 0000000..f080d00
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LowerCasePasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final String ERROR_MESSAGE = "invalidPasswordMinLowerCaseCharsMessage";
+
+ private KeycloakContext context;
+
+ public LowerCasePasswordPolicyProvider(KeycloakContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ int min = context.getRealm().getPasswordPolicy().getPolicyConfig(LowerCasePasswordPolicyProviderFactory.ID);
+ int count = 0;
+ for (char c : password.toCharArray()) {
+ if (Character.isLowerCase(c)) {
+ count++;
+ }
+ }
+ return count < min ? new PolicyError(ERROR_MESSAGE, min) : null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return validate(user.getUsername(), password);
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : 1;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..7e96dcf
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LowerCasePasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ public static final String ID = "lowerCase";
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new LowerCasePasswordPolicyProvider(session.getContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Lowercase Characters";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.INT_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return "1";
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java
new file mode 100644
index 0000000..f08edab
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class NotUsernamePasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final String ERROR_MESSAGE = "invalidPasswordNotUsernameMessage";
+
+ private KeycloakContext context;
+
+ public NotUsernamePasswordPolicyProvider(KeycloakContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ if (username == null) {
+ return null;
+ }
+ return username.equals(password) ? new PolicyError(ERROR_MESSAGE) : null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return validate(user.getUsername(), password);
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return null;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..30ebbff
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class NotUsernamePasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ static final String ID = "notUsername";
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new NotUsernamePasswordPolicyProvider(session.getContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Not Username";
+ }
+
+ @Override
+ public String getConfigType() {
+ return null;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return null;
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProvider.java
new file mode 100644
index 0000000..e5e8497
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:roelof.naude@epiuse.com">Roelof Naude</a>
+ */
+public interface PasswordPolicyManagerProvider extends Provider {
+
+ PolicyError validate(RealmModel realm, UserModel user, String password);
+ PolicyError validate(String user, String password);
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java
new file mode 100644
index 0000000..f68701e
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:roelof.naude@epiuse.com">Roelof Naude</a>
+ */
+public interface PasswordPolicyManagerProviderFactory extends ProviderFactory<PasswordPolicyManagerProvider> {
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerSpi.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerSpi.java
new file mode 100644
index 0000000..266cf1f
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerSpi.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+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 PasswordPolicyManagerSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "password-policy-manager";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return PasswordPolicyManagerProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return PasswordPolicyManagerProviderFactory.class;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..44714e3
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyProviderFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:roelof.naude@epiuse.com">Roelof Naude</a>
+ */
+public interface PasswordPolicyProviderFactory extends ProviderFactory<PasswordPolicyProvider> {
+
+ String getDisplayName();
+ String getConfigType();
+ String getDefaultConfigValue();
+ boolean isMultiplSupported();
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java
new file mode 100644
index 0000000..97ad19a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:roelof.naude@epiuse.com">Roelof Naude</a>
+ */
+public class PasswordPolicySpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "password-policy";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return PasswordPolicyProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return PasswordPolicyProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java
new file mode 100644
index 0000000..52c83b8
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RegexPatternsPasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final String ERROR_MESSAGE = "invalidPasswordRegexPatternMessage";
+
+ private KeycloakContext context;
+
+ public RegexPatternsPasswordPolicyProvider(KeycloakContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ Pattern pattern = context.getRealm().getPasswordPolicy().getPolicyConfig(RegexPatternsPasswordPolicyProviderFactory.ID);
+ Matcher matcher = pattern.matcher(password);
+ if (!matcher.matches()) {
+ return new PolicyError(ERROR_MESSAGE, pattern.pattern());
+ }
+ return null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return validate(user.getUsername(), password);
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ if (value == null) {
+ throw new IllegalArgumentException("Config required");
+ }
+ return Pattern.compile(value);
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..c0ce732
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RegexPatternsPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ static final String ID = "regexPattern";
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new RegexPatternsPasswordPolicyProvider(session.getContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Regular Expression";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.STRING_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return "";
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return true;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java
new file mode 100644
index 0000000..fa85137
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class SpecialCharsPasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final String ERROR_MESSAGE = "invalidPasswordMinSpecialCharsMessage";
+
+ private KeycloakContext context;
+
+ public SpecialCharsPasswordPolicyProvider(KeycloakContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ int min = context.getRealm().getPasswordPolicy().getPolicyConfig(SpecialCharsPasswordPolicyProviderFactory.ID);
+ int count = 0;
+ for (char c : password.toCharArray()) {
+ if (!Character.isLetterOrDigit(c)) {
+ count++;
+ }
+ }
+ return count < min ? new PolicyError(ERROR_MESSAGE, min) : null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return validate(user.getUsername(), password);
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : 1;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..908cbee
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class SpecialCharsPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ public static final String ID = "specialChars";
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new SpecialCharsPasswordPolicyProvider(session.getContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Special Characters";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.INT_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return "1";
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java
new file mode 100644
index 0000000..16ac1ef
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UpperCasePasswordPolicyProvider implements PasswordPolicyProvider {
+
+ private static final String ERROR_MESSAGE = "invalidPasswordMinUpperCaseCharsMessage";
+
+ private KeycloakContext context;
+
+ public UpperCasePasswordPolicyProvider(KeycloakContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public PolicyError validate(String username, String password) {
+ int min = context.getRealm().getPasswordPolicy().getPolicyConfig(UpperCasePasswordPolicyProviderFactory.ID);
+ int count = 0;
+ for (char c : password.toCharArray()) {
+ if (Character.isUpperCase(c)) {
+ count++;
+ }
+ }
+ return count < min ? new PolicyError(ERROR_MESSAGE, min) : null;
+ }
+
+ @Override
+ public PolicyError validate(RealmModel realm, UserModel user, String password) {
+ return validate(user.getUsername(), password);
+ }
+
+ @Override
+ public Object parseConfig(String value) {
+ return value != null ? Integer.parseInt(value) : 1;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java
new file mode 100644
index 0000000..8dce247
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.policy;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UpperCasePasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
+
+ public static final String ID = "upperCase";
+
+ @Override
+ public PasswordPolicyProvider create(KeycloakSession session) {
+ return new UpperCasePasswordPolicyProvider(session.getContext());
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Uppercase Characters";
+ }
+
+ @Override
+ public String getConfigType() {
+ return PasswordPolicyProvider.INT_CONFIG_TYPE;
+ }
+
+ @Override
+ public String getDefaultConfigValue() {
+ return "1";
+ }
+
+ @Override
+ public boolean isMultiplSupported() {
+ return false;
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java
new file mode 100755
index 0000000..4d39f8a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+import org.keycloak.Config;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.ProviderEvent;
+import org.keycloak.provider.ProviderEventListener;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class AbstractLoginProtocolFactory implements LoginProtocolFactory {
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ factory.register(new ProviderEventListener() {
+ @Override
+ public void onEvent(ProviderEvent event) {
+ if (event instanceof RealmModel.ClientCreationEvent) {
+ ClientModel client = ((RealmModel.ClientCreationEvent)event).getCreatedClient();
+ addDefaults(client);
+ }
+ }
+ });
+ }
+
+ protected abstract void addDefaults(ClientModel realm);
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java b/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java
new file mode 100755
index 0000000..0c55c4f
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * Provides a template/sample client config adapter file. For example keycloak.json for our OIDC adapter. keycloak-saml.xml for our SAML client adapter
+ *
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ClientInstallationProvider extends Provider, ProviderFactory<ClientInstallationProvider> {
+ Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri);
+ String getProtocol();
+ String getDisplayType();
+ String getHelpText();
+ String getFilename();
+ String getMediaType();
+ boolean isDownloadOnly();
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java
new file mode 100755
index 0000000..abd86ff
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+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 ClientInstallationSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "client-installation";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return ClientInstallationProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return ClientInstallationProvider.class;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java
new file mode 100755
index 0000000..086a8ed
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.services.managers.ClientSessionCode;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface LoginProtocol extends Provider {
+
+ public static enum Error {
+
+ /**
+ * Login cancelled by the user
+ */
+ CANCELLED_BY_USER,
+ /**
+ * Consent denied by the user
+ */
+ CONSENT_DENIED,
+ /**
+ * Passive authentication mode requested but nobody is logged in
+ */
+ PASSIVE_LOGIN_REQUIRED,
+ /**
+ * Passive authentication mode requested, user is logged in, but some other user interaction is necessary (eg. some required login actions exist or Consent approval is necessary for logged in
+ * user)
+ */
+ PASSIVE_INTERACTION_REQUIRED;
+ }
+
+ LoginProtocol setSession(KeycloakSession session);
+
+ LoginProtocol setRealm(RealmModel realm);
+
+ LoginProtocol setUriInfo(UriInfo uriInfo);
+
+ LoginProtocol setHttpHeaders(HttpHeaders headers);
+
+ LoginProtocol setEventBuilder(EventBuilder event);
+
+ Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode);
+
+ Response sendError(ClientSessionModel clientSession, Error error);
+
+ void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
+ Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
+ Response finishLogout(UserSessionModel userSession);
+
+ /**
+ * @param userSession
+ * @param clientSession
+ * @return true if SSO cookie authentication can't be used. User will need to "actively" reauthenticate
+ */
+ boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession);
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
new file mode 100755
index 0000000..931a00d
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface LoginProtocolFactory extends ProviderFactory<LoginProtocol> {
+ /**
+ * List of built in protocol mappers that can be used to apply to clients.
+ *
+ * @return
+ */
+ List<ProtocolMapperModel> getBuiltinMappers();
+
+ /**
+ * List of mappers, which are added to new clients by default
+ * @return
+ */
+ List<ProtocolMapperModel> getDefaultBuiltinMappers();
+
+ Object createProtocolEndpoint(RealmModel realm, EventBuilder event);
+
+ /**
+ * Setup default values for new clients. This expects that the representation has already set up the client
+ *
+ * @param rep
+ * @param newClient
+ */
+ void setupClientDefaults(ClientRepresentation rep, ClientModel newClient);
+
+ /**
+ * Setup default values for new templates. This expects that the representation has already set up the template
+ *
+ * @param clientRep
+ * @param newClient
+ */
+ void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java
new file mode 100755
index 0000000..84133a7
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+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 LoginProtocolSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "login-protocol";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return LoginProtocol.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return LoginProtocolFactory.class;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java
new file mode 100644
index 0000000..e30f5da
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.provider.Provider;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * Provides introspection for a determined OAuth2 token type.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface TokenIntrospectionProvider extends Provider {
+
+ /**
+ * Introspect the <code>token</code>.
+ *
+ * @param token the token to introspect.
+ * @return the response with the information about the token
+ */
+ Response introspect(String token);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java
new file mode 100644
index 0000000..48b7556
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * A factory that creates {@link TokenIntrospectionProvider} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface TokenIntrospectionProviderFactory extends ProviderFactory<TokenIntrospectionProvider> {
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java
new file mode 100644
index 0000000..4eb6d39
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * <p>A {@link Spi} to support additional tokens types to the OAuth2 Token Introspection Endpoint.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TokenIntrospectionSpi implements Spi {
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "oauth2-token-introspection";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return TokenIntrospectionProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return TokenIntrospectionProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapper.java b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapper.java
new file mode 100755
index 0000000..8055fae
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperContainerModel;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.ConfiguredProvider;
+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 ProtocolMapper extends Provider, ProviderFactory<ProtocolMapper>,ConfiguredProvider {
+ String getProtocol();
+ String getDisplayCategory();
+ String getDisplayType();
+
+ /**
+ * Called when instance of mapperModel is created/updated for this protocolMapper through admin endpoint
+ *
+ * @param session
+ * @param realm
+ * @param client client or clientTemplate
+ * @param mapperModel
+ * @throws ProtocolMapperConfigException if configuration provided in mapperModel is not valid
+ */
+ default void validateConfig(KeycloakSession session, RealmModel realm, ProtocolMapperContainerModel client, ProtocolMapperModel mapperModel) throws ProtocolMapperConfigException {
+ };
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java
new file mode 100644
index 0000000..3f1f676
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ProtocolMapperConfigException extends Exception {
+
+ private String messageKey;
+ private Object[] parameters;
+
+ public ProtocolMapperConfigException(String message) {
+ super(message);
+ }
+
+ public ProtocolMapperConfigException(String message, String messageKey) {
+ super(message);
+ this.messageKey = messageKey;
+ }
+
+ public ProtocolMapperConfigException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ProtocolMapperConfigException(String message, String messageKey, Throwable cause) {
+ super(message, cause);
+ this.messageKey = messageKey;
+ }
+
+ public ProtocolMapperConfigException(String message, Object ... parameters) {
+ super(message);
+ this.parameters = parameters;
+ }
+
+ public ProtocolMapperConfigException(String messageKey, String message, Object ... parameters) {
+ super(message);
+ this.messageKey = messageKey;
+ this.parameters = parameters;
+ }
+
+ public String getMessageKey() {
+ return messageKey;
+ }
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Object[] parameters) {
+ this.parameters = parameters;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperSpi.java
new file mode 100755
index 0000000..5aa841c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperSpi.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol;
+
+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 ProtocolMapperSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "protocol-mapper";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return ProtocolMapper.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return ProtocolMapper.class;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java b/server-spi-private/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java
new file mode 100644
index 0000000..047c1bd
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.provider;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.component.ComponentValidationException;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ConfigurationValidationHelper {
+
+ private ComponentModel model;
+
+ private ConfigurationValidationHelper(ComponentModel model) {
+ this.model = model;
+ }
+
+ public static ConfigurationValidationHelper check(ComponentModel model) {
+ return new ConfigurationValidationHelper(model);
+ }
+
+ public ConfigurationValidationHelper checkInt(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
+ return checkInt(property.getName(), property.getLabel(), required);
+ }
+
+ public ConfigurationValidationHelper checkInt(String key, String label, boolean required) throws ComponentValidationException {
+ checkSingle(key, label, required);
+
+ String val = model.getConfig().getFirst(key);
+ if (val != null) {
+ try {
+ Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ throw new ComponentValidationException("''{0}'' should be a number", label);
+ }
+ }
+
+ return this;
+ }
+
+ public ConfigurationValidationHelper checkLong(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
+ return checkLong(property.getName(), property.getLabel(), required);
+ }
+
+ public ConfigurationValidationHelper checkLong(String key, String label, boolean required) throws ComponentValidationException {
+ checkSingle(key, label, required);
+
+ String val = model.getConfig().getFirst(key);
+ if (val != null) {
+ try {
+ Long.parseLong(val);
+ } catch (NumberFormatException e) {
+ throw new ComponentValidationException("''{0}'' should be a number", label);
+ }
+ }
+
+ return this;
+ }
+
+ public ConfigurationValidationHelper checkSingle(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
+ return checkSingle(property.getName(), property.getLabel(), required);
+ }
+
+ public ConfigurationValidationHelper checkSingle(String key, String label, boolean required) throws ComponentValidationException {
+ if (model.getConfig().containsKey(key) && model.getConfig().get(key).size() > 1) {
+ throw new ComponentValidationException("''{0}'' should be a single entry", label);
+ }
+
+ if (required) {
+ checkRequired(key, label);
+ }
+
+ return this;
+ }
+
+ public ConfigurationValidationHelper checkRequired(ProviderConfigProperty property) throws ComponentValidationException {
+ return checkRequired(property.getName(), property.getLabel());
+ }
+
+ public ConfigurationValidationHelper checkRequired(String key, String label) throws ComponentValidationException {
+ List<String> values = model.getConfig().get(key);
+ if (values == null) {
+ throw new ComponentValidationException("''{0}'' is required", label);
+ }
+
+ return this;
+ }
+
+ public ConfigurationValidationHelper checkBoolean(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
+ return checkBoolean(property.getName(), property.getLabel(), required);
+ }
+
+ public ConfigurationValidationHelper checkBoolean(String key, String label, boolean required) {
+ checkSingle(key, label, required);
+
+ String val = model.getConfig().getFirst(key);
+ if (val != null && !(val.equals("true") || val.equals("false"))) {
+ throw new ComponentValidationException("''{0}'' should be ''true'' or ''false''", label);
+ }
+
+ return this;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java
new file mode 100644
index 0000000..b4e993a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.provider;
+
+/**
+ * Providers that are only supported in some environments can implement this interface to be able to determine if they
+ * should be available or not.
+ *
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface EnvironmentDependentProviderFactory {
+
+ /**
+ * @return <code>true</code> if the provider is supported and should be available, <code>false</code> otherwise
+ */
+ boolean isSupported();
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoader.java b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoader.java
new file mode 100644
index 0000000..2d7a07a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoader.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.provider;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ProviderLoader {
+
+ /**
+ * Load the SPI definitions themselves.
+ *
+ * @return a list of Spi definition objects
+ */
+ List<Spi> loadSpis();
+
+ /**
+ * Load all provider factories of a specific SPI.
+ *
+ * @param spi the Spi definition
+ * @return a list of provider factories
+ */
+ List<ProviderFactory> load(Spi spi);
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java
new file mode 100644
index 0000000..85a5e17
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.provider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ProviderLoaderFactory {
+
+ boolean supports(String type);
+
+ ProviderLoader create(ClassLoader baseClassLoader, String resource);
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java
new file mode 100644
index 0000000..a4e6718
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.provider;
+
+import java.util.Map;
+
+/**
+ * Marker interface for {@link ProviderFactory} of Provider which wants to show some info on "Server Info" page in Admin console.
+ *
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public interface ServerInfoAwareProviderFactory {
+
+ /**
+ * Return actual info about the provider. This info contains informations about providers configuration and operational conditions (eg. errors in connection to remote systems etc) which is
+ * shown on "Server Info" page then.
+ *
+ * @return Map with keys describing value and relevant values itself
+ */
+ public Map<String, String> getOperationalInfo();
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/scripting/InvocableScriptAdapter.java b/server-spi-private/src/main/java/org/keycloak/scripting/InvocableScriptAdapter.java
new file mode 100644
index 0000000..c3859ab
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/scripting/InvocableScriptAdapter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.scripting;
+
+import org.keycloak.models.ScriptModel;
+
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+/**
+ * Wraps a {@link ScriptModel} and makes it {@link Invocable}.
+ *
+ * @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
+ */
+public class InvocableScriptAdapter implements Invocable {
+
+ /**
+ * Holds the {@ScriptModel}
+ */
+ private final ScriptModel scriptModel;
+
+ /**
+ * Holds the {@link ScriptEngine} instance initialized with the script code.
+ */
+ private final ScriptEngine scriptEngine;
+
+ /**
+ * Creates a new {@link InvocableScriptAdapter} instance.
+ *
+ * @param scriptModel must not be {@literal null}
+ * @param scriptEngine must not be {@literal null}
+ */
+ public InvocableScriptAdapter(ScriptModel scriptModel, ScriptEngine scriptEngine) {
+
+ if (scriptModel == null) {
+ throw new IllegalArgumentException("scriptModel must not be null");
+ }
+
+ if (scriptEngine == null) {
+ throw new IllegalArgumentException("scriptEngine must not be null");
+ }
+
+ this.scriptModel = scriptModel;
+ this.scriptEngine = loadScriptIntoEngine(scriptModel, scriptEngine);
+ }
+
+ @Override
+ public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptExecutionException {
+
+ try {
+ return getInvocableEngine().invokeMethod(thiz, name, args);
+ } catch (ScriptException | NoSuchMethodException e) {
+ throw new ScriptExecutionException(scriptModel, e);
+ }
+ }
+
+ @Override
+ public Object invokeFunction(String name, Object... args) throws ScriptExecutionException {
+ try {
+ return getInvocableEngine().invokeFunction(name, args);
+ } catch (ScriptException | NoSuchMethodException e) {
+ throw new ScriptExecutionException(scriptModel, e);
+ }
+ }
+
+ @Override
+ public <T> T getInterface(Class<T> clazz) {
+ return getInvocableEngine().getInterface(clazz);
+ }
+
+ @Override
+ public <T> T getInterface(Object thiz, Class<T> clazz) {
+ return getInvocableEngine().getInterface(thiz, clazz);
+ }
+
+ /**
+ * Returns {@literal true} if the {@link ScriptEngine} has a definition with the given {@code name}.
+ *
+ * @param name
+ * @return
+ */
+ public boolean isDefined(String name) {
+
+ Object candidate = scriptEngine.getContext().getAttribute(name);
+
+ return candidate != null;
+ }
+
+ private ScriptEngine loadScriptIntoEngine(ScriptModel script, ScriptEngine engine) {
+
+ try {
+ engine.eval(script.getCode());
+ } catch (ScriptException se) {
+ throw new ScriptExecutionException(script, se);
+ }
+
+ return engine;
+ }
+
+ private Invocable getInvocableEngine() {
+ return (Invocable) scriptEngine;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/scripting/Script.java b/server-spi-private/src/main/java/org/keycloak/scripting/Script.java
new file mode 100644
index 0000000..ef86902
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/scripting/Script.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.scripting;
+
+import org.keycloak.models.ScriptModel;
+
+/**
+ * A {@link ScriptModel} which holds some meta-data.
+ *
+ * @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
+ */
+public class Script implements ScriptModel {
+
+ private String id;
+
+ private String realmId;
+
+ private String name;
+
+ private String mimeType;
+
+ private String code;
+
+ private String description;
+
+ public Script(String id, String realmId, String name, String mimeType, String code, String description) {
+
+ this.id = id;
+ this.realmId = realmId;
+ this.name = name;
+ this.mimeType = mimeType;
+ this.code = code;
+ this.description = description;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getRealmId() {
+ return realmId;
+ }
+
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public void setMimeType(String mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ @Override
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return "Script{" +
+ "id='" + id + '\'' +
+ ", realmId='" + realmId + '\'' +
+ ", name='" + name + '\'' +
+ ", type='" + mimeType + '\'' +
+ ", code='" + code + '\'' +
+ ", description='" + description + '\'' +
+ '}';
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/scripting/ScriptBindingsConfigurer.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptBindingsConfigurer.java
new file mode 100644
index 0000000..9613eb6
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptBindingsConfigurer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.scripting;
+
+import javax.script.Bindings;
+
+/**
+ * Callback interface for customization of {@link Bindings} for a {@link javax.script.ScriptEngine}.
+ * <p>Used by {@link ScriptingProvider}</p>
+ * @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
+ */
+@FunctionalInterface
+public interface ScriptBindingsConfigurer {
+
+ /**
+ * A default {@link ScriptBindingsConfigurer} that provides no Bindings.
+ */
+ ScriptBindingsConfigurer EMPTY = new ScriptBindingsConfigurer() {
+
+ @Override
+ public void configureBindings(Bindings bindings) {
+ //NOOP
+ }
+ };
+
+ void configureBindings(Bindings bindings);
+}
\ No newline at end of file
diff --git a/server-spi-private/src/main/java/org/keycloak/scripting/ScriptExecutionException.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptExecutionException.java
new file mode 100644
index 0000000..2063bd2
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptExecutionException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.scripting;
+
+import org.keycloak.models.ScriptModel;
+
+import javax.script.ScriptException;
+
+/**
+ * Augments a {@link ScriptException} and adds additional metadata.
+ *
+ * @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
+ */
+public class ScriptExecutionException extends RuntimeException {
+
+ public ScriptExecutionException(ScriptModel script, Exception ex) {
+ super("Could not execute script '" + script.getName() + "' problem was: " + ex.getMessage(), ex);
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/scripting/ScriptingProvider.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptingProvider.java
new file mode 100644
index 0000000..67bad5a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptingProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.scripting;
+
+import org.keycloak.models.ScriptModel;
+import org.keycloak.provider.Provider;
+
+import javax.script.ScriptEngine;
+
+/**
+ * A {@link Provider} than provides Scripting capabilities.
+ *
+ * @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
+ */
+public interface ScriptingProvider extends Provider {
+
+ /**
+ * Returns an {@link InvocableScriptAdapter} based on the given {@link ScriptModel}.
+ * <p>The {@code InvocableScriptAdapter} wraps a dedicated {@link ScriptEngine} that was populated with the provided {@link ScriptBindingsConfigurer}</p>
+ *
+ * @param scriptModel the scriptModel to wrap
+ * @param bindingsConfigurer populates the {@link javax.script.Bindings}
+ * @return
+ */
+ InvocableScriptAdapter prepareInvocableScript(ScriptModel scriptModel, ScriptBindingsConfigurer bindingsConfigurer);
+
+ /**
+ * Creates a new {@link ScriptModel} instance.
+ *
+ * @param realmId
+ * @param scriptName
+ * @param scriptCode
+ * @param scriptDescription
+ * @return
+ */
+ ScriptModel createScript(String realmId, String mimeType, String scriptName, String scriptCode, String scriptDescription);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtector.java b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
new file mode 100755
index 0000000..e884b02
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.managers;
+
+import org.keycloak.common.ClientConnection;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface BruteForceProtector extends Provider {
+ void failedLogin(RealmModel realm, UserModel user, ClientConnection clientConnection);
+
+ boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, UserModel user);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java
new file mode 100755
index 0000000..b76981f
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.managers;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface BruteForceProtectorFactory extends ProviderFactory<BruteForceProtector> {
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java
new file mode 100755
index 0000000..0d3e24c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.managers;
+
+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 BruteForceProtectorSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "bruteForceProtector";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return BruteForceProtector.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return BruteForceProtectorFactory.class;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java b/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
new file mode 100755
index 0000000..2710174
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.managers;
+
+import org.jboss.logging.Logger;
+import org.keycloak.common.util.Base64Url;
+import org.keycloak.common.util.Time;
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.KeyManager;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.security.PublicKey;
+import java.security.Signature;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClientSessionCode {
+
+ private static final Logger logger = Logger.getLogger(ClientSessionCode.class);
+
+ private static final String NEXT_CODE = ClientSessionCode.class.getName() + ".nextCode";
+
+ private KeycloakSession session;
+ private final RealmModel realm;
+ private final ClientSessionModel clientSession;
+
+ public enum ActionType {
+ CLIENT,
+ LOGIN,
+ USER
+ }
+
+ public ClientSessionCode(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
+ this.session = session;
+ this.realm = realm;
+ this.clientSession = clientSession;
+ }
+
+ public static class ParseResult {
+ ClientSessionCode code;
+ boolean clientSessionNotFound;
+ boolean illegalHash;
+ ClientSessionModel clientSession;
+
+ public ClientSessionCode getCode() {
+ return code;
+ }
+
+ public boolean isClientSessionNotFound() {
+ return clientSessionNotFound;
+ }
+
+ public boolean isIllegalHash() {
+ return illegalHash;
+ }
+
+ public ClientSessionModel getClientSession() {
+ return clientSession;
+ }
+ }
+
+ public static ParseResult parseResult(String code, KeycloakSession session, RealmModel realm) {
+ ParseResult result = new ParseResult();
+ if (code == null) {
+ result.illegalHash = true;
+ return result;
+ }
+ try {
+ result.clientSession = getClientSession(code, session, realm);
+ if (result.clientSession == null) {
+ result.clientSessionNotFound = true;
+ return result;
+ }
+
+ if (!verifyCode(code, session, realm, result.clientSession)) {
+ result.illegalHash = true;
+ return result;
+ }
+
+ result.code = new ClientSessionCode(session, realm, result.clientSession);
+ return result;
+ } catch (RuntimeException e) {
+ result.illegalHash = true;
+ return result;
+ }
+ }
+
+ public static ClientSessionCode parse(String code, KeycloakSession session, RealmModel realm) {
+ try {
+ ClientSessionModel clientSession = getClientSession(code, session, realm);
+ if (clientSession == null) {
+ return null;
+ }
+
+ if (!verifyCode(code, session, realm, clientSession)) {
+ return null;
+ }
+
+ return new ClientSessionCode(session, realm, clientSession);
+ } catch (RuntimeException e) {
+ return null;
+ }
+ }
+
+ public static ClientSessionModel getClientSession(String code, KeycloakSession session, RealmModel realm) {
+ String[] parts = code.split("\\.");
+ String id = parts[1];
+ return session.sessions().getClientSession(realm, id);
+ }
+
+ public ClientSessionModel getClientSession() {
+ return clientSession;
+ }
+
+ public boolean isValid(String requestedAction, ActionType actionType) {
+ if (!isValidAction(requestedAction)) return false;
+ return isActionActive(actionType);
+ }
+
+ public boolean isActionActive(ActionType actionType) {
+ int timestamp = clientSession.getTimestamp();
+
+ int lifespan;
+ switch (actionType) {
+ case CLIENT:
+ lifespan = realm.getAccessCodeLifespan();
+ break;
+ case LOGIN:
+ lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction();
+ break;
+ case USER:
+ lifespan = realm.getAccessCodeLifespanUserAction();
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ return timestamp + lifespan > Time.currentTime();
+ }
+
+ public boolean isValidAction(String requestedAction) {
+ String action = clientSession.getAction();
+ if (action == null) {
+ return false;
+ }
+ if (!action.equals(requestedAction)) {
+ return false;
+ }
+ return true;
+ }
+
+
+ public Set<RoleModel> getRequestedRoles() {
+ Set<RoleModel> requestedRoles = new HashSet<>();
+ for (String roleId : clientSession.getRoles()) {
+ RoleModel role = realm.getRoleById(roleId);
+ if (role != null) {
+ requestedRoles.add(role);
+ }
+ }
+ return requestedRoles;
+ }
+
+ public Set<ProtocolMapperModel> getRequestedProtocolMappers() {
+ Set<ProtocolMapperModel> requestedProtocolMappers = new HashSet<>();
+ Set<String> protocolMappers = clientSession.getProtocolMappers();
+ ClientModel client = clientSession.getClient();
+ ClientTemplateModel template = client.getClientTemplate();
+ if (protocolMappers != null) {
+ for (String protocolMapperId : protocolMappers) {
+ ProtocolMapperModel protocolMapper = client.getProtocolMapperById(protocolMapperId);
+ if (protocolMapper == null && template != null) {
+ protocolMapper = template.getProtocolMapperById(protocolMapperId);
+ }
+ if (protocolMapper != null) {
+ requestedProtocolMappers.add(protocolMapper);
+ }
+ }
+ }
+ return requestedProtocolMappers;
+ }
+
+ public void setAction(String action) {
+ clientSession.setAction(action);
+ clientSession.setTimestamp(Time.currentTime());
+ }
+
+ public String getCode() {
+ String nextCode = (String) session.getAttribute(NEXT_CODE + "." + clientSession.getId());
+ if (nextCode == null) {
+ nextCode = generateCode(session, realm, clientSession);
+ session.setAttribute(NEXT_CODE + "." + clientSession.getId(), nextCode);
+ } else {
+ logger.debug("Code already generated for session, using code from session attributes");
+ }
+ return nextCode;
+ }
+
+ private static String generateCode(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
+ try {
+ KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
+
+ String secret = KeycloakModelUtils.generateSecret();
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(secret);
+ sb.append('.');
+ sb.append(clientSession.getId());
+
+ String code = sb.toString();
+
+ Signature signature = RSAProvider.getSignature(Algorithm.RS256);
+ signature.initSign(keys.getPrivateKey());
+ signature.update(code.getBytes("utf-8"));
+
+ sb = new StringBuilder();
+
+ sb.append(Base64Url.encode(signature.sign()));
+ sb.append('.');
+ sb.append(keys.getKid());
+
+ clientSession.setNote(ClientSessionModel.ACTION_SIGNATURE, sb.toString());
+
+ return code;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static boolean verifyCode(String code, KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
+ try {
+ String note = clientSession.getNote(ClientSessionModel.ACTION_SIGNATURE);
+ if (note == null) {
+ logger.debug("Action signature not found in client session");
+ return false;
+ }
+
+ clientSession.removeNote(ClientSessionModel.ACTION_SIGNATURE);
+
+ String[] signed = note.split("\\.");
+
+ PublicKey publicKey = session.keys().getPublicKey(realm, signed[1]);
+
+ Signature verifier = RSAProvider.getSignature(Algorithm.RS256);
+ verifier.initVerify(publicKey);
+ verifier.update(code.getBytes("utf-8"));
+ return verifier.verify(Base64Url.decode(signed[0]));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java
new file mode 100644
index 0000000..8f615c6
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java
@@ -0,0 +1,38 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.resource;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * <p>A {@link RealmResourceProvider} creates JAX-RS <emphasis>sub-resource</emphasis> instances for paths relative
+ * to Realm's RESTful API that could not be resolved by the server.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface RealmResourceProvider extends Provider {
+
+ /**
+ * <p>Returns a JAX-RS resource instance.
+ *
+ * @return a JAX-RS sub-resource instance
+ */
+ Object getResource();
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java
new file mode 100644
index 0000000..b39bc12
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java
@@ -0,0 +1,30 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.resource;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * <p>A factory that creates {@link RealmResourceProvider} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface RealmResourceProviderFactory extends ProviderFactory<RealmResourceProvider> {
+
+}
\ No newline at end of file
diff --git a/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceSPI.java b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceSPI.java
new file mode 100644
index 0000000..04e8f6d
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceSPI.java
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.resource;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * <p>A {@link Spi} to plug additional sub-resources to Realms' RESTful API.
+ *
+ * <p>Implementors can use this {@link Spi} to provide additional services to the mentioned API and extend Keycloak capabilities by
+ * creating JAX-RS sub-resources for paths not known by the server.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RealmResourceSPI implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "realm-restapi-extension";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return RealmResourceProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return RealmResourceProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/theme/Theme.java b/server-spi-private/src/main/java/org/keycloak/theme/Theme.java
new file mode 100755
index 0000000..283f4d7
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/theme/Theme.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.theme;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Locale;
+import java.util.Properties;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface Theme {
+
+ public enum Type { LOGIN, ACCOUNT, ADMIN, EMAIL, WELCOME, COMMON };
+
+ public String getName();
+
+ public String getParentName();
+
+ public String getImportName();
+
+ public Type getType();
+
+ public URL getTemplate(String name) throws IOException;
+
+ public InputStream getTemplateAsStream(String name) throws IOException;
+
+ public URL getResource(String path) throws IOException;
+
+ public InputStream getResourceAsStream(String path) throws IOException;
+
+ /**
+ * Same as getMessages(baseBundlename, locale), but uses a default baseBundlename
+ * such as "messages".
+ *
+ * @param locale The locale of the desired message bundle.
+ * @return The localized messages from the bundle.
+ * @throws IOException If bundle can not be read.
+ */
+ public Properties getMessages(Locale locale) throws IOException;
+
+ /**
+ * Retrieve localized messages from a message bundle.
+ *
+ * @param baseBundlename The base name of the bundle, such as "messages" in
+ * messages_en.properties.
+ * @param locale The locale of the desired message bundle.
+ * @return The localized messages from the bundle.
+ * @throws IOException If bundle can not be read.
+ */
+ public Properties getMessages(String baseBundlename, Locale locale) throws IOException;
+
+ public Properties getProperties() throws IOException;
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java
new file mode 100755
index 0000000..34de97b
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.theme;
+
+import org.keycloak.provider.Provider;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ThemeProvider extends Provider {
+
+ public int getProviderPriority();
+
+ public Theme getTheme(String name, Theme.Type type) throws IOException;
+
+ public Set<String> nameSet(Theme.Type type);
+
+ public boolean hasTheme(String name, Theme.Type type);
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/theme/ThemeProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProviderFactory.java
new file mode 100755
index 0000000..d28fea5
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProviderFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.theme;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ThemeProviderFactory extends ProviderFactory<ThemeProvider> {
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/theme/ThemeSpi.java b/server-spi-private/src/main/java/org/keycloak/theme/ThemeSpi.java
new file mode 100755
index 0000000..b0f263c
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/theme/ThemeSpi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.theme;
+
+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 ThemeSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "theme";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return ThemeProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return ThemeProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/timer/ScheduledTask.java b/server-spi-private/src/main/java/org/keycloak/timer/ScheduledTask.java
new file mode 100644
index 0000000..ba092ac
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/timer/ScheduledTask.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.timer;
+
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ScheduledTask {
+
+ public void run(KeycloakSession session);
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/timer/TimerProvider.java b/server-spi-private/src/main/java/org/keycloak/timer/TimerProvider.java
new file mode 100644
index 0000000..5dbf69b
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/timer/TimerProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.timer;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface TimerProvider extends Provider {
+
+ public void schedule(Runnable runnable, long intervalMillis, String taskName);
+
+ public void scheduleTask(ScheduledTask scheduledTask, long intervalMillis, String taskName);
+
+ public void cancelTask(String taskName);
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/timer/TimerProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/timer/TimerProviderFactory.java
new file mode 100644
index 0000000..a676242
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/timer/TimerProviderFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.timer;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface TimerProviderFactory extends ProviderFactory<TimerProvider> {
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/timer/TimerSpi.java b/server-spi-private/src/main/java/org/keycloak/timer/TimerSpi.java
new file mode 100644
index 0000000..b300a6b
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/timer/TimerSpi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.timer;
+
+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 TimerSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "timer";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return TimerProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return TimerProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java b/server-spi-private/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java
new file mode 100644
index 0000000..ebcf52e
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.transaction;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+import javax.transaction.TransactionManager;
+
+/**
+ * JTA TransactionManager lookup
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface JtaTransactionManagerLookup extends Provider, ProviderFactory<JtaTransactionManagerLookup> {
+ @Override
+ default void close() {
+
+ }
+
+ @Override
+ default JtaTransactionManagerLookup create(KeycloakSession session) {
+ return this;
+ }
+
+ TransactionManager getTransactionManager();
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java b/server-spi-private/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java
new file mode 100755
index 0000000..f45d897
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.transaction;
+
+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 TransactionManagerLookupSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "jta-lookup";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return JtaTransactionManagerLookup.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return JtaTransactionManagerLookup.class;
+ }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/truststore/HostnameVerificationPolicy.java b/server-spi-private/src/main/java/org/keycloak/truststore/HostnameVerificationPolicy.java
new file mode 100755
index 0000000..4c4f069
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/truststore/HostnameVerificationPolicy.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.truststore;
+
+public enum HostnameVerificationPolicy {
+
+ /**
+ * Hostname verification is not done on the server's certificate
+ */
+ ANY,
+
+ /**
+ * Allows wildcards in subdomain names i.e. *.foo.com
+ */
+ WILDCARD,
+
+ /**
+ * CN must match hostname connecting to
+ */
+ STRICT
+}
\ No newline at end of file
diff --git a/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProvider.java b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProvider.java
new file mode 100755
index 0000000..00b868a
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.truststore;
+
+import org.keycloak.provider.Provider;
+
+import java.security.KeyStore;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public interface TruststoreProvider extends Provider {
+
+ HostnameVerificationPolicy getPolicy();
+
+ KeyStore getTruststore();
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProviderFactory.java
new file mode 100755
index 0000000..83c79b2
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProviderFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.truststore;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public interface TruststoreProviderFactory extends ProviderFactory<TruststoreProvider> {
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreSpi.java b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreSpi.java
new file mode 100755
index 0000000..160ccba
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreSpi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.truststore;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class TruststoreSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "truststore";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return TruststoreProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return TruststoreProviderFactory.class;
+ }
+}
diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory
new file mode 100644
index 0000000..3cf430c
--- /dev/null
+++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.models.session.DisabledUserSessionPersisterProvider
\ No newline at end of file
diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory
new file mode 100644
index 0000000..128272d
--- /dev/null
+++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.policy.DefaultPasswordPolicyManagerProviderFactory
\ No newline at end of file
diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory
new file mode 100644
index 0000000..a436fe9
--- /dev/null
+++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory
@@ -0,0 +1,28 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.policy.DigitsPasswordPolicyProviderFactory
+org.keycloak.policy.ForceExpiredPasswordPolicyProviderFactory
+org.keycloak.policy.HashAlgorithmPasswordPolicyProviderFactory
+org.keycloak.policy.HashIterationsPasswordPolicyProviderFactory
+org.keycloak.policy.HistoryPasswordPolicyProviderFactory
+org.keycloak.policy.LengthPasswordPolicyProviderFactory
+org.keycloak.policy.LowerCasePasswordPolicyProviderFactory
+org.keycloak.policy.NotUsernamePasswordPolicyProviderFactory
+org.keycloak.policy.RegexPatternsPasswordPolicyProviderFactory
+org.keycloak.policy.SpecialCharsPasswordPolicyProviderFactory
+org.keycloak.policy.UpperCasePasswordPolicyProviderFactory
diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100755
index 0000000..bbd588e
--- /dev/null
+++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1,68 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.models.UserFederationSpi
+org.keycloak.storage.UserStorageProviderSpi
+org.keycloak.storage.federated.UserFederatedStorageProviderSpi
+org.keycloak.mappers.UserFederationMapperSpi
+org.keycloak.models.RealmSpi
+org.keycloak.models.UserSessionSpi
+org.keycloak.models.UserSpi
+org.keycloak.models.session.UserSessionPersisterSpi
+org.keycloak.models.dblock.DBLockSpi
+org.keycloak.migration.MigrationSpi
+org.keycloak.events.EventListenerSpi
+org.keycloak.events.EventStoreSpi
+org.keycloak.exportimport.ExportSpi
+org.keycloak.exportimport.ImportSpi
+org.keycloak.timer.TimerSpi
+org.keycloak.scripting.ScriptingSpi
+org.keycloak.services.managers.BruteForceProtectorSpi
+org.keycloak.services.resource.RealmResourceSPI
+org.keycloak.protocol.ClientInstallationSpi
+org.keycloak.protocol.LoginProtocolSpi
+org.keycloak.protocol.ProtocolMapperSpi
+org.keycloak.broker.provider.IdentityProviderSpi
+org.keycloak.broker.provider.IdentityProviderMapperSpi
+org.keycloak.broker.social.SocialProviderSpi
+org.keycloak.forms.account.AccountSpi
+org.keycloak.forms.login.LoginFormsSpi
+org.keycloak.email.EmailSenderSpi
+org.keycloak.email.EmailTemplateSpi
+org.keycloak.theme.ThemeSpi
+org.keycloak.truststore.TruststoreSpi
+org.keycloak.connections.httpclient.HttpClientSpi
+org.keycloak.models.cache.CacheRealmProviderSpi
+org.keycloak.models.cache.CacheUserProviderSpi
+org.keycloak.authentication.AuthenticatorSpi
+org.keycloak.authentication.ClientAuthenticatorSpi
+org.keycloak.authentication.RequiredActionSpi
+org.keycloak.authentication.FormAuthenticatorSpi
+org.keycloak.authentication.FormActionSpi
+org.keycloak.cluster.ClusterSpi
+org.keycloak.authorization.policy.provider.PolicySpi
+org.keycloak.authorization.store.StoreFactorySpi
+org.keycloak.authorization.AuthorizationSpi
+org.keycloak.models.cache.authorization.CachedStoreFactorySpi
+org.keycloak.protocol.oidc.TokenIntrospectionSpi
+org.keycloak.policy.PasswordPolicySpi
+org.keycloak.policy.PasswordPolicyManagerSpi
+org.keycloak.transaction.TransactionManagerLookupSpi
+org.keycloak.credential.hash.PasswordHashSpi
+org.keycloak.credential.CredentialSpi
+org.keycloak.keys.PublicKeyStorageSpi
+org.keycloak.keys.KeySpi
\ No newline at end of file
diff --git a/server-spi-private/src/test/java/org/keycloak/models/HmacTest.java b/server-spi-private/src/test/java/org/keycloak/models/HmacTest.java
new file mode 100755
index 0000000..45aaf16
--- /dev/null
+++ b/server-spi-private/src/test/java/org/keycloak/models/HmacTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.models.utils.Base32;
+import org.keycloak.models.utils.HmacOTP;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class HmacTest {
+
+ @Test
+ public void testHmac() throws Exception {
+ HmacOTP hmacOTP = new HmacOTP(6, HmacOTP.HMAC_SHA1, 10);
+ String secret = "JNSVMMTEKZCUGSKJIVGHMNSQOZBDA5JT";
+ String decoded = new String(Base32.decode(secret));
+ System.out.println(hmacOTP.generateHOTP(decoded, 0));
+ System.out.println(hmacOTP.validateHOTP("550233", decoded, 0));
+ Assert.assertEquals(1, hmacOTP.validateHOTP("550233", decoded, 0));
+ }
+}
services/pom.xml 5(+5 -0)
diff --git a/services/pom.xml b/services/pom.xml
index dc33a3d..0fb68f7 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -64,6 +64,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
</dependency>
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java
index fdaa481..ef17476 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java
@@ -20,6 +20,7 @@ package org.keycloak.authentication.authenticators.browser;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.RoleUtils;
import javax.ws.rs.core.MultivaluedMap;
import java.util.List;
@@ -30,7 +31,6 @@ import static org.keycloak.authentication.authenticators.browser.ConditionalOtpF
import static org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticator.OtpDecision.SHOW_OTP;
import static org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticator.OtpDecision.SKIP_OTP;
import static org.keycloak.models.utils.KeycloakModelUtils.getRoleFromString;
-import static org.keycloak.models.utils.KeycloakModelUtils.hasRole;
/**
* An {@link OTPFormAuthenticator} that can conditionally require OTP authentication.
@@ -264,6 +264,6 @@ public class ConditionalOtpFormAuthenticator extends OTPFormAuthenticator {
RoleModel role = getRoleFromString(context.getRealm(), roleName);
UserModel user = context.getUser();
- return hasRole(user.getRoleMappings(), role);
+ return RoleUtils.hasRole(user.getRoleMappings(), role);
}
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java
index 85a217f..9bff3f9 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java
@@ -142,7 +142,7 @@ public class ScriptBasedAuthenticator implements Authenticator {
RealmModel realm = context.getRealm();
- ScriptingProvider scripting = context.getSession().scripting();
+ ScriptingProvider scripting = context.getSession().getProvider(ScriptingProvider.class);
//TODO lookup script by scriptId instead of creating it every time
ScriptModel script = scripting.createScript(realm.getId(), ScriptModel.TEXT_JAVASCRIPT, scriptName, scriptCode, scriptDescription);
diff --git a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java
index d0558ab..97a92ea 100644
--- a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java
+++ b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java
@@ -140,7 +140,7 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, policy.getHashAlgorithm());
if (hash == null) {
logger.warnv("Realm PasswordPolicy PasswordHashProvider {0} not found", policy.getHashAlgorithm());
- return session.getProvider(PasswordHashProvider.class, HashAlgorithmPasswordPolicyProviderFactory.DEFAULT_VALUE);
+ return session.getProvider(PasswordHashProvider.class, PasswordPolicy.HASH_ALGORITHM_DEFAULT);
}
return hash;
}
diff --git a/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
index 1ea4092..2357a65 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
@@ -108,7 +108,8 @@ public class ImportUtils {
}
}
- RealmImporter realmManager = session.getContext().getRealmManager();
+ RealmManager realmManager = new RealmManager(session);
+ realmManager.setContextPath(session.getContext().getContextPath());
realmManager.importRealm(rep);
if (System.getProperty(ExportImportConfig.ACTION) != null) {
diff --git a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java
index c5f09f1..f6bbaeb 100644
--- a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java
+++ b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java
@@ -23,7 +23,6 @@ import org.keycloak.models.RealmModel;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
diff --git a/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java b/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java
index 0128e4e..cce0fec 100755
--- a/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java
@@ -24,7 +24,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.PartialImportRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import java.util.HashMap;
import java.util.List;
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
index 99f2559..07313d8 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
@@ -111,13 +111,6 @@ public class DefaultKeycloakContext implements KeycloakContext {
}
@Override
- public RealmImporter getRealmManager() {
- RealmManager manager = new RealmManager(session);
- manager.setContextPath(getContextPath());
- return manager;
- }
-
- @Override
public Locale resolveLocale(UserModel user) {
return LocaleHelper.getLocale(session, realm, user);
}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
index 7fbd7a3..4cc77a2 100644
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
@@ -59,7 +59,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
private UserProvider userModel;
private UserStorageManager userStorageManager;
private UserCredentialStoreManager userCredentialStorageManager;
- private ScriptingProvider scriptingProvider;
private UserSessionProvider sessionProvider;
private UserFederationManager federationManager;
private UserFederatedStorageProvider userFederatedStorageProvider;
@@ -275,14 +274,4 @@ public class DefaultKeycloakSession implements KeycloakSession {
}
}
}
-
- @Override
- public ScriptingProvider scripting() {
-
- if (scriptingProvider == null) {
- scriptingProvider = getProvider(ScriptingProvider.class);
- }
-
- return scriptingProvider;
- }
}
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
index b648bbd..eb6fba6 100644
--- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
@@ -27,6 +27,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserManager;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.session.UserSessionPersisterProvider;
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 f9ae190..7a8964f 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
@@ -66,7 +66,7 @@ import org.keycloak.services.Urls;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.ClientSessionCode;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.AccountService;
import org.keycloak.services.validation.Validation;
testsuite/integration/pom.xml 2(+1 -1)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index d1311b5..873d99d 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -120,7 +120,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-server-spi</artifactId>
+ <artifactId>keycloak-server-spi-private</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java
index cafe113..6eccb21 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java
@@ -79,7 +79,7 @@ public class FederatedStorageExportImportTest {
protected PasswordHashProvider getHashProvider(KeycloakSession session, PasswordPolicy policy) {
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, policy.getHashAlgorithm());
if (hash == null) {
- return session.getProvider(PasswordHashProvider.class, HashAlgorithmPasswordPolicyProviderFactory.DEFAULT_VALUE);
+ return session.getProvider(PasswordHashProvider.class, PasswordPolicy.HASH_ALGORITHM_DEFAULT);
}
return hash;
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
index 431bff6..2db24ad 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
@@ -34,7 +34,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UserSessionProviderFactory;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.testsuite.rule.KeycloakRule;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
index 074438f..80f663f 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
@@ -33,7 +33,7 @@ import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import java.util.ArrayList;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
index 013f16c..b9f4f2a 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
@@ -34,7 +34,7 @@ import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.LoggingRule;
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
index d75e467..f2fc3aa 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -31,7 +31,7 @@ import org.keycloak.models.UserLoginFailureModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.services.managers.UserManager;
+import org.keycloak.models.UserManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import java.util.Arrays;
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml
index 28e4789..6f20838 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml
@@ -28,6 +28,7 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
+ <module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.keycloak.keycloak-services"/>
<module name="org.keycloak.keycloak-model-infinispan"/>
<module name="org.keycloak.keycloak-model-jpa"/>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java
index 5c54213..642a872 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java
@@ -22,15 +22,10 @@ import org.jboss.arquillian.graphene.page.Page;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.MultivaluedHashMap;
-import org.keycloak.common.util.PemUtils;
-import org.keycloak.keys.Attributes;
-import org.keycloak.keys.GeneratedRsaKeyProviderFactory;
import org.keycloak.keys.JavaKeystoreKeyProviderFactory;
import org.keycloak.keys.KeyMetadata;
import org.keycloak.keys.KeyProvider;
-import org.keycloak.keys.RsaKeyProviderFactory;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.ErrorRepresentation;
import org.keycloak.representations.idm.KeysMetadataRepresentation;
@@ -45,9 +40,6 @@ import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
-import java.security.KeyPair;
-import java.security.cert.Certificate;
-import java.security.interfaces.RSAPublicKey;
import java.util.List;
import static org.junit.Assert.*;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java
index 2dc3506..5bfaef2 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java
@@ -20,17 +20,13 @@ package org.keycloak.testsuite.keys;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Rule;
import org.junit.Test;
-import org.keycloak.RSATokenVerifier;
-import org.keycloak.common.VerificationException;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.PemUtils;
import org.keycloak.keys.Attributes;
-import org.keycloak.keys.GeneratedRsaKeyProviderFactory;
import org.keycloak.keys.KeyMetadata;
import org.keycloak.keys.KeyProvider;
-import org.keycloak.keys.RsaKeyProvider;
import org.keycloak.keys.RsaKeyProviderFactory;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.ErrorRepresentation;
@@ -40,16 +36,11 @@ import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.pages.AppPage;
-import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.util.OAuthClient;
-import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.security.KeyPair;
-import java.security.PublicKey;
import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
import java.util.List;
import static org.junit.Assert.*;
wildfly/adduser/pom.xml 4(+4 -0)
diff --git a/wildfly/adduser/pom.xml b/wildfly/adduser/pom.xml
index b33eb5f..6a827f4 100755
--- a/wildfly/adduser/pom.xml
+++ b/wildfly/adduser/pom.xml
@@ -36,6 +36,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-common</artifactId>
</dependency>
<dependency>
wildfly/extensions/pom.xml 5(+5 -0)
diff --git a/wildfly/extensions/pom.xml b/wildfly/extensions/pom.xml
index 0cafd9c..9b677db 100755
--- a/wildfly/extensions/pom.xml
+++ b/wildfly/extensions/pom.xml
@@ -47,6 +47,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-spi-private</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
wildfly/server-subsystem/pom.xml 2(+1 -1)
diff --git a/wildfly/server-subsystem/pom.xml b/wildfly/server-subsystem/pom.xml
index 7710f9f..03ac1a7 100755
--- a/wildfly/server-subsystem/pom.xml
+++ b/wildfly/server-subsystem/pom.xml
@@ -108,7 +108,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-server-spi</artifactId>
+ <artifactId>keycloak-server-spi-private</artifactId>
<scope>provided</scope>
</dependency>