diff --git a/admin-ui-styles/src/main/resources/META-INF/web-fragment.xml b/admin-ui-styles/src/main/resources/META-INF/web-fragment.xml index af5af31f88..cf5933b122 100755 --- a/admin-ui-styles/src/main/resources/META-INF/web-fragment.xml +++ b/admin-ui-styles/src/main/resources/META-INF/web-fragment.xml @@ -1,5 +1,5 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd "> diff --git a/admin-ui/src/main/resources/META-INF/web-fragment.xml b/admin-ui/src/main/resources/META-INF/web-fragment.xml index af5af31f88..cf5933b122 100755 --- a/admin-ui/src/main/resources/META-INF/web-fragment.xml +++ b/admin-ui/src/main/resources/META-INF/web-fragment.xml @@ -1,5 +1,5 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd "> diff --git a/core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java b/core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java index 0fde58744a..c4b4f7b69f 100755 --- a/core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java +++ b/core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java @@ -37,7 +37,8 @@ public class JaxrsOAuthClient extends AbstractOAuthClient { .queryParam("state", state) .build(); NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure, true); - logger.info("NewCookie: " + cookie.toString()); + logger.debug("NewCookie: " + cookie.toString()); + logger.debug("Oauth Redirect to: " + url); return Response.status(302) .location(url) .cookie(cookie).build(); diff --git a/core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java b/core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java index 2b40251b91..601bbbefba 100755 --- a/core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java +++ b/core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java @@ -4,7 +4,10 @@ import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.annotate.JsonProperty; import org.jboss.resteasy.jwt.JsonWebToken; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * @author Bill Burke diff --git a/core/src/main/java/org/keycloak/representations/idm/admin/AdminAction.java b/core/src/main/java/org/keycloak/representations/idm/admin/AdminAction.java index 0a7f553acf..3b0a4e4702 100755 --- a/core/src/main/java/org/keycloak/representations/idm/admin/AdminAction.java +++ b/core/src/main/java/org/keycloak/representations/idm/admin/AdminAction.java @@ -1,7 +1,6 @@ package org.keycloak.representations.idm.admin; import org.codehaus.jackson.annotate.JsonIgnore; -import org.codehaus.jackson.annotate.JsonProperty; /** * Posted to managed client from admin server. diff --git a/core/src/test/java/org/keycloak/RSAVerifierTest.java b/core/src/test/java/org/keycloak/RSAVerifierTest.java index 8841954222..d169d37cb0 100755 --- a/core/src/test/java/org/keycloak/RSAVerifierTest.java +++ b/core/src/test/java/org/keycloak/RSAVerifierTest.java @@ -6,13 +6,10 @@ import org.bouncycastle.openssl.PEMWriter; import org.bouncycastle.x509.X509V1CertificateGenerator; import org.jboss.resteasy.jose.jws.JWSBuilder; import org.jboss.resteasy.jwt.JsonSerialization; -import org.keycloak.RSATokenVerifier; -import org.keycloak.ResourceMetadata; -import org.keycloak.VerificationException; -import org.keycloak.representations.SkeletonKeyToken; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.keycloak.representations.SkeletonKeyToken; import javax.security.auth.x500.X500Principal; import java.io.IOException; diff --git a/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java index cf5e459d54..c9d2e0df42 100755 --- a/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java +++ b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java @@ -5,9 +5,9 @@ import org.jboss.resteasy.jose.jws.JWSBuilder; import org.jboss.resteasy.jose.jws.JWSInput; import org.jboss.resteasy.jose.jws.crypto.RSAProvider; import org.jboss.resteasy.jwt.JsonSerialization; +import org.junit.Test; import org.keycloak.representations.SkeletonKeyScope; import org.keycloak.representations.SkeletonKeyToken; -import org.junit.Test; import java.security.KeyPair; import java.security.KeyPairGenerator; diff --git a/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp b/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp index 91657c93da..54df669476 100755 --- a/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp +++ b/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp @@ -1,4 +1,4 @@ -<%@ page import="javax.ws.rs.core.*" language="java" contentType="text/html; charset=ISO-8859-1" +<%@ page import="javax.ws.rs.core.UriBuilder" language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> diff --git a/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp b/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp index fe8d990ba6..bf1ca5ad91 100755 --- a/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp +++ b/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp @@ -1,4 +1,4 @@ -<%@ page import="javax.ws.rs.core.*" language="java" contentType="text/html; charset=ISO-8859-1" +<%@ page import="javax.ws.rs.core.UriBuilder" language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> diff --git a/examples/as7-eap-demo/server/pom.xml b/examples/as7-eap-demo/server/pom.xml index 3282368dd4..6d21685586 100755 --- a/examples/as7-eap-demo/server/pom.xml +++ b/examples/as7-eap-demo/server/pom.xml @@ -35,11 +35,6 @@ keycloak-model-api ${project.version} - - org.keycloak - keycloak-model-picketlink - ${project.version} - org.keycloak keycloak-social-core @@ -75,22 +70,6 @@ keycloak-admin-ui-styles ${project.version} - - org.picketlink - picketlink-idm-api - - - org.picketlink - picketlink-idm-impl - - - org.picketlink - picketlink-idm-simple-schema - - - org.picketlink - picketlink-config - org.jboss.resteasy resteasy-jaxrs diff --git a/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java b/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java index b9bbc82c17..0b7b49d934 100755 --- a/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java +++ b/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java @@ -1,11 +1,11 @@ package org.keycloak.example.demo; import org.jboss.resteasy.jwt.JsonSerialization; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.RealmManager; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; import org.keycloak.services.resources.KeycloakApplication; import javax.servlet.ServletContext; diff --git a/examples/as7-eap-demo/server/src/main/resources/META-INF/persistence.xml b/examples/as7-eap-demo/server/src/main/resources/META-INF/persistence.xml index cb5a1c34ec..b9497152f1 100755 --- a/examples/as7-eap-demo/server/src/main/resources/META-INF/persistence.xml +++ b/examples/as7-eap-demo/server/src/main/resources/META-INF/persistence.xml @@ -2,27 +2,25 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> - + java:jboss/datasources/ExampleDS - - org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity - org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity - org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity - org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity - org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity - org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity - org.keycloak.models.picketlink.mappings.RealmEntity - org.keycloak.models.picketlink.mappings.ApplicationEntity + + org.keycloak.models.jpa.entities.ApplicationEntity + org.keycloak.models.jpa.entities.ApplicationScopeMappingEntity + org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity + org.keycloak.models.jpa.entities.CredentialEntity + org.keycloak.models.jpa.entities.OAuthClientEntity + org.keycloak.models.jpa.entities.RealmEntity + org.keycloak.models.jpa.entities.RealmScopeMappingEntity + org.keycloak.models.jpa.entities.RealmUserRoleMappingEntity + org.keycloak.models.jpa.entities.RequiredCredentialEntity + org.keycloak.models.jpa.entities.RoleEntity + org.keycloak.models.jpa.entities.SocialLinkEntity + org.keycloak.models.jpa.entities.UserEntity + org.keycloak.models.jpa.entities.UserRoleMappingEntity true - + diff --git a/examples/as7-eap-demo/server/src/main/webapp/saas/oauthGrantForm.jsp b/examples/as7-eap-demo/server/src/main/webapp/saas/oauthGrantForm.jsp index 4bbd6df5f8..d62b00e48d 100755 --- a/examples/as7-eap-demo/server/src/main/webapp/saas/oauthGrantForm.jsp +++ b/examples/as7-eap-demo/server/src/main/webapp/saas/oauthGrantForm.jsp @@ -1,5 +1,6 @@ -<%@ page import="org.keycloak.models.*,org.keycloak.services.resources.*,javax.ws.rs.core.*,java.util.*" language="java" contentType="text/html; charset=ISO-8859-1" +<%@ page import="org.keycloak.models.RealmModel,org.keycloak.models.RoleModel,org.keycloak.models.UserModel,javax.ws.rs.core.MultivaluedMap" language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> +<%@ page import="java.util.List" %> <% RealmModel realm = (RealmModel)request.getAttribute(RealmModel.class.getName()); String username = (String)request.getAttribute("username"); diff --git a/examples/as7-eap-dev/customer-app/src/main/webapp/customers/view.jsp b/examples/as7-eap-dev/customer-app/src/main/webapp/customers/view.jsp index 91657c93da..cc5925d81a 100755 --- a/examples/as7-eap-dev/customer-app/src/main/webapp/customers/view.jsp +++ b/examples/as7-eap-dev/customer-app/src/main/webapp/customers/view.jsp @@ -1,4 +1,4 @@ -<%@ page import="javax.ws.rs.core.*" language="java" contentType="text/html; charset=ISO-8859-1" +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> diff --git a/examples/as7-eap-dev/server/pom.xml b/examples/as7-eap-dev/server/pom.xml index b568d692bc..4ccbd3ebbc 100755 --- a/examples/as7-eap-dev/server/pom.xml +++ b/examples/as7-eap-dev/server/pom.xml @@ -35,11 +35,6 @@ keycloak-model-api ${project.version} - - org.keycloak - keycloak-model-picketlink - ${project.version} - org.keycloak keycloak-social-core @@ -65,22 +60,6 @@ keycloak-forms ${project.version} - - org.picketlink - picketlink-idm-api - - - org.picketlink - picketlink-idm-impl - - - org.picketlink - picketlink-idm-simple-schema - - - org.picketlink - picketlink-config - org.jboss.resteasy resteasy-jaxrs diff --git a/examples/as7-eap-dev/server/src/main/java/org/keycloak/example/demo/DemoApplication.java b/examples/as7-eap-dev/server/src/main/java/org/keycloak/example/demo/DemoApplication.java index 2f512e2807..856f3558c7 100755 --- a/examples/as7-eap-dev/server/src/main/java/org/keycloak/example/demo/DemoApplication.java +++ b/examples/as7-eap-dev/server/src/main/java/org/keycloak/example/demo/DemoApplication.java @@ -1,11 +1,11 @@ package org.keycloak.example.demo; import org.jboss.resteasy.jwt.JsonSerialization; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.RealmManager; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; import org.keycloak.services.resources.KeycloakApplication; import javax.servlet.ServletContext; diff --git a/examples/as7-eap-dev/server/src/main/resources/META-INF/persistence.xml b/examples/as7-eap-dev/server/src/main/resources/META-INF/persistence.xml index cb5a1c34ec..23f09137d8 100755 --- a/examples/as7-eap-dev/server/src/main/resources/META-INF/persistence.xml +++ b/examples/as7-eap-dev/server/src/main/resources/META-INF/persistence.xml @@ -2,27 +2,25 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> - + java:jboss/datasources/ExampleDS - - org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity - org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity - org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity - org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity - org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity - org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity - org.keycloak.models.picketlink.mappings.RealmEntity - org.keycloak.models.picketlink.mappings.ApplicationEntity + + org.keycloak.models.jpa.entities.ApplicationEntity + org.keycloak.models.jpa.entities.ApplicationScopeMappingEntity + org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity + org.keycloak.models.jpa.entities.CredentialEntity + org.keycloak.models.jpa.entities.OAuthClientEntity + org.keycloak.models.jpa.entities.RealmEntity + org.keycloak.models.jpa.entities.RealmScopeMappingEntity + org.keycloak.models.jpa.entities.RealmUserRoleMappingEntity + org.keycloak.models.jpa.entities.RequiredCredentialEntity + org.keycloak.models.jpa.entities.RoleEntity + org.keycloak.models.jpa.entities.SocialLinkEntity + org.keycloak.models.jpa.entities.UserEntity + org.keycloak.models.jpa.entities.UserRoleMappingEntity true - + @@ -30,4 +28,5 @@ + diff --git a/examples/as7-eap-dev/server/src/main/webapp/saas/oauthGrantForm.jsp b/examples/as7-eap-dev/server/src/main/webapp/saas/oauthGrantForm.jsp index 4bbd6df5f8..d62b00e48d 100755 --- a/examples/as7-eap-dev/server/src/main/webapp/saas/oauthGrantForm.jsp +++ b/examples/as7-eap-dev/server/src/main/webapp/saas/oauthGrantForm.jsp @@ -1,5 +1,6 @@ -<%@ page import="org.keycloak.models.*,org.keycloak.services.resources.*,javax.ws.rs.core.*,java.util.*" language="java" contentType="text/html; charset=ISO-8859-1" +<%@ page import="org.keycloak.models.RealmModel,org.keycloak.models.RoleModel,org.keycloak.models.UserModel,javax.ws.rs.core.MultivaluedMap" language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> +<%@ page import="java.util.List" %> <% RealmModel realm = (RealmModel)request.getAttribute(RealmModel.class.getName()); String username = (String)request.getAttribute("username"); diff --git a/forms/pom.xml b/forms/pom.xml index 0af1892e05..933b9aef0f 100755 --- a/forms/pom.xml +++ b/forms/pom.xml @@ -33,11 +33,6 @@ keycloak-social-core ${project.version} - - org.picketlink - picketlink-common - provided - org.jboss.resteasy resteasy-jaxrs diff --git a/forms/src/main/java/org/keycloak/forms/LoginBean.java b/forms/src/main/java/org/keycloak/forms/LoginBean.java index 9174813539..02fc10bbe3 100755 --- a/forms/src/main/java/org/keycloak/forms/LoginBean.java +++ b/forms/src/main/java/org/keycloak/forms/LoginBean.java @@ -21,12 +21,11 @@ */ package org.keycloak.forms; -import java.util.LinkedList; -import java.util.List; +import org.keycloak.forms.model.RequiredCredential; import javax.ws.rs.core.MultivaluedMap; - -import org.keycloak.forms.model.RequiredCredential; +import java.util.LinkedList; +import java.util.List; /** * @author Stian Thorgersen diff --git a/forms/src/main/java/org/keycloak/forms/OAuthGrantBean.java b/forms/src/main/java/org/keycloak/forms/OAuthGrantBean.java old mode 100644 new mode 100755 index ce7bf04da8..560c37a121 --- a/forms/src/main/java/org/keycloak/forms/OAuthGrantBean.java +++ b/forms/src/main/java/org/keycloak/forms/OAuthGrantBean.java @@ -21,14 +21,13 @@ */ package org.keycloak.forms; -import java.util.ArrayList; -import java.util.List; - -import javax.ws.rs.core.MultivaluedMap; - import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import javax.ws.rs.core.MultivaluedMap; +import java.util.ArrayList; +import java.util.List; + /** * @author Viliam Rockai */ diff --git a/forms/src/main/java/org/keycloak/forms/RegisterBean.java b/forms/src/main/java/org/keycloak/forms/RegisterBean.java index cb8c83d308..94bb83cc66 100755 --- a/forms/src/main/java/org/keycloak/forms/RegisterBean.java +++ b/forms/src/main/java/org/keycloak/forms/RegisterBean.java @@ -21,11 +21,10 @@ */ package org.keycloak.forms; +import javax.ws.rs.core.MultivaluedMap; import java.util.HashMap; import java.util.Map; -import javax.ws.rs.core.MultivaluedMap; - /** * @author Stian Thorgersen */ diff --git a/forms/src/main/java/org/keycloak/forms/SocialBean.java b/forms/src/main/java/org/keycloak/forms/SocialBean.java old mode 100644 new mode 100755 index 19baaa78bc..0861b55404 --- a/forms/src/main/java/org/keycloak/forms/SocialBean.java +++ b/forms/src/main/java/org/keycloak/forms/SocialBean.java @@ -21,15 +21,14 @@ */ package org.keycloak.forms; -import java.net.URI; -import java.util.*; - -import javax.imageio.spi.ServiceRegistry; -import javax.ws.rs.core.UriBuilder; - import org.keycloak.forms.model.SocialProvider; import org.keycloak.services.resources.flows.Urls; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; +import java.util.LinkedList; +import java.util.List; + /** * @author Stian Thorgersen */ diff --git a/forms/src/main/java/org/keycloak/forms/TotpBean.java b/forms/src/main/java/org/keycloak/forms/TotpBean.java old mode 100644 new mode 100755 index a283c2e45e..5ce40bcdb1 --- a/forms/src/main/java/org/keycloak/forms/TotpBean.java +++ b/forms/src/main/java/org/keycloak/forms/TotpBean.java @@ -21,11 +21,12 @@ */ package org.keycloak.forms; +import org.keycloak.models.utils.Base32; + import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Random; -import org.picketlink.common.util.Base32; /** * @author Stian Thorgersen diff --git a/forms/src/main/java/org/keycloak/forms/UrlBean.java b/forms/src/main/java/org/keycloak/forms/UrlBean.java index 8d25c3d969..49635a0cee 100755 --- a/forms/src/main/java/org/keycloak/forms/UrlBean.java +++ b/forms/src/main/java/org/keycloak/forms/UrlBean.java @@ -21,10 +21,10 @@ */ package org.keycloak.forms; -import java.net.URI; - import org.keycloak.services.resources.flows.Urls; +import java.net.URI; + /** * @author Stian Thorgersen */ diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java old mode 100644 new mode 100755 index 6ec433ef5b..b7b914c131 --- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java +++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java @@ -21,19 +21,12 @@ */ package org.keycloak.service; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.HashMap; -import java.util.Map; -import java.util.ResourceBundle; - import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.jboss.resteasy.logging.Logger; -import org.keycloak.forms.MessageBean; import org.keycloak.forms.LoginBean; +import org.keycloak.forms.MessageBean; import org.keycloak.forms.OAuthGrantBean; import org.keycloak.forms.RealmBean; import org.keycloak.forms.RegisterBean; @@ -45,6 +38,13 @@ import org.keycloak.forms.UserBean; import org.keycloak.services.FormService; import org.keycloak.services.resources.flows.Pages; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + /** * @author Viliam Rockai */ diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java index f151e15363..5d2d7d4e48 100755 --- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java +++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java @@ -7,7 +7,6 @@ import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.valves.ValveBase; import org.jboss.logging.Logger; -import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.SkeletonKeySession; import org.keycloak.adapters.as7.config.ManagedResourceConfig; import org.keycloak.representations.SkeletonKeyToken; diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthAuthenticationServerValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthAuthenticationServerValve.java deleted file mode 100755 index 0c7c24df70..0000000000 --- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthAuthenticationServerValve.java +++ /dev/null @@ -1,879 +0,0 @@ -package org.keycloak.adapters.as7; - -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.authenticator.Constants; -import org.apache.catalina.authenticator.FormAuthenticator; -import org.apache.catalina.connector.Request; -import org.apache.catalina.connector.Response; -import org.apache.catalina.core.StandardContext; -import org.apache.catalina.deploy.LoginConfig; -import org.apache.catalina.realm.GenericPrincipal; -import org.bouncycastle.openssl.PEMWriter; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.ObjectWriter; -import org.codehaus.jackson.map.SerializationConfig; -import org.codehaus.jackson.map.annotate.JsonSerialize; -import org.jboss.logging.Logger; -import org.jboss.resteasy.client.jaxrs.ResteasyClient; -import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; -import org.jboss.resteasy.jose.jws.JWSBuilder; -import org.jboss.resteasy.jose.jws.JWSInput; -import org.jboss.resteasy.jose.jws.crypto.RSAProvider; -import org.jboss.resteasy.jwt.JsonSerialization; -import org.jboss.resteasy.plugins.providers.RegisterBuiltin; -import org.jboss.resteasy.plugins.server.servlet.ServletUtil; -import org.jboss.resteasy.spi.ResteasyProviderFactory; -import org.jboss.resteasy.spi.ResteasyUriInfo; -import org.jboss.resteasy.util.BasicAuthHelper; -import org.keycloak.EnvUtil; -import org.keycloak.PemUtils; -import org.keycloak.ResourceMetadata; -import org.keycloak.SkeletonKeySession; -import org.keycloak.adapters.as7.config.AuthServerConfig; -import org.keycloak.adapters.as7.config.ManagedResourceConfig; -import org.keycloak.representations.AccessTokenResponse; -import org.keycloak.representations.SkeletonKeyToken; - -import javax.security.auth.login.LoginException; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.UriBuilder; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.security.KeyStore; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.Certificate; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Turns a web deployment into an authentication server that follwos the OAuth 2 protocol and Skeleton Key bearer tokens. - * Authentication store is backed by a JBoss security domain. - *

- * Servlet FORM authentication that uses the local security domain to authenticate and for role mappings. - *

- * Supports bearer token creation and authentication. The client asking for access must be set up as a valid user - * within the security domain. - *

- * If no an OAuth access request, this works like normal FORM authentication and authorization. - * - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class OAuthAuthenticationServerValve extends FormAuthenticator implements LifecycleListener { - - - public static class AccessCode { - protected String id = UUID.randomUUID().toString() + System.currentTimeMillis(); - protected long expiration; - protected SkeletonKeyToken token; - protected String client; - protected boolean sso; - protected String redirect; - - public boolean isExpired() { - return expiration != 0 && (System.currentTimeMillis() / 1000) > expiration; - } - - public String getId() { - return id; - } - - public long getExpiration() { - return expiration; - } - - public void setExpiration(long expiration) { - this.expiration = expiration; - } - - public SkeletonKeyToken getToken() { - return token; - } - - public void setToken(SkeletonKeyToken token) { - this.token = token; - } - - public String getClient() { - return client; - } - - public void setClient(String client) { - this.client = client; - } - - public boolean isSso() { - return sso; - } - - public void setSso(boolean sso) { - this.sso = sso; - } - - public String getRedirect() { - return redirect; - } - - public void setRedirect(String redirect) { - this.redirect = redirect; - } - } - - protected ConcurrentHashMap accessCodeMap = new ConcurrentHashMap(); - private static final Logger log = Logger.getLogger(OAuthAuthenticationServerValve.class); - - private static AtomicLong counter = new AtomicLong(1); - - private static String generateId() { - return counter.getAndIncrement() + "." + UUID.randomUUID().toString(); - } - - protected AuthServerConfig skeletonKeyConfig; - protected PrivateKey realmPrivateKey; - protected PublicKey realmPublicKey; - protected String realmPublicKeyPem; - protected ResteasyProviderFactory providers; - protected ResourceMetadata resourceMetadata; - protected UserSessionManagement userSessionManagement = new UserSessionManagement(); - protected ObjectMapper mapper; - protected ObjectWriter accessTokenResponseWriter; - protected ObjectWriter mapWriter; - - private static KeyStore loadKeyStore(String filename, String password) throws Exception { - KeyStore trustStore = KeyStore.getInstance(KeyStore - .getDefaultType()); - File truststoreFile = new File(filename); - FileInputStream trustStream = new FileInputStream(truststoreFile); - trustStore.load(trustStream, password.toCharArray()); - trustStream.close(); - return trustStore; - } - - @Override - public void start() throws LifecycleException { - super.start(); - StandardContext standardContext = (StandardContext) context; - standardContext.addLifecycleListener(this); - } - - @Override - public void lifecycleEvent(LifecycleEvent event) { - if (event.getType() == Lifecycle.AFTER_START_EVENT) init(); - } - - protected void init() { - mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT); - accessTokenResponseWriter = mapper.writerWithType(AccessTokenResponse.class); - mapWriter = mapper.writerWithType(mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class)); - - InputStream is = null; - String path = context.getServletContext().getInitParameter("skeleton.key.config.file"); - if (path == null) { - is = context.getServletContext().getResourceAsStream("/WEB-INF/resteasy-oauth.json"); - } else { - try { - is = new FileInputStream(path); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } - try { - skeletonKeyConfig = mapper.readValue(is, AuthServerConfig.class); - } catch (IOException e) { - throw new RuntimeException(e); - } - if (skeletonKeyConfig.getLoginRole() == null) { - throw new RuntimeException("You must define the login-role in your config file"); - } - if (skeletonKeyConfig.getClientRole() == null) { - throw new RuntimeException("You must define the oauth-client-role in your config file"); - } - if (skeletonKeyConfig.getRealmPrivateKey() != null) { - try { - realmPrivateKey = PemUtils.decodePrivateKey(skeletonKeyConfig.getRealmPrivateKey()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - if (skeletonKeyConfig.getRealmPublicKey() != null) { - try { - realmPublicKey = PemUtils.decodePublicKey(skeletonKeyConfig.getRealmPublicKey()); - realmPublicKeyPem = skeletonKeyConfig.getRealmPublicKey(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - if (skeletonKeyConfig.getRealmKeyStore() != null) { - if (skeletonKeyConfig.getRealmKeyAlias() == null) throw new RuntimeException("Must define realm-key-alias"); - String keystorePath = EnvUtil.replace(skeletonKeyConfig.getRealmKeyStore()); - try { - KeyStore ks = loadKeyStore(keystorePath, skeletonKeyConfig.getRealmKeystorePassword()); - if (realmPrivateKey == null) { - realmPrivateKey = (PrivateKey) ks.getKey(skeletonKeyConfig.getRealmKeyAlias(), skeletonKeyConfig.getRealmPrivateKeyPassword().toCharArray()); - } - if (realmPublicKey == null) { - Certificate cert = ks.getCertificate(skeletonKeyConfig.getRealmKeyAlias()); - realmPublicKey = cert.getPublicKey(); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - if (realmPublicKey == null) throw new RuntimeException("You have not declared a keystore or public key"); - if (realmPrivateKey == null) throw new RuntimeException("You have not declared a keystore or private key"); - if (realmPublicKeyPem == null) { - StringWriter sw = new StringWriter(); - PEMWriter writer = new PEMWriter(sw); - try { - writer.writeObject(realmPublicKey); - writer.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } - realmPublicKeyPem = sw.toString(); - realmPublicKeyPem = PemUtils.removeBeginEnd(realmPublicKeyPem); - } - providers = new ResteasyProviderFactory(); - ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(OAuthAuthenticationServerValve.class.getClassLoader()); - try { - ResteasyProviderFactory.getInstance(); // initialize builtins - RegisterBuiltin.register(providers); - } finally { - Thread.currentThread().setContextClassLoader(old); - } - resourceMetadata = new ResourceMetadata(); - resourceMetadata.setRealm(skeletonKeyConfig.getRealm()); - resourceMetadata.setRealmKey(realmPublicKey); - String truststore = skeletonKeyConfig.getTruststore(); - if (truststore != null) { - truststore = EnvUtil.replace(truststore); - String truststorePassword = skeletonKeyConfig.getTruststorePassword(); - KeyStore trust = null; - try { - trust = loadKeyStore(truststore, truststorePassword); - } catch (Exception e) { - throw new RuntimeException("Failed to load truststore", e); - } - resourceMetadata.setTruststore(trust); - } - String clientKeystore = skeletonKeyConfig.getClientKeystore(); - String clientKeyPassword = null; - if (clientKeystore != null) { - clientKeystore = EnvUtil.replace(clientKeystore); - String clientKeystorePassword = skeletonKeyConfig.getClientKeystorePassword(); - KeyStore serverKS = null; - try { - serverKS = loadKeyStore(clientKeystore, clientKeystorePassword); - } catch (Exception e) { - throw new RuntimeException("Failed to load keystore", e); - } - resourceMetadata.setClientKeystore(serverKS); - clientKeyPassword = skeletonKeyConfig.getClientKeyPassword(); - resourceMetadata.setClientKeyPassword(clientKeyPassword); - } - } - - @Override - public void invoke(Request request, Response response) throws IOException, ServletException { - try { - String contextPath = request.getContextPath(); - String requestURI = request.getDecodedRequestURI(); - log.debug("--- invoke: " + requestURI); - if (request.getMethod().equalsIgnoreCase("GET") - && context.getLoginConfig().getLoginPage().equals(request.getRequestPathMB().toString())) { - if (handleLoginPage(request, response)) return; - } else if (request.getMethod().equalsIgnoreCase("GET") - && requestURI.endsWith(Actions.J_OAUTH_LOGOUT)) { - logoutCurrentUser(request, response); - return; - } else if (request.getMethod().equalsIgnoreCase("POST") - && requestURI.endsWith(Actions.J_OAUTH_ADMIN_FORCED_LOGOUT)) { - adminLogout(request, response); - return; - } else if (request.getMethod().equalsIgnoreCase("POST") - && requestURI.startsWith(contextPath) && - requestURI.endsWith(Constants.FORM_ACTION) - && request.getParameter("client_id") != null) { - handleOAuth(request, response); - return; - } else if (request.getMethod().equalsIgnoreCase("POST") - && requestURI.endsWith(Actions.J_OAUTH_TOKEN_GRANT)) { - tokenGrant(request, response); - return; - } else if (request.getMethod().equalsIgnoreCase("POST") - && requestURI.startsWith(contextPath) && - requestURI.endsWith(Actions.J_OAUTH_RESOLVE_ACCESS_CODE)) { - resolveAccessCode(request, response); - return; - } else if (request.getMethod().equalsIgnoreCase("GET") - && requestURI.startsWith(contextPath) && - requestURI.endsWith("j_oauth_realm_info.html")) { - publishRealmInfoHtml(request, response); - return; - } - // propagate the skeleton key token string? - if (!skeletonKeyConfig.isCancelPropagation()) { - if (request.getAttribute(SkeletonKeySession.class.getName()) == null && request.getSessionInternal() != null) { - SkeletonKeySession skSession = (SkeletonKeySession) request.getSessionInternal().getNote(SkeletonKeySession.class.getName()); - if (skSession != null) { - request.setAttribute(SkeletonKeySession.class.getName(), skSession); - ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession); - } - } - } - request.setAttribute("OAUTH_FORM_ACTION", "j_security_check"); - super.invoke(request, response); - } finally { - ResteasyProviderFactory.clearContextData(); // to clear push of SkeletonKeySession - } - } - - protected boolean handleLoginPage(Request request, Response response) throws IOException, ServletException { - String client_id = request.getParameter("client_id"); - // if this is not an OAUTH redirect, just return and let the default flow happen - if (client_id == null) return false; - - String redirect_uri = request.getParameter("redirect_uri"); - String state = request.getParameter("state"); - - if (redirect_uri == null) { - response.sendError(400, "No oauth redirect query parameter set"); - return true; - } - // only bypass authentication if our session is authenticated, - // the login query parameter is on request URL, - // and we have configured the login-role - else if (!skeletonKeyConfig.isSsoDisabled() - && request.getSessionInternal() != null - && request.getSessionInternal().getPrincipal() != null - && request.getParameter("login") != null) { - log.debug("We're ALREADY LOGGED IN!!!"); - GenericPrincipal gp = (GenericPrincipal) request.getSessionInternal().getPrincipal(); - redirectAccessCode(true, response, redirect_uri, client_id, state, gp); - } else { - UriBuilder builder = UriBuilder.fromUri("j_security_check") - .queryParam("redirect_uri", redirect_uri) - .queryParam("client_id", client_id); - if (state != null) builder.queryParam("state", state); - String loginAction = builder.build().toString(); - request.setAttribute("OAUTH_FORM_ACTION", loginAction); - getNext().invoke(request, response); - } - return true; - } - - protected GenericPrincipal checkLoggedIn(Request request, HttpServletResponse response) { - if (request.getPrincipal() != null) { - return (GenericPrincipal) request.getPrincipal(); - } else if (request.getSessionInternal() != null && request.getSessionInternal().getPrincipal() != null) { - return (GenericPrincipal) request.getSessionInternal().getPrincipal(); - } - return null; - } - - - protected void adminLogout(Request request, HttpServletResponse response) throws IOException { - log.debug("<< adminLogout"); - GenericPrincipal gp = checkLoggedIn(request, response); - if (gp == null) { - if (bearer(request, response, false)) { - gp = (GenericPrincipal) request.getPrincipal(); - } else { - response.sendError(403); - return; - } - } - if (!gp.hasRole(skeletonKeyConfig.getAdminRole())) { - response.sendError(403); - return; - } - String logoutUser = request.getParameter("user"); - if (logoutUser != null) { - userSessionManagement.logout(logoutUser); - logoutResources(logoutUser, gp.getName()); - } else { - userSessionManagement.logoutAllBut(gp.getName()); - logoutResources(null, gp.getName()); - } - String forwardTo = request.getParameter("forward"); - if (forwardTo == null) { - response.setStatus(204); - return; - } - RequestDispatcher disp = - context.getServletContext().getRequestDispatcher(forwardTo); - try { - disp.forward(request.getRequest(), response); - } catch (Throwable t) { - request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - "failed to forward"); - } - - - } - - - protected void logoutCurrentUser(Request request, HttpServletResponse response) throws IOException { - if (request.getSessionInternal() == null || request.getSessionInternal().getPrincipal() == null) { - redirectToWelcomePage(request, response); - return; - } - GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal(); - String username = principal.getName(); - String admin = username; - userSessionManagement.logout(username); - request.setUserPrincipal(null); - request.setAuthType(null); - // logout user on all declared authenticated applications - logoutResources(username, admin); - redirectToWelcomePage(request, response); - } - - protected void logoutResources(String username, String admin) { - if (skeletonKeyConfig.getResources().size() != 0) { - SkeletonKeyToken token = new SkeletonKeyToken(); - token.id(generateId()); - token.principal(admin); - token.audience(skeletonKeyConfig.getRealm()); - SkeletonKeyToken.Access realmAccess = new SkeletonKeyToken.Access(); - realmAccess.addRole(skeletonKeyConfig.getAdminRole()); - token.setRealmAccess(realmAccess); - String tokenString = buildTokenString(realmPrivateKey, token); - ResteasyClient client = new ResteasyClientBuilder() - .providerFactory(providers) - .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY) - .trustStore(resourceMetadata.getTruststore()) - .keyStore(resourceMetadata.getClientKeystore(), resourceMetadata.getClientKeyPassword()) - .build(); - try { - for (String resource : skeletonKeyConfig.getResources()) { - try { - log.debug("logging out: " + resource); - WebTarget target = client.target(resource).path(Actions.J_OAUTH_REMOTE_LOGOUT); - if (username != null) target = target.queryParam("user", username); - javax.ws.rs.core.Response response = target.request() - .header("Authorization", "Bearer " + tokenString) - .put(null); - if (response.getStatus() != 204) log.error("Failed to log out"); - response.close(); - } catch (Exception ignored) { - log.error("Failed to log out", ignored); - } - } - } finally { - client.close(); - } - } - } - - protected void redirectToWelcomePage(Request request, HttpServletResponse response) throws IOException { - ResteasyUriInfo uriInfo = ServletUtil.extractUriInfo(request, null); - String[] welcomes = context.findWelcomeFiles(); - if (welcomes.length > 0) { - UriBuilder welcome = uriInfo.getBaseUriBuilder().path(welcomes[0]); - response.sendRedirect(welcome.toTemplate()); - } else { - response.setStatus(204); - } - } - - - protected void publishRealmInfoHtml(Request request, HttpServletResponse response) throws IOException { - ManagedResourceConfig rep = getRealmRepresentation(request); - StringWriter writer; - String json; - - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT); - mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT); - - StringBuffer html = new StringBuffer(); - html.append(""); - html.append("

Realm: ").append(rep.getRealm()).append("

"); - - ManagedResourceConfig bearer = new ManagedResourceConfig(); - bearer.setRealm(rep.getRealm()); - bearer.setRealmKey(rep.getRealmKey()); - writer = new StringWriter(); - mapper.writeValue(writer, bearer); - json = writer.toString(); - - html.append("

BearerTokenAuthValve Json Config

"); - html.append("
"); - - html.append("
"); - - writer = new StringWriter(); - rep.getCredentials().put("password", "REQUIRED"); - //rep.setClientId("REQUIRED"); - rep.setTruststore("REQUIRED"); - rep.setTruststorePassword("REQUIRED"); - mapper.writeValue(writer, rep); - json = writer.toString(); - html.append("

OAuthManagedResourceValve Json Config

"); - html.append("
"); - - html.append(""); - - response.setStatus(200); - response.setContentType("text/html"); - response.getOutputStream().println(html.toString()); - response.getOutputStream().flush(); - - } - - - protected ManagedResourceConfig getRealmRepresentation(Request request) { - ManagedResourceConfig rep = new ManagedResourceConfig(); - ResteasyUriInfo uriInfo = ServletUtil.extractUriInfo(request, null); - UriBuilder authUrl = uriInfo.getBaseUriBuilder().path(context.getLoginConfig().getLoginPage()); - UriBuilder codeUrl = uriInfo.getBaseUriBuilder().path(Actions.J_OAUTH_RESOLVE_ACCESS_CODE); - rep.setRealm(skeletonKeyConfig.getRealm()); - rep.setRealmKey(realmPublicKeyPem); - rep.setAuthUrl(authUrl.toTemplate()); - rep.setCodeUrl(codeUrl.toTemplate()); - rep.setAdminRole(skeletonKeyConfig.getAdminRole()); - return rep; - } - - public boolean bearer(Request request, HttpServletResponse response, boolean propagate) throws IOException { - if (request.getHeader("Authorization") != null) { - CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(resourceMetadata, false, false); - try { - if (bearer.login(request, response)) { - return true; - } - } catch (LoginException e) { - } - } - return false; - } - - @Override - protected void register(Request request, HttpServletResponse response, Principal principal, String authType, String username, String password) { - super.register(request, response, principal, authType, username, password); - log.debug("authenticate userSessionManage.login(): " + principal.getName()); - userSessionManagement.login(request.getSessionInternal(), principal.getName()); - if (!skeletonKeyConfig.isCancelPropagation()) { - GenericPrincipal gp = (GenericPrincipal) request.getPrincipal(); - if (gp != null) { - SkeletonKeyToken token = buildToken(gp); - String stringToken = buildTokenString(realmPrivateKey, token); - SkeletonKeySession skSession = new SkeletonKeySession(stringToken, token, resourceMetadata); - request.setAttribute(SkeletonKeySession.class.getName(), skSession); - ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession); - request.getSessionInternal(true).setNote(SkeletonKeySession.class.getName(), skSession); - } - } - } - - @Override - public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException { - if (bearer(request, response, true)) { - return true; - } - return super.authenticate(request, response, config); - } - - - protected void resolveAccessCode(Request request, Response response) throws IOException { - if (!request.isSecure()) { - response.sendError(400); - return; - } - // always verify code and remove access code from map before authenticating user - // if user authentication fails, we want the code to be removed irreguardless just in case we're under attack - String code = request.getParameter("code"); - JWSInput input = new JWSInput(code, providers); - boolean verifiedCode = false; - try { - verifiedCode = RSAProvider.verify(input, realmPublicKey); - } catch (Exception ignored) { - log.error("Failed to verify signature", ignored); - } - if (!verifiedCode) { - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Unable to verify code signature"); - response.sendError(400); - response.setContentType("application/json"); - mapWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - return; - } - String key = input.readContent(String.class); - AccessCode accessCode = accessCodeMap.remove(key); - String redirect = request.getParameter("redirect_uri"); - - GenericPrincipal gp = basicAuth(request, response); - if (gp == null) { - log.error("Failed to authenticate client_id"); - return; - } - if (accessCode == null) { - log.error("No access code: " + code); - response.sendError(400); - return; - } - if (accessCode.isExpired()) { - log.debug("Access code expired"); - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Code is expired"); - response.setStatus(400); - response.setContentType("application/json"); - mapWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - return; - } - if (!accessCode.getToken().isActive()) { - log.debug("token not active"); - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Token expired"); - response.setStatus(400); - response.setContentType("application/json"); - mapWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - return; - } - if (!gp.getName().equals(accessCode.getClient())) { - log.debug("not equal client"); - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Auth error"); - response.setStatus(400); - response.setContentType("application/json"); - mapWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - return; - } - if (!accessCode.getRedirect().equals(redirect)) { - log.debug("not equal redirect"); - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Auth error"); - response.setStatus(400); - response.setContentType("application/json"); - mapWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - return; - } - if (accessCode.isSso() && !gp.hasRole(skeletonKeyConfig.getLoginRole())) { - // we did not authenticate user on an access code request because a session was already established - // but, the client_id does not have permission to bypass this on a simple grant. We want - // to always ask for credentials from a simple oath request - - log.debug("does not have login permission"); - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Auth error"); - response.setStatus(400); - response.setContentType("application/json"); - mapWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - return; - } else if (!gp.hasRole(skeletonKeyConfig.getClientRole()) && !gp.hasRole(skeletonKeyConfig.getLoginRole())) { - log.debug("does not have login or client role permission for access token request"); - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Auth error"); - response.setStatus(400); - response.setContentType("application/json"); - mapWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - return; - - } - String wildcard = skeletonKeyConfig.getWildcardRole() == null ? "*" : skeletonKeyConfig.getWildcardRole(); - Set codeRoles = accessCode.getToken().getRealmAccess().getRoles(); - if (codeRoles != null && - (codeRoles.contains(skeletonKeyConfig.getClientRole()) || codeRoles.contains(skeletonKeyConfig.getLoginRole()))) { - // we store roles a oauth client is granted in the user role mapping, remove those roles as we don't want those clients with those - // permissions if they are logging in. - Set newRoles = new HashSet(); - if (codeRoles.contains(skeletonKeyConfig.getClientRole())) newRoles.add(skeletonKeyConfig.getClientRole()); - if (codeRoles.contains(skeletonKeyConfig.getLoginRole())) newRoles.add(skeletonKeyConfig.getLoginRole()); - if (codeRoles.contains(wildcard)) newRoles.add(wildcard); - codeRoles.clear(); - codeRoles.addAll(newRoles); - } - - // is we have a login role, then we don't need to filter out roles, just grant all the roles the user has - // Also, if the client has the "wildcard" role, then we don't need to filter out roles - if (codeRoles != null - && !gp.hasRole(wildcard) - && !gp.hasRole(skeletonKeyConfig.getLoginRole())) { - Set clientAllowed = new HashSet(); - for (String role : gp.getRoles()) { - clientAllowed.add(role); - } - Set newRoles = new HashSet(); - newRoles.addAll(codeRoles); - for (String role : newRoles) { - if (!clientAllowed.contains(role)) { - codeRoles.remove(role); - } - } - } - AccessTokenResponse res = accessTokenResponse(realmPrivateKey, accessCode.getToken()); - response.setStatus(200); - response.setContentType("application/json"); - accessTokenResponseWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - } - - protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) { - String encodedToken = buildTokenString(privateKey, token); - - AccessTokenResponse res = new AccessTokenResponse(); - res.setToken(encodedToken); - res.setTokenType("bearer"); - if (token.getExpiration() != 0) { - long time = token.getExpiration() - (System.currentTimeMillis() / 1000); - res.setExpiresIn(time); - } - return res; - } - - protected String buildTokenString(PrivateKey privateKey, SkeletonKeyToken token) { - byte[] tokenBytes = null; - try { - tokenBytes = JsonSerialization.toByteArray(token, false); - } catch (Exception e) { - throw new RuntimeException(e); - } - return new JWSBuilder() - .content(tokenBytes) - .rsa256(privateKey); - } - - - protected void handleOAuth(Request request, Response response) throws IOException { - log.debug("<--- Begin oauthAuthenticate"); - String redirect_uri = request.getParameter("redirect_uri"); - String client_id = request.getParameter("client_id"); - String state = request.getParameter("state"); - String username = request.getParameter(Constants.FORM_USERNAME); - String password = request.getParameter(Constants.FORM_PASSWORD); - Principal principal = context.getRealm().authenticate(username, password); - if (principal == null) { - UriBuilder builder = UriBuilder.fromUri(redirect_uri).queryParam("error", "unauthorized_client"); - if (state != null) builder.queryParam("state", state); - response.sendRedirect(builder.toTemplate()); - return; - } - GenericPrincipal gp = (GenericPrincipal) principal; - register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password); - userSessionManagement.login(request.getSessionInternal(), username); - redirectAccessCode(false, response, redirect_uri, client_id, state, gp); - - return; - } - - protected void tokenGrant(Request request, Response response) throws IOException { - if (!request.isSecure()) { - response.sendError(400); - return; - } - GenericPrincipal gp = basicAuth(request, response); - if (gp == null) return; - SkeletonKeyToken token = buildToken(gp); - AccessTokenResponse res = accessTokenResponse(realmPrivateKey, token); - response.setStatus(200); - response.setContentType("application/json"); - accessTokenResponseWriter.writeValue(response.getOutputStream(), res); - response.getOutputStream().flush(); - } - - protected GenericPrincipal basicAuth(Request request, Response response) throws IOException { - String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION); - if (authHeader == null) { - basicAuthError(response); - return null; - } - String[] creds = BasicAuthHelper.parseHeader(authHeader); - if (creds == null) { - basicAuthError(response); - return null; - } - String username = creds[0]; - String password = creds[1]; - GenericPrincipal gp = (GenericPrincipal) context.getRealm().authenticate(username, password); - if (gp == null) { - basicAuthError(response); - return null; - } - return gp; - } - - protected void basicAuthError(Response response) throws IOException { - response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + context.getLoginConfig().getRealmName() + "\""); - response.sendError(401); - } - - protected void redirectAccessCode(boolean sso, Response response, String redirect_uri, String client_id, String state, GenericPrincipal gp) throws IOException { - SkeletonKeyToken token = buildToken(gp); - AccessCode code = new AccessCode(); - code.setToken(token); - code.setClient(client_id); - code.setSso(sso); - code.setRedirect(redirect_uri); - int expiration = skeletonKeyConfig.getAccessCodeLifetime() == 0 ? 300 : skeletonKeyConfig.getAccessCodeLifetime(); - code.setExpiration((System.currentTimeMillis() / 1000) + expiration); - accessCodeMap.put(code.getId(), code); - log.debug("--- sign access code"); - String accessCode = null; - try { - accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realmPrivateKey); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - log.debug("--- build redirect"); - UriBuilder redirectUri = UriBuilder.fromUri(redirect_uri).queryParam("code", accessCode); - if (state != null) redirectUri.queryParam("state", state); - response.sendRedirect(redirectUri.toTemplate()); - log.debug("<--- end oauthAuthenticate"); - } - - protected SkeletonKeyToken buildToken(GenericPrincipal gp) { - SkeletonKeyToken token = new SkeletonKeyToken(); - token.id(generateId()); - token.principal(gp.getName()); - token.audience(skeletonKeyConfig.getRealm()); - int expiration = skeletonKeyConfig.getAccessCodeLifetime() == 0 ? 3600 : skeletonKeyConfig.getAccessCodeLifetime(); - if (skeletonKeyConfig.getTokenLifetime() > 0) { - token.expiration((System.currentTimeMillis() / 1000) + expiration); - } - SkeletonKeyToken.Access realmAccess = new SkeletonKeyToken.Access(); - for (String role : gp.getRoles()) { - realmAccess.addRole(role); - } - token.setRealmAccess(realmAccess); - return token; - } - -} diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfig.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfig.java index 655dd465a6..3aada8b36e 100755 --- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfig.java +++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfig.java @@ -5,7 +5,6 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder; import java.util.HashMap; import java.util.Map; -import java.util.Set; /** * @author Bill Burke diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfigLoader.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfigLoader.java index cc6a2d6fc8..29b1e60e7e 100755 --- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfigLoader.java +++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfigLoader.java @@ -13,7 +13,6 @@ import org.keycloak.PemUtils; import org.keycloak.ResourceMetadata; import org.keycloak.representations.idm.PublishedRealmRepresentation; -import javax.ws.rs.client.WebTarget; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; diff --git a/model/api/src/main/java/org/keycloak/models/ApplicationModel.java b/model/api/src/main/java/org/keycloak/models/ApplicationModel.java index 05dedaf3cf..efeccb8e38 100755 --- a/model/api/src/main/java/org/keycloak/models/ApplicationModel.java +++ b/model/api/src/main/java/org/keycloak/models/ApplicationModel.java @@ -1,8 +1,5 @@ package org.keycloak.models; -import java.util.List; -import java.util.Set; - /** * @author Bill Burke * @version $Revision: 1 $ diff --git a/model/api/src/main/java/org/keycloak/models/ModelProvider.java b/model/api/src/main/java/org/keycloak/models/ModelProvider.java new file mode 100755 index 0000000000..6e8e31e937 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/ModelProvider.java @@ -0,0 +1,9 @@ +package org.keycloak.models; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface ModelProvider { + KeycloakSessionFactory createFactory(); +} diff --git a/model/api/src/main/java/org/keycloak/models/OAuthClientModel.java b/model/api/src/main/java/org/keycloak/models/OAuthClientModel.java index 2b25f46b91..622500c588 100755 --- a/model/api/src/main/java/org/keycloak/models/OAuthClientModel.java +++ b/model/api/src/main/java/org/keycloak/models/OAuthClientModel.java @@ -1,7 +1,5 @@ package org.keycloak.models; -import org.keycloak.models.UserModel; - /** * @author Bill Burke * @version $Revision: 1 $ @@ -10,8 +8,4 @@ public interface OAuthClientModel { String getId(); UserModel getOAuthAgent(); - - String getBaseUrl(); - - void setBaseUrl(String base); } diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java index 5c1b468f87..401eb6a4d5 100755 --- a/model/api/src/main/java/org/keycloak/models/RealmModel.java +++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java @@ -2,7 +2,6 @@ package org.keycloak.models; import java.security.PrivateKey; import java.security.PublicKey; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -138,11 +137,11 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa List getOAuthClients(); - HashMap getSmtpConfig(); + Map getSmtpConfig(); - void setSmtpConfig(HashMap smtpConfig); + void setSmtpConfig(Map smtpConfig); - HashMap getSocialConfig(); + Map getSocialConfig(); - void setSocialConfig(HashMap socialConfig); + void setSocialConfig(Map socialConfig); } diff --git a/model/api/src/main/java/org/keycloak/models/utils/Base32.java b/model/api/src/main/java/org/keycloak/models/utils/Base32.java new file mode 100755 index 0000000000..aef4422d32 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/utils/Base32.java @@ -0,0 +1,113 @@ +package org.keycloak.models.utils; + + +/* (PD) 2006 The Bitzi Corporation + * Please see http://bitzi.com/publicdomain for more info. + * + * $Id: Base32.java,v 1.2 2006/07/14 04:58:39 gojomo Exp $ + */ + +/** + * Base32 - encodes and decodes RFC3548 Base32 (see http://www.faqs.org/rfcs/rfc3548.html ) + * + * @author Robert Kaye + * @author Gordon Mohr + */ +public class Base32 { + private static final String base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + private static final int[] base32Lookup = { 0xFF, 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + /** + * Encodes byte array to Base32 String. + * + * @param bytes Bytes to encode. + * @return Encoded byte array bytes as a String. + * + */ + public static String encode(final byte[] bytes) { + int i = 0, index = 0, digit = 0; + int currByte, nextByte; + StringBuffer base32 = new StringBuffer((bytes.length + 7) * 8 / 5); + + while (i < bytes.length) { + currByte = (bytes[i] >= 0) ? bytes[i] : (bytes[i] + 256); + + /* Is the current digit going to span a byte boundary? */ + if (index > 3) { + if ((i + 1) < bytes.length) { + nextByte = (bytes[i + 1] >= 0) ? bytes[i + 1] : (bytes[i + 1] + 256); + } else { + nextByte = 0; + } + + digit = currByte & (0xFF >> index); + index = (index + 5) % 8; + digit <<= index; + digit |= nextByte >> (8 - index); + i++; + } else { + digit = (currByte >> (8 - (index + 5))) & 0x1F; + index = (index + 5) % 8; + if (index == 0) + i++; + } + base32.append(base32Chars.charAt(digit)); + } + + return base32.toString(); + } + + /** + * Decodes the given Base32 String to a raw byte array. + * + * @param base32 + * @return Decoded base32 String as a raw byte array. + */ + public static byte[] decode(final String base32) { + int i, index, lookup, offset, digit; + byte[] bytes = new byte[base32.length() * 5 / 8]; + + for (i = 0, index = 0, offset = 0; i < base32.length(); i++) { + lookup = base32.charAt(i) - '0'; + + /* Skip chars outside the lookup table */ + if (lookup < 0 || lookup >= base32Lookup.length) { + continue; + } + + digit = base32Lookup[lookup]; + + /* If this digit is not in the table, ignore it */ + if (digit == 0xFF) { + continue; + } + + if (index <= 3) { + index = (index + 5) % 8; + if (index == 0) { + bytes[offset] |= digit; + offset++; + if (offset >= bytes.length) + break; + } else { + bytes[offset] |= digit << (8 - index); + } + } else { + index = (index + 5) % 8; + bytes[offset] |= (digit >>> index); + offset++; + + if (offset >= bytes.length) { + break; + } + bytes[offset] |= digit << (8 - index); + } + } + return bytes; + } + +} \ No newline at end of file diff --git a/model/api/src/main/java/org/keycloak/models/utils/Base64.java b/model/api/src/main/java/org/keycloak/models/utils/Base64.java new file mode 100755 index 0000000000..2fa912b11d --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/utils/Base64.java @@ -0,0 +1,2282 @@ +package org.keycloak.models.utils; + +/** + *

Encodes and decodes to and from Base64 notation.

+ *

Homepage: http://iharder.net/base64.

+ *

+ *

Example:

+ *

+ * String encoded = Base64.encode( myByteArray ); + *
+ * byte[] myByteArray = Base64.decode( encoded ); + *

+ *

The options parameter, which appears in a few places, is used to pass + * several pieces of information to the encoder. In the "higher level" methods such as + * encodeBytes( bytes, options ) the options parameter can be used to indicate such + * things as first gzipping the bytes before encoding them, not inserting linefeeds, + * and encoding using the URL-safe and Ordered dialects.

+ *

+ *

Note, according to RFC3548, + * Section 2.1, implementations should not add line feeds unless explicitly told + * to do so. I've got Base64 set to this behavior now, although earlier versions + * broke lines by default.

+ *

+ *

The constants defined in Base64 can be OR-ed together to combine options, so you + * might make a call like this:

+ *

+ * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES ); + *

to compress the data before encoding it and then making the output have newline characters.

+ *

Also...

+ * String encoded = Base64.encodeBytes( crazyString.getBytes() ); + *

+ *

+ *

+ *

+ * Change Log: + *

+ *
    + *
  • v2.3.7 - Fixed subtle bug when base 64 input stream contained the + * value 01111111, which is an invalid base 64 character but should not + * throw an ArrayIndexOutOfBoundsException either. Led to discovery of + * mishandling (or potential for better handling) of other bad input + * characters. You should now get an IOException if you try decoding + * something that has bad characters in it.
  • + *
  • v2.3.6 - Fixed bug when breaking lines and the final byte of the encoded + * string ended in the last column; the buffer was not properly shrunk and + * contained an extra (null) byte that made it into the string.
  • + *
  • v2.3.5 - Fixed bug in {@link #encodeFromFile} where estimated buffer size + * was wrong for files of size 31, 34, and 37 bytes.
  • + *
  • v2.3.4 - Fixed bug when working with gzipped streams whereby flushing + * the Base64.OutputStream closed the Base64 encoding (by padding with equals + * signs) too soon. Also added an option to suppress the automatic decoding + * of gzipped streams. Also added experimental support for specifying a + * class loader when using the + * {@link #decodeToObject(java.lang.String, int, java.lang.ClassLoader)} + * method.
  • + *
  • v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java + * footprint with its CharEncoders and so forth. Fixed some javadocs that were + * inconsistent. Removed imports and specified things like java.io.IOException + * explicitly inline.
  • + *
  • v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the + * final encoded data will be so that the code doesn't have to create two output + * arrays: an oversized initial one and then a final, exact-sized one. Big win + * when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not + * using the gzip options which uses a different mechanism with streams and stuff).
  • + *
  • v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some + * similar helper methods to be more efficient with memory by not returning a + * String but just a byte array.
  • + *
  • v2.3 - This is not a drop-in replacement! This is two years of comments + * and bug fixes queued up and finally executed. Thanks to everyone who sent + * me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else. + * Much bad coding was cleaned up including throwing exceptions where necessary + * instead of returning null values or something similar. Here are some changes + * that may affect you: + *
      + *
    • Does not break lines, by default. This is to keep in compliance with + * RFC3548.
    • + *
    • Throws exceptions instead of returning null values. Because some operations + * (especially those that may permit the GZIP option) use IO streams, there + * is a possiblity of an java.io.IOException being thrown. After some discussion and + * thought, I've changed the behavior of the methods to throw java.io.IOExceptions + * rather than return null if ever there's an error. I think this is more + * appropriate, though it will require some changes to your code. Sorry, + * it should have been done this way to begin with.
    • + *
    • Removed all references to System.out, System.err, and the like. + * Shame on me. All I can say is sorry they were ever there.
    • + *
    • Throws NullPointerExceptions and IllegalArgumentExceptions as needed + * such as when passed arrays are null or offsets are invalid.
    • + *
    • Cleaned up as much javadoc as I could to avoid any javadoc warnings. + * This was especially annoying before for people who were thorough in their + * own projects and then had gobs of javadoc warnings on this file.
    • + *
    + *
  • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug + * when using very small files (~< 40 bytes).
  • + *
  • v2.2 - Added some helper methods for encoding/decoding directly from + * one file to the next. Also added a main() method to support command line + * encoding/decoding from one file to the next. Also added these Base64 dialects: + *
      + *
    1. The default is RFC3548 format.
    2. + *
    3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates + * URL and file name friendly format as described in Section 4 of RFC3548. + * http://www.faqs.org/rfcs/rfc3548.html
    4. + *
    5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates + * URL and file name friendly format that preserves lexical ordering as described + * in http://www.faqs.org/qa/rfcc-1940.html
    6. + *
    + * Special thanks to Jim Kellerman at http://www.powerset.com/ + * for contributing the new Base64 dialects. + *
  • + *

    + *

  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.
  • + *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).
  • + *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.
  • + *
  • v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (ints that you "OR" together).
  • + *
  • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
  • + *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.
  • + *
  • v1.4 - Added helper methods to read/write files.
  • + *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • + *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.
  • + *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • + *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • + *
+ *

+ *

+ * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

+ * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.3.7 + */ +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** + * No options specified. Value is zero. + */ + public final static int NO_OPTIONS = 0; + + /** + * Specify encoding in first bit. Value is one. + */ + public final static int ENCODE = 1; + + + /** + * Specify decoding in first bit. Value is zero. + */ + public final static int DECODE = 0; + + + /** + * Specify that data should be gzip-compressed in second bit. Value is two. + */ + public final static int GZIP = 2; + + /** + * Specify that gzipped data should not be automatically gunzipped. + */ + public final static int DONT_GUNZIP = 4; + + + /** + * Do break lines when encoding. Value is 8. + */ + public final static int DO_BREAK_LINES = 8; + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public final static int URL_SAFE = 16; + + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + public final static int ORDERED = 32; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** + * Maximum line length (76) of Base64 output. + */ + private final static int MAX_LINE_LENGTH = 76; + + + /** + * The equals sign (=) as a byte. + */ + private final static byte EQUALS_SIGN = (byte) '='; + + + /** + * The new line character (\n) as a byte. + */ + private final static byte NEW_LINE = (byte) '\n'; + + + /** + * Preferred encoding. + */ + private final static String PREFERRED_ENCODING = "US-ASCII"; + + + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + + /** + * The 64 valid Base64 values. + */ + /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ + private final static byte[] _STANDARD_ALPHABET = { + (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', + (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', + (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', + (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', + (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', + (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', + (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', + (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', + (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', + (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' + }; + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + */ + private final static byte[] _STANDARD_DECODABET = { + -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 + -5, -5, // Whitespace: Tab and Linefeed + -9, -9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 + -9, -9, -9, -9, -9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9, -9, -9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine + -9, -9, -9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9, -9, -9, // Decimal 62 - 64 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' + -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' + -9, -9, -9, -9, -9 // Decimal 123 - 127 + , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 + }; + + +/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ + private final static byte[] _URL_SAFE_ALPHABET = { + (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', + (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', + (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', + (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', + (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', + (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', + (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', + (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', + (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', + (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_' + }; + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ + private final static byte[] _URL_SAFE_DECODABET = { + -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 + -5, -5, // Whitespace: Tab and Linefeed + -9, -9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 + -9, -9, -9, -9, -9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine + -9, -9, -9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9, -9, -9, // Decimal 62 - 64 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' + -9, -9, -9, -9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' + -9, -9, -9, -9, -9 // Decimal 123 - 127 + , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 + }; + + +/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ + + /** + * I don't get the point of this technique, but someone requested it, + * and it is described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + private final static byte[] _ORDERED_ALPHABET = { + (byte) '-', + (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', + (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', + (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', + (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', + (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', + (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', + (byte) '_', + (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', + (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', + (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', + (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' + }; + + /** + * Used in decoding the "ordered" dialect of Base64. + */ + private final static byte[] _ORDERED_DECODABET = { + -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 + -5, -5, // Whitespace: Tab and Linefeed + -9, -9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 + -9, -9, -9, -9, -9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine + -9, -9, -9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9, -9, -9, // Decimal 62 - 64 + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M' + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z' + -9, -9, -9, -9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm' + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z' + -9, -9, -9, -9, -9 // Decimal 123 - 127 + , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243 + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 + }; + + +/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ + + + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getAlphabet(int options) + { + if ((options & URL_SAFE) == URL_SAFE) + { + return _URL_SAFE_ALPHABET; + } + else if ((options & ORDERED) == ORDERED) + { + return _ORDERED_ALPHABET; + } + else + { + return _STANDARD_ALPHABET; + } + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getDecodabet(int options) + { + if ((options & URL_SAFE) == URL_SAFE) + { + return _URL_SAFE_DECODABET; + } + else if ((options & ORDERED) == ORDERED) + { + return _ORDERED_DECODABET; + } + else + { + return _STANDARD_DECODABET; + } + } // end getAlphabet + + + /** + * Defeats instantiation. + */ + private Base64() + { + } + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes, int options) + { + encode3to4(threeBytes, 0, numSigBytes, b4, 0, options); + return b4; + } // end encode3to4 + + + /** + *

Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes.

+ *

This is the lowest level of the encoding methods with + * all possible parameters.

+ * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options) + { + + byte[] ALPHABET = getAlphabet(options); + + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0) + | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0) + | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0); + + switch (numSigBytes) + { + case 3: + destination[destOffset] = ALPHABET[(inBuff >>> 18)]; + destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; + destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; + destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f]; + return destination; + + case 2: + destination[destOffset] = ALPHABET[(inBuff >>> 18)]; + destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; + destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; + destination[destOffset + 3] = EQUALS_SIGN; + return destination; + + case 1: + destination[destOffset] = ALPHABET[(inBuff >>> 18)]; + destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; + destination[destOffset + 2] = EQUALS_SIGN; + destination[destOffset + 3] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + /** + * Performs Base64 encoding on the raw ByteBuffer, + * writing it to the encoded ByteBuffer. + * This is an experimental feature. Currently it does not + * pass along any options (such as {@link #DO_BREAK_LINES} + * or {@link #GZIP}. + * + * @param raw input buffer + * @param encoded output buffer + * @since 2.3 + */ + public static void encode(java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded) + { + byte[] raw3 = new byte[3]; + byte[] enc4 = new byte[4]; + + while (raw.hasRemaining()) + { + int rem = Math.min(3, raw.remaining()); + raw.get(raw3, 0, rem); + Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS); + encoded.put(enc4); + } // end input remaining + } + + + /** + * Performs Base64 encoding on the raw ByteBuffer, + * writing it to the encoded CharBuffer. + * This is an experimental feature. Currently it does not + * pass along any options (such as {@link #DO_BREAK_LINES} + * or {@link #GZIP}. + * + * @param raw input buffer + * @param encoded output buffer + * @since 2.3 + */ + public static void encode(java.nio.ByteBuffer raw, java.nio.CharBuffer encoded) + { + byte[] raw3 = new byte[3]; + byte[] enc4 = new byte[4]; + + while (raw.hasRemaining()) + { + int rem = Math.min(3, raw.remaining()); + raw.get(raw3, 0, rem); + Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS); + for (int i = 0; i < 4; i++) + { + encoded.put((char) (enc4[i] & 0xFF)); + } + } // end input remaining + } + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. + *

+ *

As of v 2.3, if the object + * cannot be serialized or there is another error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ *

+ * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @throws java.io.IOException if there is an error + * @throws NullPointerException if serializedObject is null + * @since 1.4 + */ + public static String encodeObject(java.io.Serializable serializableObject) + throws java.io.IOException + { + return encodeObject(serializableObject, NO_OPTIONS); + } // end encodeObject + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. + *

+ *

As of v 2.3, if the object + * cannot be serialized or there is another error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ *

+ * The object is not GZip-compressed before being encoded. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     * 
+ *

+ * Example: encodeObject( myObj, Base64.GZIP ) or + *

+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @throws java.io.IOException if there is an error + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject(java.io.Serializable serializableObject, int options) + throws java.io.IOException + { + + if (serializableObject == null) + { + throw new NullPointerException("Cannot serialize a null object."); + } // end if: null + + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.util.zip.GZIPOutputStream gzos = null; + java.io.ObjectOutputStream oos = null; + + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream(baos, ENCODE | options); + if ((options & GZIP) != 0) + { + // Gzip + gzos = new java.util.zip.GZIPOutputStream(b64os); + oos = new java.io.ObjectOutputStream(gzos); + } + else + { + // Not gzipped + oos = new java.io.ObjectOutputStream(b64os); + } + oos.writeObject(serializableObject); + } // end try + catch (java.io.IOException e) + { + // Catch it and then throw it immediately so that + // the finally{} block is called for cleanup. + throw e; + } // end catch + finally + { + try + { oos.close(); } + catch (Exception e) + {} + try + { gzos.close(); } + catch (Exception e) + {} + try + { b64os.close(); } + catch (Exception e) + {} + try + { baos.close(); } + catch (Exception e) + {} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String(baos.toByteArray(), PREFERRED_ENCODING); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + // Fall back to some Java default + return new String(baos.toByteArray()); + } // end catch + + } // end encode + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @return The data in Base64-encoded form + * @throws NullPointerException if source array is null + * @since 1.4 + */ + public static String encodeBytes(byte[] source) + { + // Since we're not going to have the GZIP encoding turned on, + // we're not going to have an java.io.IOException thrown, so + // we should not force the user to have to catch it. + String encoded = null; + try + { + encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); + } + catch (java.io.IOException ex) + { + assert false : ex.getMessage(); + } // end catch + assert encoded != null; + return encoded; + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) + *

+ *

+ *

As of v 2.3, if there is an error with the GZIP stream, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param source The data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes(byte[] source, int options) throws java.io.IOException + { + return encodeBytes(source, 0, source.length, options); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + *

+ *

As of v 2.3, if there is an error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @return The Base64-encoded data as a String + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @since 1.4 + */ + public static String encodeBytes(byte[] source, int off, int len) + { + // Since we're not going to have the GZIP encoding turned on, + // we're not going to have an java.io.IOException thrown, so + // we should not force the user to have to catch it. + String encoded = null; + try + { + encoded = encodeBytes(source, off, len, NO_OPTIONS); + } + catch (java.io.IOException ex) + { + assert false : ex.getMessage(); + } // end catch + assert encoded != null; + return encoded; + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) + *

+ *

+ *

As of v 2.3, if there is an error with the GZIP stream, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes(byte[] source, int off, int len, int options) throws java.io.IOException + { + byte[] encoded = encodeBytesToBytes(source, off, len, options); + + // Return value according to relevant encoding. + try + { + return new String(encoded, PREFERRED_ENCODING); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String(encoded); + } // end catch + + } // end encodeBytes + + + /** + * Similar to {@link #encodeBytes(byte[])} but returns + * a byte array instead of instantiating a String. This is more efficient + * if you're working with I/O streams and have large data sets to encode. + * + * @param source The data to convert + * @return The Base64-encoded data as a byte[] (of ASCII characters) + * @throws NullPointerException if source array is null + * @since 2.3.1 + */ + public static byte[] encodeBytesToBytes(byte[] source) + { + byte[] encoded = null; + try + { + encoded = encodeBytesToBytes(source, 0, source.length, Base64.NO_OPTIONS); + } + catch (java.io.IOException ex) + { + assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); + } + return encoded; + } + + + /** + * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns + * a byte array instead of instantiating a String. This is more efficient + * if you're working with I/O streams and have large data sets to encode. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @since 2.3.1 + */ + public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) throws java.io.IOException + { + + if (source == null) + { + throw new NullPointerException("Cannot serialize a null array."); + } // end if: null + + if (off < 0) + { + throw new IllegalArgumentException("Cannot have negative offset: " + off); + } // end if: off < 0 + + if (len < 0) + { + throw new IllegalArgumentException("Cannot have length offset: " + len); + } // end if: len < 0 + + if (off + len > source.length) + { + throw new IllegalArgumentException( + String.format("Cannot have offset of %d and length of %d with array of length %d", off, len, source.length)); + } // end if: off < 0 + + + // Compress? + if ((options & GZIP) != 0) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream(baos, ENCODE | options); + gzos = new java.util.zip.GZIPOutputStream(b64os); + + gzos.write(source, off, len); + gzos.close(); + } // end try + catch (java.io.IOException e) + { + // Catch it and then throw it immediately so that + // the finally{} block is called for cleanup. + throw e; + } // end catch + finally + { + try + { gzos.close(); } + catch (Exception e) + {} + try + { b64os.close(); } + catch (Exception e) + {} + try + { baos.close(); } + catch (Exception e) + {} + } // end finally + + return baos.toByteArray(); + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + boolean breakLines = (options & DO_BREAK_LINES) != 0; + + //int len43 = len * 4 / 3; + //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + // Try to determine more precisely how big the array needs to be. + // If we get it right, we don't have to do an array copy, and + // we save a bunch of memory. + int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual encoding + if (breakLines) + { + encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters + } + byte[] outBuff = new byte[encLen]; + + + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for (; d < len2; d += 3, e += 4) + { + encode3to4(source, d + off, 3, outBuff, e, options); + + lineLength += 4; + if (breakLines && lineLength >= MAX_LINE_LENGTH) + { + outBuff[e + 4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if (d < len) + { + encode3to4(source, d + off, len - d, outBuff, e, options); + e += 4; + } // end if: some padding needed + + + // Only resize array if we didn't guess it right. + if (e <= outBuff.length - 1) + { + // If breaking lines and the last byte falls right at + // the line length (76 bytes per line), there will be + // one extra byte, and the array will need to be resized. + // Not too bad of an estimate on array size, I'd say. + byte[] finalOut = new byte[e]; + System.arraycopy(outBuff, 0, finalOut, 0, e); + //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); + return finalOut; + } + else + { + //System.err.println("No need to resize array."); + return outBuff; + } + + } // end else: don't compress + + } // end encodeBytesToBytes + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + *

This is the lowest level of the decoding methods with + * all possible parameters.

+ * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return the number of decoded bytes converted + * @throws NullPointerException if source or destination arrays are null + * @throws IllegalArgumentException if srcOffset or destOffset are invalid + * or there is not enough room in the array. + * @since 1.3 + */ + private static int decode4to3( + byte[] source, int srcOffset, + byte[] destination, int destOffset, int options) + { + + // Lots of error checking and exception throwing + if (source == null) + { + throw new NullPointerException("Source array was null."); + } // end if + if (destination == null) + { + throw new NullPointerException("Destination array was null."); + } // end if + if (srcOffset < 0 || srcOffset + 3 >= source.length) + { + throw new IllegalArgumentException(String.format( + "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset)); + } // end if + if (destOffset < 0 || destOffset + 2 >= destination.length) + { + throw new IllegalArgumentException(String.format( + "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset)); + } // end if + + + byte[] DECODABET = getDecodabet(options); + + // Example: Dk== + if (source[srcOffset + 2] == EQUALS_SIGN) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) + | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12); + + destination[destOffset] = (byte) (outBuff >>> 16); + return 1; + } + + // Example: DkL= + else if (source[srcOffset + 3] == EQUALS_SIGN) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) + | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) + | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6); + + destination[destOffset] = (byte) (outBuff >>> 16); + destination[destOffset + 1] = (byte) (outBuff >>> 8); + return 2; + } + + // Example: DkLE + else + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) + | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) + | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) + | ((DECODABET[source[srcOffset + 3]] & 0xFF)); + + + destination[destOffset] = (byte) (outBuff >> 16); + destination[destOffset + 1] = (byte) (outBuff >> 8); + destination[destOffset + 2] = (byte) (outBuff); + + return 3; + } + } // end decodeToBytes + + + /** + * Low-level access to decoding ASCII characters in + * the form of a byte array. Ignores GUNZIP option, if + * it's set. This is not generally a recommended method, + * although it is used internally as part of the decoding process. + * Special case: if len = 0, an empty array is returned. Still, + * if you need more speed and reduced memory footprint (and aren't + * gzipping), consider this method. + * + * @param source The Base64 encoded data + * @return decoded data + * @since 2.3.1 + */ + public static byte[] decode(byte[] source) + throws java.io.IOException + { + byte[] decoded = null; +// try { + decoded = decode(source, 0, source.length, Base64.NO_OPTIONS); +// } catch( java.io.IOException ex ) { +// assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); +// } + return decoded; + } + + + /** + * Low-level access to decoding ASCII characters in + * the form of a byte array. Ignores GUNZIP option, if + * it's set. This is not generally a recommended method, + * although it is used internally as part of the decoding process. + * Special case: if len = 0, an empty array is returned. Still, + * if you need more speed and reduced memory footprint (and aren't + * gzipping), consider this method. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @param options Can specify options such as alphabet type to use + * @return decoded data + * @throws java.io.IOException If bogus characters exist in source data + * @since 1.3 + */ + public static byte[] decode(byte[] source, int off, int len, int options) + throws java.io.IOException + { + + // Lots of error checking and exception throwing + if (source == null) + { + throw new NullPointerException("Cannot decode null source array."); + } // end if + if (off < 0 || off + len > source.length) + { + throw new IllegalArgumentException(String.format( + "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len)); + } // end if + + if (len == 0) + { + return new byte[0]; + } + else if (len < 4) + { + throw new IllegalArgumentException( + "Base64-encoded string must have at least four characters, but length specified was " + len); + } // end if + + byte[] DECODABET = getDecodabet(options); + + int len34 = len * 3 / 4; // Estimate on array size + byte[] outBuff = new byte[len34]; // Upper limit on size of output + int outBuffPosn = 0; // Keep track of where we're writing + + byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space + int b4Posn = 0; // Keep track of four byte input buffer + int i = 0; // Source array counter + byte sbiDecode = 0; // Special value from DECODABET + + for (i = off; i < off + len; i++) + { // Loop through source + + sbiDecode = DECODABET[source[i] & 0xFF]; + + // White space, Equals sign, or legit Base64 character + // Note the values such as -5 and -9 in the + // DECODABETs at the top of the file. + if (sbiDecode >= WHITE_SPACE_ENC) + { + if (sbiDecode >= EQUALS_SIGN_ENC) + { + b4[b4Posn++] = source[i]; // Save non-whitespace + if (b4Posn > 3) + { // Time to decode? + outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if (source[i] == EQUALS_SIGN) + { + break; + } // end if: equals sign + } // end if: quartet built + } // end if: equals sign or better + } // end if: white space, equals sign or better + else + { + // There's a bad input character in the Base64 stream. + throw new java.io.IOException(String.format( + "Bad Base64 input character decimal %d in array position %d", ((int) source[i]) & 0xFF, i)); + } // end else: + } // each input character + + byte[] out = new byte[outBuffPosn]; + System.arraycopy(outBuff, 0, out, 0, outBuffPosn); + return out; + } // end decode + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @throws java.io.IOException If there is a problem + * @since 1.4 + */ + public static byte[] decode(String s) throws java.io.IOException + { + return decode(s, NO_OPTIONS); + } + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @param options encode options such as URL_SAFE + * @return the decoded data + * @throws java.io.IOException if there is an error + * @throws NullPointerException if s is null + * @since 1.4 + */ + public static byte[] decode(String s, int options) throws java.io.IOException + { + + if (s == null) + { + throw new NullPointerException("Input string was null."); + } // end if + + byte[] bytes; + try + { + bytes = s.getBytes(PREFERRED_ENCODING); + } // end try + catch (java.io.UnsupportedEncodingException uee) + { + bytes = s.getBytes(); + } // end catch + // + + // Decode + bytes = decode(bytes, 0, bytes.length, options); + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + boolean dontGunzip = (options & DONT_GUNZIP) != 0; + if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) + { + + int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream(bytes); + gzis = new java.util.zip.GZIPInputStream(bais); + + while ((length = gzis.read(buffer)) >= 0) + { + baos.write(buffer, 0, length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch (java.io.IOException e) + { + e.printStackTrace(); + // Just return originally-decoded bytes + } // end catch + finally + { + try + { baos.close(); } + catch (Exception e) + {} + try + { gzis.close(); } + catch (Exception e) + {} + try + { bais.close(); } + catch (Exception e) + {} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @throws NullPointerException if encodedObject is null + * @throws java.io.IOException if there is a general error + * @throws ClassNotFoundException if the decoded object is of a + * class that cannot be found by the JVM + * @since 1.5 + */ + public static Object decodeToObject(String encodedObject) + throws java.io.IOException, java.lang.ClassNotFoundException + { + return decodeToObject(encodedObject, NO_OPTIONS, null); + } + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * If loader is not null, it will be the class loader + * used when deserializing. + * + * @param encodedObject The Base64 data to decode + * @param options Various parameters related to decoding + * @param loader Optional class loader to use in deserializing classes. + * @return The decoded and deserialized object + * @throws NullPointerException if encodedObject is null + * @throws java.io.IOException if there is a general error + * @throws ClassNotFoundException if the decoded object is of a + * class that cannot be found by the JVM + * @since 2.3.4 + */ + public static Object decodeToObject( + String encodedObject, int options, final ClassLoader loader) + throws java.io.IOException, java.lang.ClassNotFoundException + { + + // Decode and gunzip if necessary + byte[] objBytes = decode(encodedObject, options); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream(objBytes); + + // If no custom class loader is provided, use Java's builtin OIS. + if (loader == null) + { + ois = new java.io.ObjectInputStream(bais); + } // end if: no loader provided + + // Else make a customized object input stream that uses + // the provided class loader. + else + { + ois = new java.io.ObjectInputStream(bais) + { + @Override + public Class resolveClass(java.io.ObjectStreamClass streamClass) + throws java.io.IOException, ClassNotFoundException + { + Class c = Class.forName(streamClass.getName(), false, loader); + if (c == null) + { + return super.resolveClass(streamClass); + } + else + { + return c; // Class loader knows of this class. + } // end else: not null + } // end resolveClass + }; // end ois + } // end else: no custom class loader + + obj = ois.readObject(); + } // end try + catch (java.io.IOException e) + { + throw e; // Catch and throw in order to execute finally{} + } // end catch + catch (java.lang.ClassNotFoundException e) + { + throw e; // Catch and throw in order to execute finally{} + } // end catch + finally + { + try + { bais.close(); } + catch (Exception e) + {} + try + { ois.close(); } + catch (Exception e) + {} + } // end finally + + return obj; + } // end decodeObject + + + /** + * Convenience method for encoding data to a file. + *

+ *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @throws java.io.IOException if there is an error + * @throws NullPointerException if dataToEncode is null + * @since 2.1 + */ + public static void encodeToFile(byte[] dataToEncode, String filename) + throws java.io.IOException + { + + if (dataToEncode == null) + { + throw new NullPointerException("Data to encode was null."); + } // end iff + + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream(filename), Base64.ENCODE); + bos.write(dataToEncode); + } // end try + catch (java.io.IOException e) + { + throw e; // Catch and throw to execute finally{} block + } // end catch: java.io.IOException + finally + { + try + { bos.close(); } + catch (Exception e) + {} + } // end finally + + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + *

+ *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static void decodeToFile(String dataToDecode, String filename) + throws java.io.IOException + { + + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream(filename), Base64.DECODE); + bos.write(dataToDecode.getBytes(PREFERRED_ENCODING)); + } // end try + catch (java.io.IOException e) + { + throw e; // Catch and throw to execute finally{} block + } // end catch: java.io.IOException + finally + { + try + { bos.close(); } + catch (Exception e) + {} + } // end finally + + } // end decodeToFile + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + *

+ *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param filename Filename for reading encoded data + * @return decoded byte array + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static byte[] decodeFromFile(String filename) + throws java.io.IOException + { + + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File(filename); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if (file.length() > Integer.MAX_VALUE) + { + throw new java.io.IOException("File is too big for this convenience method (" + file.length() + " bytes)."); + } // end if: file too big for int index + buffer = new byte[(int) file.length()]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream(file)), Base64.DECODE); + + // Read until done + while ((numBytes = bis.read(buffer, length, 4096)) >= 0) + { + length += numBytes; + } // end while + + // Save in a variable to return + decodedData = new byte[length]; + System.arraycopy(buffer, 0, decodedData, 0, length); + + } // end try + catch (java.io.IOException e) + { + throw e; // Catch and release to execute finally{} + } // end catch: java.io.IOException + finally + { + try + { bis.close(); } + catch (Exception e) + {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + *

+ *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param filename Filename for reading binary data + * @return base64-encoded string + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static String encodeFromFile(String filename) + throws java.io.IOException + { + + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File(filename); + byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 40)]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5) + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream(file)), Base64.ENCODE); + + // Read until done + while ((numBytes = bis.read(buffer, length, 4096)) >= 0) + { + length += numBytes; + } // end while + + // Save in a variable to return + encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING); + + } // end try + catch (java.io.IOException e) + { + throw e; // Catch and release to execute finally{} + } // end catch: java.io.IOException + finally + { + try + { bis.close(); } + catch (Exception e) + {} + } // end finally + + return encodedData; + } // end encodeFromFile + + /** + * Reads infile and encodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @throws java.io.IOException if there is an error + * @since 2.2 + */ + public static void encodeFileToFile(String infile, String outfile) + throws java.io.IOException + { + + String encoded = Base64.encodeFromFile(infile); + java.io.OutputStream out = null; + try + { + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream(outfile)); + out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output. + } // end try + catch (java.io.IOException e) + { + throw e; // Catch and release to execute finally{} + } // end catch + finally + { + try + { out.close(); } + catch (Exception ex) + {} + } // end finally + } // end encodeFileToFile + + + /** + * Reads infile and decodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @throws java.io.IOException if there is an error + * @since 2.2 + */ + public static void decodeFileToFile(String infile, String outfile) + throws java.io.IOException + { + + byte[] decoded = Base64.decodeFromFile(infile); + java.io.OutputStream out = null; + try + { + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream(outfile)); + out.write(decoded); + } // end try + catch (java.io.IOException e) + { + throw e; // Catch and release to execute finally{} + } // end catch + finally + { + try + { out.close(); } + catch (Exception ex) + {} + } // end finally + } // end decodeFileToFile + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + /** + * A {@link Base64.InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + private int options; // Record options used to create the stream. + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream(java.io.InputStream in) + { + this(in, DECODE); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DO_BREAK_LINES: break lines at 76 characters
+         *     (only meaningful when encoding)
+         * 
+ *

+ * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DO_BREAK_LINES + * @since 2.0 + */ + public InputStream(java.io.InputStream in, int options) + { + + super(in); + this.options = options; // Record for later + this.breakLines = (options & DO_BREAK_LINES) > 0; + this.encode = (options & ENCODE) > 0; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[bufferLength]; + this.position = -1; + this.lineLength = 0; + this.decodabet = getDecodabet(options); + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + @Override + public int read() throws java.io.IOException + { + + // Do we need to get data? + if (position < 0) + { + if (encode) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for (int i = 0; i < 3; i++) + { + int b = in.read(); + + // If end of stream, b is -1. + if (b >= 0) + { + b3[i] = (byte) b; + numBinaryBytes++; + } + else + { + break; // out of for loop + } // end else: end of stream + + } // end for: each needed input byte + + if (numBinaryBytes > 0) + { + encode3to4(b3, 0, numBinaryBytes, buffer, 0, options); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; // Must be end of stream + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for (i = 0; i < 4; i++) + { + // Read four "meaningful" bytes: + int b = 0; + do + { b = in.read(); } + while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC); + + if (b < 0) + { + break; // Reads a -1 if end of stream + } // end if: end of stream + + b4[i] = (byte) b; + } // end for: each needed input byte + + if (i == 4) + { + numSigBytes = decode4to3(b4, 0, buffer, 0, options); + position = 0; + } // end if: got four characters + else if (i == 0) + { + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException("Improperly padded Base64 input."); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if (position >= 0) + { + // End of relevant data? + if ( /*!encode &&*/ position >= numSigBytes) + { + return -1; + } // end if: got data + + if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[position++]; + + if (position >= bufferLength) + { + position = -1; + } // end if: end + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + throw new java.io.IOException("Error in Base64 code reading stream."); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + @Override + public int read(byte[] dest, int off, int len) + throws java.io.IOException + { + int i; + int b; + for (i = 0; i < len; i++) + { + b = read(); + + if (b >= 0) + { + dest[off + i] = (byte) b; + } + else if (i == 0) + { + return -1; + } + else + { + break; // Out of 'for' loop + } // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + /** + * A {@link Base64.OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + private int options; // Record for later + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream(java.io.OutputStream out) + { + this(out, ENCODE); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DO_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         * 
+ *

+ * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DO_BREAK_LINES + * @since 1.3 + */ + public OutputStream(java.io.OutputStream out, int options) + { + super(out); + this.breakLines = (options & DO_BREAK_LINES) != 0; + this.encode = (options & ENCODE) != 0; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[bufferLength]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + this.options = options; + this.decodabet = getDecodabet(options); + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + @Override + public void write(int theByte) + throws java.io.IOException + { + // Encoding suspended? + if (suspendEncoding) + { + this.out.write(theByte); + return; + } // end if: supsended + + // Encode? + if (encode) + { + buffer[position++] = (byte) theByte; + if (position >= bufferLength) + { // Enough to encode. + + this.out.write(encode3to4(b4, buffer, bufferLength, options)); + + lineLength += 4; + if (breakLines && lineLength >= MAX_LINE_LENGTH) + { + this.out.write(NEW_LINE); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) + { + buffer[position++] = (byte) theByte; + if (position >= bufferLength) + { // Enough to output. + + int len = Base64.decode4to3(buffer, 0, b4, 0, options); + out.write(b4, 0, len); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) + { + throw new java.io.IOException("Invalid character in Base64 data."); + } // end else: not white space either + } // end else: decoding + } // end write + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + @Override + public void write(byte[] theBytes, int off, int len) + throws java.io.IOException + { + // Encoding suspended? + if (suspendEncoding) + { + this.out.write(theBytes, off, len); + return; + } // end if: supsended + + for (int i = 0; i < len; i++) + { + write(theBytes[off + i]); + } // end for: each byte written + + } // end write + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + * + * @throws java.io.IOException if there's an error. + */ + public void flushBase64() throws java.io.IOException + { + if (position > 0) + { + if (encode) + { + out.write(encode3to4(b4, buffer, position, options)); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException("Base64 input not properly padded."); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + @Override + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base64-encoded data in a stream. + * + * @throws java.io.IOException if there's an error flushing + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base64-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + } // end inner class OutputStream + + +} // end class Base64 diff --git a/model/api/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java b/model/api/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java new file mode 100755 index 0000000000..a9fec22b37 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java @@ -0,0 +1,58 @@ +package org.keycloak.models.utils; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + + +/** + *

+ * Password that uses SHA to encode passwords. You can always change the SHA strength by specifying a valid + * integer when creating a new instance. + *

+ *

Passwords are returned with a Base64 encoding.

+ * + * @author Pedro Silva + * + */ +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/model/api/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java b/model/api/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java new file mode 100755 index 0000000000..d27718b276 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java @@ -0,0 +1,216 @@ +package org.keycloak.models.utils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.math.BigInteger; +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 { + + public static final String HMAC_SHA1 = "HmacSHA1"; + public static final String HMAC_SHA256 = "HmacSHA256"; + public static final String HMAC_SHA512 = "HmacSHA512"; + + public static final String DEFAULT_ALGORITHM = HMAC_SHA1; + public static final int DEFAULT_NUMBER_DIGITS = 6; + public static final int DEFAULT_INTERVAL_SECONDS = 30; + public static final int DEFAULT_DELAY_WINDOW = 1; + + // 0 1 2 3 4 5 6 7 8 + private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; + + private Clock clock; + private final String algorithm; + private final int numberDigits; + private final int delayWindow; + + 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 delayWindow the number of previous intervals that should be used to validate tokens. + */ + public TimeBasedOTP(String algorithm, int numberDigits, int timeIntervalInSeconds, int delayWindow) { + this.algorithm = algorithm; + this.numberDigits = numberDigits; + this.clock = new Clock(timeIntervalInSeconds); + this.delayWindow = delayWindow; + } + + /** + *

Generates a token.

+ * + * @param secretKey the secret key to derive the token from. + */ + public String generate(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 generateTOTP(secretKey, steps, this.numberDigits, this.algorithm); + } + + /** + * This method generates an TOTP value for the given set of parameters. + * + * @param key the shared secret, HEX encoded + * @param time a value that reflects a time + * @param returnDigits number of digits to return + * @param crypto the crypto function to use + * @return A numeric String in base 10 that includes {@link truncationDigits} digits + * @throws java.security.GeneralSecurityException + * + */ + public String generateTOTP(String key, String time, int returnDigits, String crypto) { + String result = null; + byte[] hash; + + // Using the counter + // First 8 bytes are for the movingFactor + // Complaint with base RFC 4226 (HOTP) + while (time.length() < 16) + time = "0" + time; + + // Get the HEX in a Byte[] + byte[] msg = hexStr2Bytes(time); + + // Adding one byte to get the right conversion + // byte[] k = hexStr2Bytes(key); + byte[] k = key.getBytes(); + + hash = hmac_sha1(crypto, k, msg); + + // put selected bytes into result int + int offset = hash[hash.length - 1] & 0xf; + + int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) + | (hash[offset + 3] & 0xff); + + int otp = binary % DIGITS_POWER[returnDigits]; + + result = Integer.toString(otp); + + while (result.length() < returnDigits) { + result = "0" + result; + } + return result; + } + + /** + *

Validates a token using a secret key.

+ * + * @param token OTP string to validate + * @param secret Shared secret + * @return + */ + public boolean validate(String token, byte[] secret) { + long currentInterval = this.clock.getCurrentInterval(); + + for (int i = this.delayWindow; 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 = generateTOTP(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); + } + + /** + * This method uses the JCE to provide the crypto algorithm. HMAC computes a Hashed Message Authentication Code with the + * crypto hash algorithm as a parameter. + * + * @param crypto the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512) + * @param keyBytes the bytes to use for the HMAC key + * @param text the message or text to be authenticated. + * @throws java.security.NoSuchAlgorithmException + * + * @throws java.security.InvalidKeyException + * + */ + private byte[] hmac_sha1(String crypto, byte[] keyBytes, byte[] text) { + byte[] value; + + try { + Mac hmac = Mac.getInstance(crypto); + SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW"); + + hmac.init(macKey); + + value = hmac.doFinal(text); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return value; + } + + /** + * This method converts HEX string to Byte[] + * + * @param hex the HEX string + * @return A byte array + */ + private byte[] hexStr2Bytes(String hex) { + // Adding one byte to get the right conversion + // values starting with "0" can be converted + byte[] bArray = new BigInteger("10" + hex, 16).toByteArray(); + + // Copy all the REAL bytes, not the "first" + byte[] ret = new byte[bArray.length - 1]; + for (int i = 0; i < ret.length; i++) + ret[i] = bArray[i + 1]; + return ret; + } + + 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/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java new file mode 100755 index 0000000000..a93b669deb --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java @@ -0,0 +1,275 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.jpa.entities.ApplicationEntity; +import org.keycloak.models.jpa.entities.ApplicationScopeMappingEntity; +import org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity; +import org.keycloak.models.jpa.entities.RoleEntity; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ApplicationAdapter implements ApplicationModel { + + protected EntityManager em; + protected ApplicationEntity application; + + public ApplicationAdapter(EntityManager em, ApplicationEntity application) { + this.em = em; + this.application = application; + } + + @Override + public void updateApplication() { + em.flush(); + } + + @Override + public UserModel getApplicationUser() { + return new UserAdapter(application.getApplicationUser()); + } + + @Override + public String getId() { + return application.getId(); + } + + @Override + public String getName() { + return application.getName(); + } + + @Override + public void setName(String name) { + application.setName(name); + } + + @Override + public boolean isEnabled() { + return application.isEnabled(); + } + + @Override + public void setEnabled(boolean enabled) { + application.setEnabled(enabled); + } + + @Override + public boolean isSurrogateAuthRequired() { + return application.isSurrogateAuthRequired(); + } + + @Override + public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { + application.setSurrogateAuthRequired(surrogateAuthRequired); + } + + @Override + public String getManagementUrl() { + return application.getManagementUrl(); + } + + @Override + public void setManagementUrl(String url) { + application.setManagementUrl(url); + } + + @Override + public String getBaseUrl() { + return null; + } + + @Override + public void setBaseUrl(String url) { + } + + @Override + public RoleModel getRole(String name) { + Collection roles = application.getRoles(); + if (roles == null) return null; + for (RoleEntity role : roles) { + if (role.getName().equals(name)) { + return new RoleAdapter(role); + } + } + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public RoleModel addRole(String name) { + RoleModel role = getRole(name); + if (role != null) return role; + RoleEntity entity = new RoleEntity(); + entity.setName(name); + em.persist(entity); + application.getRoles().add(entity); + em.flush(); + return new RoleAdapter(entity); + } + + @Override + public List getRoles() { + ArrayList list = new ArrayList(); + Collection roles = application.getRoles(); + if (roles == null) return list; + for (RoleEntity entity : roles) { + list.add(new RoleAdapter(entity)); + } + return list; + } + + @Override + public RoleModel getRoleById(String id) { + RoleEntity entity = em.find(RoleEntity.class, id); + if (entity == null) return null; + return new RoleAdapter(entity); + } + + @Override + public boolean hasRole(UserModel user, RoleModel role) { + TypedQuery query = getApplicationUserRoleMappingEntityTypedQuery((UserAdapter) user, (RoleAdapter) role); + return query.getResultList().size() > 0; + } + + protected TypedQuery getApplicationUserRoleMappingEntityTypedQuery(UserAdapter user, RoleAdapter role) { + TypedQuery query = em.createNamedQuery("userHasApplicationRole", ApplicationUserRoleMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("role", ((RoleAdapter)role).getRole()); + query.setParameter("application", application); + return query; + } + + @Override + public void grantRole(UserModel user, RoleModel role) { + if (hasRole(user, role)) return; + ApplicationUserRoleMappingEntity entity = new ApplicationUserRoleMappingEntity(); + entity.setApplication(application); + entity.setUser(((UserAdapter) user).getUser()); + entity.setRole(((RoleAdapter)role).getRole()); + em.persist(entity); + em.flush(); + } + + @Override + public List getRoleMappings(UserModel user) { + TypedQuery query = em.createNamedQuery("userApplicationMappings", ApplicationUserRoleMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("application", application); + List entities = query.getResultList(); + List roles = new ArrayList(); + for (ApplicationUserRoleMappingEntity entity : entities) { + roles.add(new RoleAdapter(entity.getRole())); + } + return roles; + } + + @Override + public Set getRoleMappingValues(UserModel user) { + TypedQuery query = em.createNamedQuery("userApplicationMappings", ApplicationUserRoleMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("application", application); + List entities = query.getResultList(); + Set roles = new HashSet(); + for (ApplicationUserRoleMappingEntity entity : entities) { + roles.add(entity.getRole().getName()); + } + return roles; + } + + @Override + public void deleteRoleMapping(UserModel user, RoleModel role) { + TypedQuery query = getApplicationUserRoleMappingEntityTypedQuery((UserAdapter) user, (RoleAdapter) role); + List results = query.getResultList(); + if (results.size() == 0) return; + for (ApplicationUserRoleMappingEntity entity : results) { + em.remove(entity); + } + } + + @Override + public boolean hasRole(UserModel user, String roleName) { + RoleModel role = getRole(roleName); + if (role == null) return false; + return hasRole(user, role); + } + + @Override + public void addScopeMapping(UserModel agent, String roleName) { + RoleModel role = getRole(roleName); + if (role == null) throw new RuntimeException("role does not exist"); + addScopeMapping(agent, role); + } + + @Override + public Set getScopeMappingValues(UserModel agent) { + TypedQuery query = em.createNamedQuery("userApplicationScopeMappings", ApplicationScopeMappingEntity.class); + query.setParameter("user", ((UserAdapter)agent).getUser()); + query.setParameter("application", application); + List entities = query.getResultList(); + Set roles = new HashSet(); + for (ApplicationScopeMappingEntity entity : entities) { + roles.add(entity.getRole().getName()); + } + return roles; + } + + @Override + public List getScopeMappings(UserModel agent) { + TypedQuery query = em.createNamedQuery("userApplicationScopeMappings", ApplicationScopeMappingEntity.class); + query.setParameter("user", ((UserAdapter)agent).getUser()); + query.setParameter("application", application); + List entities = query.getResultList(); + List roles = new ArrayList(); + for (ApplicationScopeMappingEntity entity : entities) { + roles.add(new RoleAdapter(entity.getRole())); + } + return roles; + } + + @Override + public void addScopeMapping(UserModel agent, RoleModel role) { + if (hasScope(agent, role)) return; + ApplicationScopeMappingEntity entity = new ApplicationScopeMappingEntity(); + entity.setApplication(application); + entity.setUser(((UserAdapter) agent).getUser()); + entity.setRole(((RoleAdapter)role).getRole()); + em.persist(entity); + em.flush(); + } + + @Override + public void deleteScopeMapping(UserModel user, RoleModel role) { + TypedQuery query = getApplicationScopeMappingQuery((UserAdapter) user, (RoleAdapter) role); + List results = query.getResultList(); + if (results.size() == 0) return; + for (ApplicationScopeMappingEntity entity : results) { + em.remove(entity); + } + } + + public boolean hasScope(UserModel user, RoleModel role) { + TypedQuery query = getApplicationScopeMappingQuery((UserAdapter) user, (RoleAdapter) role); + return query.getResultList().size() > 0; + } + + + protected TypedQuery getApplicationScopeMappingQuery(UserAdapter user, RoleAdapter role) { + TypedQuery query = em.createNamedQuery("userHasApplicationScope", ApplicationScopeMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("role", ((RoleAdapter)role).getRole()); + query.setParameter("application", application); + return query; + } + +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java new file mode 100755 index 0000000000..13d26d3138 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java @@ -0,0 +1,74 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakTransaction; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.jpa.entities.RealmEntity; +import org.keycloak.models.utils.KeycloakSessionUtils; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class JpaKeycloakSession implements KeycloakSession { + protected EntityManager em; + + public JpaKeycloakSession(EntityManager em) { + this.em = em; + } + + @Override + public KeycloakTransaction getTransaction() { + return new JpaKeycloakTransaction(em); + } + + @Override + public RealmModel createRealm(String name) { + return createRealm(KeycloakSessionUtils.generateId(), name); + } + + @Override + public RealmModel createRealm(String id, String name) { + RealmEntity realm = new RealmEntity(); + realm.setName(name); + realm.setId(id); + em.persist(realm); + em.flush(); + return new RealmAdapter(em, realm); + } + + @Override + public RealmModel getRealm(String id) { + RealmEntity realm = em.find(RealmEntity.class, id); + if (realm == null) return null; + return new RealmAdapter(em, realm); + } + + @Override + public List getRealms(UserModel admin) { + TypedQuery query = em.createQuery("select r from RealmEntity r", RealmEntity.class); + List entities = query.getResultList(); + List realms = new ArrayList(); + for (RealmEntity entity : entities) { + realms.add(new RealmAdapter(em, entity)); + } + return realms; + } + + @Override + public void deleteRealm(RealmModel realm) { + throw new RuntimeException("Not Implemented Yet"); + } + + @Override + public void close() { + if (em.getTransaction().isActive()) em.getTransaction().rollback(); + if (em.isOpen()) em.close(); + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSessionFactory.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSessionFactory.java new file mode 100755 index 0000000000..67ba543832 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSessionFactory.java @@ -0,0 +1,28 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + +import javax.persistence.EntityManagerFactory; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class JpaKeycloakSessionFactory implements KeycloakSessionFactory { + protected EntityManagerFactory factory; + + public JpaKeycloakSessionFactory(EntityManagerFactory factory) { + this.factory = factory; + } + + @Override + public KeycloakSession createSession() { + return new JpaKeycloakSession(factory.createEntityManager()); + } + + @Override + public void close() { + factory.close(); + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java new file mode 100755 index 0000000000..6e7f3f38cf --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java @@ -0,0 +1,48 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.KeycloakTransaction; + +import javax.persistence.EntityManager; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class JpaKeycloakTransaction implements KeycloakTransaction { + + protected EntityManager em; + + public JpaKeycloakTransaction(EntityManager em) { + this.em = em; + } + + @Override + public void begin() { + em.getTransaction().begin(); + } + + @Override + public void commit() { + em.getTransaction().commit(); + } + + @Override + public void rollback() { + em.getTransaction().rollback(); + } + + @Override + public void setRollbackOnly() { + em.getTransaction().setRollbackOnly(); + } + + @Override + public boolean getRollbackOnly() { + return em.getTransaction().getRollbackOnly(); + } + + @Override + public boolean isActive() { + return em.getTransaction().isActive(); + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java new file mode 100755 index 0000000000..1c5f743f56 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java @@ -0,0 +1,20 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.ModelProvider; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class JpaModelProvider implements ModelProvider { + @Override + public KeycloakSessionFactory createFactory() { + EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store"); + return new JpaKeycloakSessionFactory(emf); + + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java new file mode 100755 index 0000000000..fd2afcc487 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java @@ -0,0 +1,32 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.OAuthClientModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.jpa.entities.OAuthClientEntity; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class OAuthClientAdapter implements OAuthClientModel { + protected OAuthClientEntity entity; + + public OAuthClientAdapter(OAuthClientEntity entity) { + this.entity = entity; + } + + public OAuthClientEntity getEntity() { + return entity; + } + + @Override + public String getId() { + return entity.getId(); + } + + @Override + public UserModel getOAuthAgent() { + return new UserAdapter(entity.getAgent()); + } + +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java new file mode 100755 index 0000000000..658588cb31 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -0,0 +1,954 @@ +package org.keycloak.models.jpa; + +import org.bouncycastle.openssl.PEMWriter; +import org.keycloak.PemUtils; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.OAuthClientModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RequiredCredentialModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.SocialLinkModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.jpa.entities.ApplicationEntity; +import org.keycloak.models.jpa.entities.CredentialEntity; +import org.keycloak.models.jpa.entities.OAuthClientEntity; +import org.keycloak.models.jpa.entities.RealmEntity; +import org.keycloak.models.jpa.entities.RealmScopeMappingEntity; +import org.keycloak.models.jpa.entities.RealmUserRoleMappingEntity; +import org.keycloak.models.jpa.entities.RequiredCredentialEntity; +import org.keycloak.models.jpa.entities.RoleEntity; +import org.keycloak.models.jpa.entities.SocialLinkEntity; +import org.keycloak.models.jpa.entities.UserEntity; +import org.keycloak.models.utils.SHAPasswordEncoder; +import org.keycloak.models.utils.TimeBasedOTP; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import java.io.IOException; +import java.io.StringWriter; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RealmAdapter implements RealmModel { + protected RealmEntity realm; + protected EntityManager em; + protected volatile transient PublicKey publicKey; + protected volatile transient PrivateKey privateKey; + + public RealmAdapter(EntityManager em, RealmEntity realm) { + this.em = em; + this.realm = realm; + } + + @Override + public String getId() { + return realm.getId(); + } + + @Override + public String getName() { + return realm.getName(); + } + + @Override + public void setName(String name) { + realm.setName(name); + em.flush(); + } + + @Override + public boolean isEnabled() { + return realm.isEnabled(); + } + + @Override + public void setEnabled(boolean enabled) { + realm.setEnabled(enabled); + em.flush(); + } + + @Override + public boolean isSslNotRequired() { + return realm.isSslNotRequired(); + } + + @Override + public void setSslNotRequired(boolean sslNotRequired) { + realm.setSslNotRequired(sslNotRequired); + em.flush(); + } + + @Override + public boolean isCookieLoginAllowed() { + return realm.isCookieLoginAllowed(); + } + + @Override + public void setCookieLoginAllowed(boolean cookieLoginAllowed) { + realm.setCookieLoginAllowed(cookieLoginAllowed); + em.flush(); + } + + @Override + public boolean isRegistrationAllowed() { + return realm.isRegistrationAllowed(); + } + + @Override + public void setRegistrationAllowed(boolean registrationAllowed) { + realm.setRegistrationAllowed(registrationAllowed); + em.flush(); + } + + @Override + public boolean isVerifyEmail() { + return realm.isVerifyEmail(); + } + + @Override + public void setVerifyEmail(boolean verifyEmail) { + realm.setVerifyEmail(verifyEmail); + em.flush(); + } + + @Override + public boolean isResetPasswordAllowed() { + return realm.isResetPasswordAllowed(); + } + + @Override + public void setResetPasswordAllowed(boolean resetPasswordAllowed) { + realm.setResetPasswordAllowed(resetPasswordAllowed); + em.flush(); + } + + @Override + public int getTokenLifespan() { + return realm.getTokenLifespan(); + } + + @Override + public void setTokenLifespan(int tokenLifespan) { + realm.setTokenLifespan(tokenLifespan); + em.flush(); + } + + @Override + public int getAccessCodeLifespan() { + return realm.getAccessCodeLifespan(); + } + + @Override + public void setAccessCodeLifespan(int accessCodeLifespan) { + realm.setAccessCodeLifespan(accessCodeLifespan); + em.flush(); + } + + @Override + public int getAccessCodeLifespanUserAction() { + return realm.getAccessCodeLifespanUserAction(); + } + + @Override + public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) { + realm.setAccessCodeLifespanUserAction(accessCodeLifespanUserAction); + em.flush(); + } + + @Override + public String getPublicKeyPem() { + return realm.getPublicKeyPem(); + } + + @Override + public void setPublicKeyPem(String publicKeyPem) { + realm.setPublicKeyPem(publicKeyPem); + em.flush(); + } + + @Override + public String getPrivateKeyPem() { + return realm.getPrivateKeyPem(); + } + + @Override + public void setPrivateKeyPem(String privateKeyPem) { + realm.setPrivateKeyPem(privateKeyPem); + em.flush(); + } + + @Override + public PublicKey getPublicKey() { + if (publicKey != null) return publicKey; + String pem = getPublicKeyPem(); + if (pem != null) { + try { + publicKey = PemUtils.decodePublicKey(pem); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return publicKey; + } + + @Override + public void setPublicKey(PublicKey publicKey) { + this.publicKey = publicKey; + StringWriter writer = new StringWriter(); + PEMWriter pemWriter = new PEMWriter(writer); + try { + pemWriter.writeObject(publicKey); + pemWriter.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + String s = writer.toString(); + setPublicKeyPem(PemUtils.removeBeginEnd(s)); + } + + @Override + public PrivateKey getPrivateKey() { + if (privateKey != null) return privateKey; + String pem = getPrivateKeyPem(); + if (pem != null) { + try { + privateKey = PemUtils.decodePrivateKey(pem); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return privateKey; + } + + @Override + public void setPrivateKey(PrivateKey privateKey) { + this.privateKey = privateKey; + StringWriter writer = new StringWriter(); + PEMWriter pemWriter = new PEMWriter(writer); + try { + pemWriter.writeObject(privateKey); + pemWriter.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + String s = writer.toString(); + setPrivateKeyPem(PemUtils.removeBeginEnd(s)); + } + + protected RequiredCredentialModel initRequiredCredentialModel(String type) { + RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type); + if (model == null) { + throw new RuntimeException("Unknown credential type " + type); + } + return model; + } + + @Override + public void addRequiredCredential(String type) { + RequiredCredentialModel model = initRequiredCredentialModel(type); + addRequiredCredential(model); + em.flush(); + } + + public void addRequiredCredential(RequiredCredentialModel model) { + RequiredCredentialEntity entity = new RequiredCredentialEntity(); + entity.setInput(model.isInput()); + entity.setSecret(model.isSecret()); + entity.setType(model.getType()); + entity.setFormLabel(model.getFormLabel()); + em.persist(entity); + realm.getRequiredCredentials().add(entity); + em.flush(); + } + + @Override + public void updateRequiredCredentials(Set creds) { + Collection relationships = realm.getRequiredCredentials(); + if (relationships == null) relationships = new ArrayList(); + + Set already = new HashSet(); + List remove = new ArrayList(); + for (RequiredCredentialEntity rel : relationships) { + if (!creds.contains(rel.getType())) { + remove.add(rel); + } else { + already.add(rel.getType()); + } + } + for (RequiredCredentialEntity entity : remove) { + relationships.remove(entity); + em.remove(entity); + } + for (String cred : creds) { + if (!already.contains(cred)) { + addRequiredCredential(cred); + } + } + em.flush(); + } + + + @Override + public List getRequiredCredentials() { + List requiredCredentialModels = new ArrayList(); + Collection entities = realm.getRequiredCredentials(); + if (entities == null) return requiredCredentialModels; + for (RequiredCredentialEntity entity : entities) { + RequiredCredentialModel model = new RequiredCredentialModel(); + model.setFormLabel(entity.getFormLabel()); + model.setType(entity.getType()); + model.setSecret(entity.isSecret()); + model.setInput(entity.isInput()); + requiredCredentialModels.add(model); + } + return requiredCredentialModels; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List getRequiredApplicationCredentials() { + List requiredCredentialModels = new ArrayList(); + Collection entities = realm.getRequiredApplicationCredentials(); + if (entities == null) return requiredCredentialModels; + for (RequiredCredentialEntity entity : entities) { + RequiredCredentialModel model = new RequiredCredentialModel(); + model.setFormLabel(entity.getFormLabel()); + model.setType(entity.getType()); + model.setSecret(entity.isSecret()); + model.setInput(entity.isInput()); + requiredCredentialModels.add(model); + } + return requiredCredentialModels; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List getRequiredOAuthClientCredentials() { + List requiredCredentialModels = new ArrayList(); + Collection entities = realm.getRequiredOAuthClientCredentials(); + if (entities == null) return requiredCredentialModels; + for (RequiredCredentialEntity entity : entities) { + RequiredCredentialModel model = new RequiredCredentialModel(); + model.setFormLabel(entity.getFormLabel()); + model.setType(entity.getType()); + model.setSecret(entity.isSecret()); + model.setInput(entity.isInput()); + requiredCredentialModels.add(model); + } + return requiredCredentialModels; //To change body of implemented methods use File | Settings | File Templates. + } + + public void addRequiredOAuthClientCredential(RequiredCredentialModel model) { + RequiredCredentialEntity entity = new RequiredCredentialEntity(); + entity.setInput(model.isInput()); + entity.setSecret(model.isSecret()); + entity.setType(model.getType()); + entity.setFormLabel(model.getFormLabel()); + em.persist(entity); + realm.getRequiredOAuthClientCredentials().add(entity); + em.flush(); + } + + @Override + public void addRequiredOAuthClientCredential(String type) { + RequiredCredentialModel model = initRequiredCredentialModel(type); + addRequiredOAuthClientCredential(model); + em.flush(); + } + + public void addRequiredResourceCredential(RequiredCredentialModel model) { + RequiredCredentialEntity entity = new RequiredCredentialEntity(); + entity.setInput(model.isInput()); + entity.setSecret(model.isSecret()); + entity.setType(model.getType()); + entity.setFormLabel(model.getFormLabel()); + em.persist(entity); + realm.getRequiredApplicationCredentials().add(entity); + em.flush(); + } + + @Override + public void addRequiredResourceCredential(String type) { + RequiredCredentialModel model = initRequiredCredentialModel(type); + addRequiredResourceCredential(model); + em.flush(); + } + + @Override + public void updateRequiredOAuthClientCredentials(Set creds) { + Collection relationships = realm.getRequiredOAuthClientCredentials(); + if (relationships == null) relationships = new ArrayList(); + + Set already = new HashSet(); + List remove = new ArrayList(); + for (RequiredCredentialEntity rel : relationships) { + if (!creds.contains(rel.getType())) { + remove.add(rel); + } else { + already.add(rel.getType()); + } + } + for (RequiredCredentialEntity entity : remove) { + relationships.remove(entity); + em.remove(entity); + } + for (String cred : creds) { + if (!already.contains(cred)) { + addRequiredOAuthClientCredential(cred); + } + } + em.flush(); + } + + @Override + public void updateRequiredApplicationCredentials(Set creds) { + Collection relationships = realm.getRequiredApplicationCredentials(); + if (relationships == null) relationships = new ArrayList(); + + Set already = new HashSet(); + List remove = new ArrayList(); + for (RequiredCredentialEntity rel : relationships) { + if (!creds.contains(rel.getType())) { + remove.add(rel); + } else { + already.add(rel.getType()); + } + } + for (RequiredCredentialEntity entity : remove) { + relationships.remove(entity); + em.remove(entity); + } + for (String cred : creds) { + if (!already.contains(cred)) { + addRequiredResourceCredential(cred); + } + } + em.flush(); + } + + @Override + public UserModel getUser(String name) { + TypedQuery query = em.createNamedQuery("getRealmUserByLoginName", UserEntity.class); + query.setParameter("loginName", name); + query.setParameter("realm", realm); + List results = query.getResultList(); + if (results.size() == 0) return null; + return new UserAdapter(results.get(0)); + } + + @Override + public UserModel addUser(String username) { + UserEntity entity = new UserEntity(); + entity.setLoginName(username); + entity.setRealm(realm); + em.persist(entity); + em.flush(); + return new UserAdapter(entity); + } + + @Override + public List getDefaultRoles() { + Collection entities = realm.getDefaultRoles(); + List roles = new ArrayList(); + if (entities == null) return roles; + for (RoleEntity entity : entities) { + roles.add(new RoleAdapter(entity)); + } + return roles; + } + + @Override + public void addDefaultRole(String name) { + RoleModel role = getRole(name); + if (role == null) { + role = addRole(name); + } + Collection entities = realm.getDefaultRoles(); + for (RoleEntity entity : entities) { + if (entity.getId().equals(role.getId())) { + return; + } + } + entities.add(((RoleAdapter) role).getRole()); + em.flush(); + } + + public static boolean contains(String str, String[] array) { + for (String s : array) { + if (str.equals(s)) return true; + } + return false; + } + + @Override + public void updateDefaultRoles(String[] defaultRoles) { + Collection entities = realm.getDefaultRoles(); + Set already = new HashSet(); + List remove = new ArrayList(); + for (RoleEntity rel : entities) { + if (!contains(rel.getName(), defaultRoles)) { + remove.add(rel); + } else { + already.add(rel.getName()); + } + } + for (RoleEntity entity : remove) { + entities.remove(entity); + em.remove(entity); + } + for (String roleName : defaultRoles) { + if (!already.contains(roleName)) { + addDefaultRole(roleName); + } + } + em.flush(); + } + + @Override + public Map getApplicationNameMap() { + Map map = new HashMap(); + for (ApplicationModel app : getApplications()) { + map.put(app.getName(), app); + } + return map; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List getApplications() { + List list = new ArrayList(); + if (realm.getApplications() == null) return list; + for (ApplicationEntity entity : realm.getApplications()) { + list.add(new ApplicationAdapter(em, entity)); + } + return list; + } + + @Override + public ApplicationModel addApplication(String name) { + ApplicationEntity applicationData = new ApplicationEntity(); + UserEntity user = new UserEntity(); + user.setLoginName(name); + user.setRealm(realm); + user.setEnabled(true); + em.persist(user); + applicationData.setApplicationUser(user); + applicationData.setName(name); + realm.getApplications().add(applicationData); + em.persist(applicationData); + em.flush(); + ApplicationModel resource = new ApplicationAdapter(em, applicationData); + resource.addRole("*"); + resource.addScopeMapping(new UserAdapter(user), "*"); + em.flush(); + return resource; + } + + @Override + public ApplicationModel getApplicationById(String id) { + ApplicationEntity app = em.find(ApplicationEntity.class, id); + if (app == null) return null; + return new ApplicationAdapter(em, app); + } + + @Override + public UserModel getUserBySocialLink(SocialLinkModel socialLink) { + TypedQuery query = em.createNamedQuery("findUserByLinkAndRealm", UserEntity.class); + query.setParameter("realm", realm); + query.setParameter("socialProvider", socialLink.getSocialProvider()); + query.setParameter("socialUsername", socialLink.getSocialUsername()); + List results = query.getResultList(); + if (results.isEmpty()) { + return null; + } else if (results.size() > 1) { + throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() + + ", socialUsername=" + socialLink.getSocialUsername() + ", results=" + results); + } else { + UserEntity user = results.get(0); + return new UserAdapter(user); + } + } + + @Override + public Set getSocialLinks(UserModel user) { + TypedQuery query = em.createNamedQuery("findSocialLinkByUser", SocialLinkEntity.class); + query.setParameter("user", ((UserAdapter) user).getUser()); + List results = query.getResultList(); + Set set = new HashSet(); + for (SocialLinkEntity entity : results) { + set.add(new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUsername())); + } + return set; + } + + @Override + public void addSocialLink(UserModel user, SocialLinkModel socialLink) { + SocialLinkEntity entity = new SocialLinkEntity(); + entity.setRealm(realm); + entity.setSocialProvider(socialLink.getSocialProvider()); + entity.setSocialUsername(socialLink.getSocialUsername()); + entity.setUser(((UserAdapter) user).getUser()); + em.persist(entity); + em.flush(); + } + + @Override + public void removeSocialLink(UserModel user, SocialLinkModel socialLink) { + TypedQuery query = em.createNamedQuery("findSocialLinkByAll", SocialLinkEntity.class); + query.setParameter("realm", realm); + query.setParameter("user", ((UserAdapter) user).getUser()); + query.setParameter("socialProvider", socialLink.getSocialProvider()); + query.setParameter("socialUsername", socialLink.getSocialUsername()); + List results = query.getResultList(); + for (SocialLinkEntity entity : results) em.remove(entity); + em.flush(); + } + + @Override + public boolean isSocial() { + return realm.isSocial(); + } + + @Override + public void setSocial(boolean social) { + realm.setSocial(social); + em.flush(); + } + + @Override + public boolean isAutomaticRegistrationAfterSocialLogin() { + return realm.isAutomaticRegistrationAfterSocialLogin(); + } + + @Override + public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) { + realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin); + em.flush(); + } + + @Override + public List searchForUserByAttributes(Map attributes) { + StringBuilder builder = new StringBuilder("select u from UserEntity u"); + boolean first = true; + for (Map.Entry entry : attributes.entrySet()) { + String attribute = null; + if (entry.getKey().equals(UserModel.LOGIN_NAME)) { + attribute = "loginName"; + } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) { + attribute = "firstName"; + } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) { + attribute = "lastName"; + } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) { + attribute = "email"; + } + if (attribute == null) continue; + if (first) { + first = false; + builder.append(" where "); + } else { + builder.append(" and "); + } + builder.append(attribute).append("='").append(entry.getValue()).append("'"); + } + TypedQuery query = em.createQuery(builder.toString(), UserEntity.class); + List results = query.getResultList(); + List users = new ArrayList(); + for (UserEntity entity : results) users.add(new UserAdapter(entity)); + return users; + } + + @Override + public OAuthClientModel addOAuthClient(String name) { + OAuthClientEntity data = new OAuthClientEntity(); + UserEntity user = new UserEntity(); + user.setLoginName(name); + user.setRealm(realm); + user.setEnabled(true); + em.persist(user); + data.setAgent(user); + data.setName(name); + data.setRealm(realm); + em.persist(data); + em.flush(); + return new OAuthClientAdapter(data); + } + + @Override + public OAuthClientModel getOAuthClient(String name) { + TypedQuery query = em.createNamedQuery("findOAuthClientByUser", OAuthClientEntity.class); + query.setParameter("name", name); + query.setParameter("realm", realm); + List entities = query.getResultList(); + if (entities.size() == 0) return null; + return new OAuthClientAdapter(entities.get(0)); + } + + @Override + public List getOAuthClients() { + TypedQuery query = em.createNamedQuery("findOAuthClientByRealm", OAuthClientEntity.class); + query.setParameter("realm", realm); + List entities = query.getResultList(); + List list = new ArrayList(); + for (OAuthClientEntity entity : entities) list.add(new OAuthClientAdapter(entity)); + return list; + } + + @Override + public Map getSmtpConfig() { + return realm.getSmtpConfig(); + } + + @Override + public void setSmtpConfig(Map smtpConfig) { + realm.setSmtpConfig(smtpConfig); + em.flush(); + } + + @Override + public Map getSocialConfig() { + return realm.getSocialConfig(); + } + + @Override + public void setSocialConfig(Map socialConfig) { + realm.setSocialConfig(socialConfig); + em.flush(); + } + + @Override + public RoleModel getRole(String name) { + Collection roles = realm.getRoles(); + if (roles == null) return null; + for (RoleEntity role : roles) { + if (role.getName().equals(name)) { + return new RoleAdapter(role); + } + } + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public RoleModel addRole(String name) { + RoleModel role = getRole(name); + if (role != null) return role; + RoleEntity entity = new RoleEntity(); + entity.setName(name); + em.persist(entity); + realm.getRoles().add(entity); + em.flush(); + return new RoleAdapter(entity); + } + + @Override + public List getRoles() { + ArrayList list = new ArrayList(); + Collection roles = realm.getRoles(); + if (roles == null) return list; + for (RoleEntity entity : roles) { + list.add(new RoleAdapter(entity)); + } + return list; + } + + @Override + public RoleModel getRoleById(String id) { + RoleEntity entity = em.find(RoleEntity.class, id); + if (entity == null) return null; + return new RoleAdapter(entity); + } + + @Override + public boolean hasRole(UserModel user, RoleModel role) { + TypedQuery query = getRealmUserRoleMappingEntityTypedQuery((UserAdapter) user, (RoleAdapter) role); + return query.getResultList().size() > 0; + } + + protected TypedQuery getRealmUserRoleMappingEntityTypedQuery(UserAdapter user, RoleAdapter role) { + TypedQuery query = em.createNamedQuery("userHasRealmRole", RealmUserRoleMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("role", ((RoleAdapter)role).getRole()); + query.setParameter("realm", realm); + return query; + } + + @Override + public void grantRole(UserModel user, RoleModel role) { + if (hasRole(user, role)) return; + RealmUserRoleMappingEntity entity = new RealmUserRoleMappingEntity(); + entity.setRealm(realm); + entity.setUser(((UserAdapter) user).getUser()); + entity.setRole(((RoleAdapter)role).getRole()); + em.persist(entity); + em.flush(); + } + + @Override + public List getRoleMappings(UserModel user) { + TypedQuery query = em.createNamedQuery("userRealmMappings", RealmUserRoleMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("realm", realm); + List entities = query.getResultList(); + List roles = new ArrayList(); + for (RealmUserRoleMappingEntity entity : entities) { + roles.add(new RoleAdapter(entity.getRole())); + } + return roles; + } + + @Override + public Set getRoleMappingValues(UserModel user) { + TypedQuery query = em.createNamedQuery("userRealmMappings", RealmUserRoleMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("realm", realm); + List entities = query.getResultList(); + Set roles = new HashSet(); + for (RealmUserRoleMappingEntity entity : entities) { + roles.add(entity.getRole().getName()); + } + return roles; + } + + @Override + public void deleteRoleMapping(UserModel user, RoleModel role) { + TypedQuery query = getRealmUserRoleMappingEntityTypedQuery((UserAdapter) user, (RoleAdapter) role); + List results = query.getResultList(); + if (results.size() == 0) return; + for (RealmUserRoleMappingEntity entity : results) { + em.remove(entity); + } + em.flush(); + } + + @Override + public boolean hasRole(UserModel user, String roleName) { + RoleModel role = getRole(roleName); + if (role == null) return false; + return hasRole(user, role); + } + + @Override + public void addScopeMapping(UserModel agent, String roleName) { + RoleModel role = getRole(roleName); + if (role == null) throw new RuntimeException("role does not exist"); + addScopeMapping(agent, role); + em.flush(); + } + + @Override + public Set getScopeMappingValues(UserModel agent) { + TypedQuery query = em.createNamedQuery("userRealmScopeMappings", RealmScopeMappingEntity.class); + query.setParameter("user", ((UserAdapter)agent).getUser()); + query.setParameter("realm", realm); + List entities = query.getResultList(); + Set roles = new HashSet(); + for (RealmScopeMappingEntity entity : entities) { + roles.add(entity.getRole().getName()); + } + return roles; + } + + @Override + public List getScopeMappings(UserModel agent) { + TypedQuery query = em.createNamedQuery("userRealmScopeMappings", RealmScopeMappingEntity.class); + query.setParameter("user", ((UserAdapter)agent).getUser()); + query.setParameter("realm", realm); + List entities = query.getResultList(); + List roles = new ArrayList(); + for (RealmScopeMappingEntity entity : entities) { + roles.add(new RoleAdapter(entity.getRole())); + } + return roles; + } + + @Override + public void addScopeMapping(UserModel agent, RoleModel role) { + if (hasScope(agent, role)) return; + RealmScopeMappingEntity entity = new RealmScopeMappingEntity(); + entity.setRealm(realm); + entity.setUser(((UserAdapter) agent).getUser()); + entity.setRole(((RoleAdapter)role).getRole()); + em.persist(entity); + em.flush(); + } + + @Override + public void deleteScopeMapping(UserModel user, RoleModel role) { + TypedQuery query = getRealmScopeMappingQuery((UserAdapter) user, (RoleAdapter) role); + List results = query.getResultList(); + if (results.size() == 0) return; + for (RealmScopeMappingEntity entity : results) { + em.remove(entity); + } + } + + public boolean hasScope(UserModel user, RoleModel role) { + TypedQuery query = getRealmScopeMappingQuery((UserAdapter) user, (RoleAdapter) role); + return query.getResultList().size() > 0; + } + + + protected TypedQuery getRealmScopeMappingQuery(UserAdapter user, RoleAdapter role) { + TypedQuery query = em.createNamedQuery("userHasRealmScope", RealmScopeMappingEntity.class); + query.setParameter("user", ((UserAdapter)user).getUser()); + query.setParameter("role", ((RoleAdapter)role).getRole()); + query.setParameter("realm", realm); + return query; + } + + @Override + public boolean validatePassword(UserModel user, String password) { + for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) { + if (cred.getType().equals(UserCredentialModel.PASSWORD)) { + return new SHAPasswordEncoder(512).verify(password, cred.getValue()); + } + } + return false; + } + + @Override + public boolean validateTOTP(UserModel user, String password, String token) { + if (!validatePassword(user, password)) return false; + for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) { + if (cred.getType().equals(UserCredentialModel.TOTP)) { + return new TimeBasedOTP().validate(token, cred.getValue().getBytes()); + } + } + return false; + } + + @Override + public void updateCredential(UserModel user, UserCredentialModel cred) { + CredentialEntity credentialEntity = null; + UserEntity userEntity = ((UserAdapter) user).getUser(); + for (CredentialEntity entity : userEntity.getCredentials()) { + if (entity.getType().equals(cred.getType())) { + credentialEntity = entity; + } + } + if (credentialEntity == null) { + credentialEntity = new CredentialEntity(); + credentialEntity.setType(cred.getType()); + credentialEntity.setDevice(cred.getDevice()); + credentialEntity.setUser(userEntity); + em.persist(credentialEntity); + userEntity.getCredentials().add(credentialEntity); + } + if (cred.getType().equals(UserCredentialModel.PASSWORD)) { + credentialEntity.setValue(new SHAPasswordEncoder(512).encode(cred.getValue())); + } else { + credentialEntity.setValue(cred.getValue()); + } + credentialEntity.setDevice(cred.getDevice()); + em.flush(); + } + +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java new file mode 100755 index 0000000000..5785dec672 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java @@ -0,0 +1,49 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.RoleModel; +import org.keycloak.models.jpa.entities.RoleEntity; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RoleAdapter implements RoleModel { + protected RoleEntity role; + + public RoleAdapter(RoleEntity role) { + this.role = role; + } + + public RoleEntity getRole() { + return role; + } + + public void setRole(RoleEntity role) { + this.role = role; + } + + @Override + public String getName() { + return role.getName(); + } + + @Override + public String getDescription() { + return role.getDescription(); + } + + @Override + public void setDescription(String description) { + role.setDescription(description); + } + + @Override + public String getId() { + return role.getId(); + } + + @Override + public void setName(String name) { + role.setName(name); + } +} 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 new file mode 100755 index 0000000000..9f822d82ab --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -0,0 +1,185 @@ +package org.keycloak.models.jpa; + +import org.keycloak.models.UserModel; +import org.keycloak.models.jpa.entities.UserEntity; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class UserAdapter implements UserModel { + + protected UserEntity user; + + public UserAdapter(UserEntity user) { + this.user = user; + } + + public UserEntity getUser() { + return user; + } + + @Override + public String getLoginName() { + return user.getLoginName(); + } + + @Override + public boolean isEnabled() { + return user.isEnabled(); + } + + @Override + public boolean isTotp() { + return user.isTotp(); + } + + @Override + public void setEnabled(boolean enabled) { + user.setEnabled(enabled); + } + + @Override + public void setAttribute(String name, String value) { + Map attributes = user.getAttributes(); + if (attributes == null) { + attributes = new HashMap(); + } + attributes.put(name, value); + user.setAttributes(attributes); + } + + @Override + public void removeAttribute(String name) { + Map attributes = user.getAttributes(); + if (attributes == null) { + attributes = new HashMap(); + } + attributes.remove(name); + user.setAttributes(attributes); + } + + @Override + public String getAttribute(String name) { + if (user.getAttributes() == null) return null; + return user.getAttributes().get(name); + } + + @Override + public Map getAttributes() { + Map result = new HashMap(); + result.putAll(user.getAttributes()); + return result; + } + + @Override + public Set getRequiredActions() { + Set result = new HashSet(); + result.addAll(user.getRequiredActions()); + return result; + } + + @Override + public void addRequiredAction(RequiredAction action) { + user.getRequiredActions().add(action); + } + + @Override + public void removeRequiredAction(RequiredAction action) { + user.getRequiredActions().remove(action); + } + + @Override + public Set getWebOrigins() { + Set result = new HashSet(); + result.addAll(user.getWebOrigins()); + return result; + } + + @Override + public void setWebOrigins(Set webOrigins) { + user.setWebOrigins(webOrigins); + } + + @Override + public void addWebOrigin(String webOrigin) { + user.getWebOrigins().add(webOrigin); + } + + @Override + public void removeWebOrigin(String webOrigin) { + user.getWebOrigins().remove(webOrigin); + } + + @Override + public Set getRedirectUris() { + Set result = new HashSet(); + result.addAll(user.getRedirectUris()); + return result; + } + + @Override + public void setRedirectUris(Set redirectUris) { + user.setRedirectUris(redirectUris); + } + + @Override + public void addRedirectUri(String redirectUri) { + user.getRedirectUris().add(redirectUri); + } + + @Override + public void removeRedirectUri(String redirectUri) { + user.getRedirectUris().remove(redirectUri); + } + + @Override + public String getFirstName() { + return user.getFirstName(); + } + + @Override + public void setFirstName(String firstName) { + user.setFirstName(firstName); + } + + @Override + public String getLastName() { + return user.getLastName(); + } + + @Override + public void setLastName(String lastName) { + user.setLastName(lastName); + } + + @Override + public String getEmail() { + return user.getEmail(); + } + + @Override + public void setEmail(String email) { + user.setEmail(email); + } + + @Override + public boolean isEmailVerified() { + return user.isEmailVerified(); + } + + @Override + public void setEmailVerified(boolean verified) { + user.setEmailVerified(verified); + } + + @Override + public void setTotp(boolean totp) { + user.setTotp(totp); + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ResourceEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java similarity index 52% rename from model/jpa/src/main/java/org/keycloak/models/jpa/entities/ResourceEntity.java rename to model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java index dc32ed9fdb..a3ab9b7f63 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ResourceEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java @@ -1,6 +1,14 @@ package org.keycloak.models.jpa.entities; -import javax.persistence.*; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import java.util.ArrayList; import java.util.Collection; /** @@ -8,37 +16,27 @@ import java.util.Collection; * @version $Revision: 1 $ */ @Entity -public class ResourceEntity { +public class ApplicationEntity { @Id @GeneratedValue private String id; - private String resourceName; + private String name; private boolean enabled; private boolean surrogateAuthRequired; private String managementUrl; - @ManyToOne - private UserEntity resourceUser; - @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true) - Collection roles; + @OneToOne(fetch = FetchType.EAGER) + private UserEntity applicationUser; + + @OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true) + @JoinTable(name="APPLICATION_ROLES") + Collection roles = new ArrayList(); public String getId() { return id; } - public void setId(String id) { - this.id = id; - } - - public String getResourceName() { - return resourceName; - } - - public void setResourceName(String resourceName) { - this.resourceName = resourceName; - } - public boolean isEnabled() { return enabled; } @@ -63,12 +61,12 @@ public class ResourceEntity { this.managementUrl = managementUrl; } - public UserEntity getResourceUser() { - return resourceUser; + public UserEntity getApplicationUser() { + return applicationUser; } - public void setResourceUser(UserEntity resourceUser) { - this.resourceUser = resourceUser; + public void setApplicationUser(UserEntity applicationUser) { + this.applicationUser = applicationUser; } public Collection getRoles() { @@ -78,4 +76,12 @@ public class ResourceEntity { public void setRoles(Collection roles) { this.roles = roles; } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationScopeMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationScopeMappingEntity.java new file mode 100755 index 0000000000..557c65d012 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationScopeMappingEntity.java @@ -0,0 +1,29 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@NamedQueries({ + @NamedQuery(name="userHasApplicationScope", query="select m from ApplicationScopeMappingEntity m where m.user = :user and m.role = :role and m.application = :application"), + @NamedQuery(name="userApplicationScopeMappings", query="select m from ApplicationScopeMappingEntity m where m.user = :user and m.application = :application") +}) +@Entity +public class ApplicationScopeMappingEntity extends UserRoleMappingEntity { + + @ManyToOne + protected ApplicationEntity application; + + public ApplicationEntity getApplication() { + return application; + } + + public void setApplication(ApplicationEntity application) { + this.application = application; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationUserRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationUserRoleMappingEntity.java new file mode 100755 index 0000000000..2b2461d370 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationUserRoleMappingEntity.java @@ -0,0 +1,29 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@NamedQueries({ + @NamedQuery(name="userHasApplicationRole", query="select m from ApplicationUserRoleMappingEntity m where m.user = :user and m.role = :role and m.application = :application"), + @NamedQuery(name="userApplicationMappings", query="select m from ApplicationUserRoleMappingEntity m where m.user = :user and m.application = :application") +}) +@Entity +public class ApplicationUserRoleMappingEntity extends UserRoleMappingEntity { + + @ManyToOne + protected ApplicationEntity application; + + public ApplicationEntity getApplication() { + return application; + } + + public void setApplication(ApplicationEntity application) { + this.application = application; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java new file mode 100755 index 0000000000..4b13fd6c04 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java @@ -0,0 +1,69 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@NamedQueries({ + @NamedQuery(name="credentialByUserAndType", query="select cred from CredentialEntity cred where cred.user = :user and cred.type = :type") +}) +@Entity +public class CredentialEntity { + @Id + @GeneratedValue + protected String id; + + protected String type; + protected String value; + protected String device; + + @ManyToOne + protected UserEntity user; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java new file mode 100755 index 0000000000..28e158dc6d --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java @@ -0,0 +1,62 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToOne; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@NamedQueries({ + @NamedQuery(name="findOAuthClientByUser", query="select o from OAuthClientEntity o where o.agent.loginName=:name and o.realm = :realm"), + @NamedQuery(name="findOAuthClientByRealm", query="select o from OAuthClientEntity o where o.realm = :realm") + +}) +@Entity +public class OAuthClientEntity { + @Id + @GeneratedValue + private String id; + + private String name; + + @OneToOne(fetch = FetchType.EAGER) + private UserEntity agent; + + @ManyToOne + protected RealmEntity realm; + + public String getId() { + return id; + } + + public UserEntity getAgent() { + return agent; + } + + public void setAgent(UserEntity agent) { + this.agent = agent; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RealmEntity getRealm() { + return realm; + } + + public void setRealm(RealmEntity realm) { + this.realm = realm; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java index 4a8ea65949..d82c68e4b3 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java @@ -1,43 +1,272 @@ package org.keycloak.models.jpa.entities; -import javax.persistence.*; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.MapKeyColumn; +import javax.persistence.OneToMany; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Map; /** * @author Bill Burke * @version $Revision: 1 $ */ +@Entity public class RealmEntity { @Id protected String id; - protected String realmName; + protected String name; protected boolean enabled; protected boolean sslNotRequired; protected boolean cookieLoginAllowed; protected boolean registrationAllowed; + protected boolean verifyEmail; + protected boolean resetPasswordAllowed; + protected boolean social; + protected boolean automaticRegistrationAfterSocialLogin; + protected int tokenLifespan; protected int accessCodeLifespan; + protected int accessCodeLifespanUserAction; + @Column(length = 2048) protected String publicKeyPem; @Column(length = 2048) protected String privateKeyPem; - protected String[] defaultRoles; - @Lob - protected HashMap smtpConfig; - @Lob - protected HashMap socialConfig; @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true) - Collection requiredCredentials; + @JoinTable(name="USER_REQUIRED_CREDENTIALS") + Collection requiredCredentials = new ArrayList(); + @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true) - Collection resources; + @JoinTable(name="APPLICATION_REQUIRED_CREDENTIALS") + Collection requiredApplicationCredentials = new ArrayList(); + + @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true) + @JoinTable(name="OAUTH_CLIENT_REQUIRED_CREDENTIALS") + Collection requiredOAuthClientCredentials = new ArrayList(); + + @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true) + Collection applications = new ArrayList(); + @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true) - Collection roles; + @JoinTable(name="REALM_ROLES") + Collection roles = new ArrayList(); + @ElementCollection + @MapKeyColumn(name="name") + @Column(name="value") + @CollectionTable + protected Map smtpConfig = new HashMap(); + @ElementCollection + @MapKeyColumn(name="name") + @Column(name="value") + @CollectionTable + protected Map socialConfig = new HashMap(); + @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true) + @JoinTable(name="REALM_DEFAULT_ROLES") + Collection defaultRoles = new ArrayList(); + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isSslNotRequired() { + return sslNotRequired; + } + + public void setSslNotRequired(boolean sslNotRequired) { + this.sslNotRequired = sslNotRequired; + } + + public boolean isCookieLoginAllowed() { + return cookieLoginAllowed; + } + + public void setCookieLoginAllowed(boolean cookieLoginAllowed) { + this.cookieLoginAllowed = cookieLoginAllowed; + } + + public boolean isRegistrationAllowed() { + return registrationAllowed; + } + + public void setRegistrationAllowed(boolean registrationAllowed) { + this.registrationAllowed = registrationAllowed; + } + + public boolean isVerifyEmail() { + return verifyEmail; + } + + public void setVerifyEmail(boolean verifyEmail) { + this.verifyEmail = verifyEmail; + } + + public boolean isResetPasswordAllowed() { + return resetPasswordAllowed; + } + + public void setResetPasswordAllowed(boolean resetPasswordAllowed) { + this.resetPasswordAllowed = resetPasswordAllowed; + } + + public boolean isSocial() { + return social; + } + + public void setSocial(boolean social) { + this.social = social; + } + + public boolean isAutomaticRegistrationAfterSocialLogin() { + return automaticRegistrationAfterSocialLogin; + } + + public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) { + this.automaticRegistrationAfterSocialLogin = automaticRegistrationAfterSocialLogin; + } + + public int getTokenLifespan() { + return tokenLifespan; + } + + public void setTokenLifespan(int tokenLifespan) { + this.tokenLifespan = tokenLifespan; + } + + public int getAccessCodeLifespan() { + return accessCodeLifespan; + } + + public void setAccessCodeLifespan(int accessCodeLifespan) { + this.accessCodeLifespan = accessCodeLifespan; + } + + public int getAccessCodeLifespanUserAction() { + return accessCodeLifespanUserAction; + } + + public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) { + this.accessCodeLifespanUserAction = accessCodeLifespanUserAction; + } + + public String getPublicKeyPem() { + return publicKeyPem; + } + + public void setPublicKeyPem(String publicKeyPem) { + this.publicKeyPem = publicKeyPem; + } + + public String getPrivateKeyPem() { + return privateKeyPem; + } + + public void setPrivateKeyPem(String privateKeyPem) { + this.privateKeyPem = privateKeyPem; + } + + public Collection getRequiredCredentials() { + return requiredCredentials; + } + + public void setRequiredCredentials(Collection requiredCredentials) { + this.requiredCredentials = requiredCredentials; + } + + public Collection getRequiredApplicationCredentials() { + return requiredApplicationCredentials; + } + + public void setRequiredApplicationCredentials(Collection requiredApplicationCredentials) { + this.requiredApplicationCredentials = requiredApplicationCredentials; + } + + public Collection getRequiredOAuthClientCredentials() { + return requiredOAuthClientCredentials; + } + + public void setRequiredOAuthClientCredentials(Collection requiredOAuthClientCredentials) { + this.requiredOAuthClientCredentials = requiredOAuthClientCredentials; + } + + public Collection getApplications() { + return applications; + } + + public void setApplications(Collection applications) { + this.applications = applications; + } + + public Collection getRoles() { + return roles; + } + + public void setRoles(Collection roles) { + this.roles = roles; + } + + public void addRole(RoleEntity role) { + if (roles == null) { + roles = new ArrayList(); + } + roles.add(role); + } + + public Map getSmtpConfig() { + return smtpConfig; + } + + public void setSmtpConfig(Map smtpConfig) { + this.smtpConfig = smtpConfig; + } + + public Map getSocialConfig() { + return socialConfig; + } + + public void setSocialConfig(Map socialConfig) { + this.socialConfig = socialConfig; + } + + public Collection getDefaultRoles() { + return defaultRoles; + } + + public void setDefaultRoles(Collection defaultRoles) { + this.defaultRoles = defaultRoles; + } } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmScopeMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmScopeMappingEntity.java new file mode 100755 index 0000000000..5800e9fffe --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmScopeMappingEntity.java @@ -0,0 +1,29 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@NamedQueries({ + @NamedQuery(name="userHasRealmScope", query="select m from RealmScopeMappingEntity m where m.user = :user and m.role = :role and m.realm = :realm"), + @NamedQuery(name="userRealmScopeMappings", query="select m from RealmScopeMappingEntity m where m.user = :user and m.realm = :realm") +}) +@Entity +public class RealmScopeMappingEntity extends UserRoleMappingEntity { + + @ManyToOne + protected RealmEntity realm; + + public RealmEntity getRealm() { + return realm; + } + + public void setRealm(RealmEntity realm) { + this.realm = realm; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmUserRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmUserRoleMappingEntity.java new file mode 100755 index 0000000000..305ce8d7aa --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmUserRoleMappingEntity.java @@ -0,0 +1,29 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@NamedQueries({ + @NamedQuery(name="userHasRealmRole", query="select m from RealmUserRoleMappingEntity m where m.user = :user and m.role = :role and m.realm = :realm"), + @NamedQuery(name="userRealmMappings", query="select m from RealmUserRoleMappingEntity m where m.user = :user and m.realm = :realm") +}) +@Entity +public class RealmUserRoleMappingEntity extends UserRoleMappingEntity { + + @ManyToOne + protected RealmEntity realm; + + public RealmEntity getRealm() { + return realm; + } + + public void setRealm(RealmEntity realm) { + this.realm = realm; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentailEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentailEntity.java deleted file mode 100755 index b2509a249a..0000000000 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentailEntity.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.keycloak.models.jpa.entities; - -import javax.persistence.Entity; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -@Entity -public class RequiredCredentailEntity { -} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java new file mode 100755 index 0000000000..40c11c97d6 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java @@ -0,0 +1,61 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@Entity +public class RequiredCredentialEntity { + @Id + @GeneratedValue + protected String id; + + protected String type; + protected boolean input; + protected boolean secret; + protected String formLabel; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public boolean isInput() { + return input; + } + + public void setInput(boolean input) { + this.input = input; + } + + public boolean isSecret() { + return secret; + } + + public void setSecret(boolean secret) { + this.secret = secret; + } + + public String getFormLabel() { + return formLabel; + } + + public void setFormLabel(String formLabel) { + this.formLabel = formLabel; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java deleted file mode 100755 index 1ac8ed86b4..0000000000 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.keycloak.models.jpa.entities; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.ManyToOne; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -@Entity -public class ScopeMappingEntity { - @Id - @GeneratedValue - private long id; - - @ManyToOne - private UserEntity user; - @ManyToOne - private RoleEntity role; - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public UserEntity getUser() { - return user; - } - - public void setUser(UserEntity user) { - this.user = user; - } - - public RoleEntity getRole() { - return role; - } - - public void setRole(RoleEntity role) { - this.role = role; - } -} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java new file mode 100755 index 0000000000..87d75ee6cb --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java @@ -0,0 +1,73 @@ +package org.keycloak.models.jpa.entities; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@NamedQueries({ + @NamedQuery(name="findSocialLinkByUser", query="select link from SocialLinkEntity link where link.user = :user"), + @NamedQuery(name="findUserByLinkAndRealm", query="select link.user from SocialLinkEntity link where link.realm = :realm and link.socialProvider = :socialProvider and link.socialUsername = :socialUsername"), + @NamedQuery(name="findSocialLinkByAll", query="select link.user from SocialLinkEntity link where link.realm = :realm and link.socialProvider = :socialProvider and link.socialUsername = :socialUsername and link.user = :user") +}) +@Entity +public class SocialLinkEntity { + @Id + @GeneratedValue + private long id; + + @ManyToOne + private UserEntity user; + + @ManyToOne + protected RealmEntity realm; + + protected String socialProvider; + protected String socialUsername; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + } + + public String getSocialProvider() { + return socialProvider; + } + + public void setSocialProvider(String socialProvider) { + this.socialProvider = socialProvider; + } + + public String getSocialUsername() { + return socialUsername; + } + + public void setSocialUsername(String socialUsername) { + this.socialUsername = socialUsername; + } + + public RealmEntity getRealm() { + return realm; + } + + public void setRealm(RealmEntity realm) { + this.realm = realm; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java index 94bdf88ac9..6b8fc5a25a 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java @@ -1,20 +1,72 @@ package org.keycloak.models.jpa.entities; +import org.keycloak.models.UserModel; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * @author Bill Burke * @version $Revision: 1 $ */ +@NamedQueries({ + @NamedQuery(name="getRealmUserByLoginName", query="select u from UserEntity u where u.loginName = :loginName and u.realm = :realm"), + @NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realm = :realm"), + @NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realm = :realm"), + @NamedQuery(name="getRealmUserByFirstLastName", query="select u from UserEntity u where u.firstName = :first and u.lastName = :last and u.realm = :realm") +}) @Entity public class UserEntity { @Id @GeneratedValue - private String id; + protected String id; - private String loginName; + protected String loginName; + protected String firstName; + protected String lastName; + protected String email; + protected boolean enabled; + protected boolean totp; + protected boolean emailVerified; + + @ManyToOne + protected RealmEntity realm; + + @ElementCollection + @MapKeyColumn(name="name") + @Column(name="value") + @CollectionTable + protected Map attributes = new HashMap(); + + @ElementCollection + @CollectionTable + protected Set requiredActions = new HashSet(); + + @ElementCollection + @CollectionTable + protected Set webOrigins = new HashSet(); + + @ElementCollection + @CollectionTable + protected Set redirectUris = new HashSet(); + + @OneToMany + protected Collection credentials = new ArrayList(); public String getId() { return id; @@ -31,4 +83,100 @@ public class UserEntity { public void setLoginName(String loginName) { this.loginName = loginName; } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isTotp() { + return totp; + } + + public void setTotp(boolean totp) { + this.totp = totp; + } + + public boolean isEmailVerified() { + return emailVerified; + } + + public void setEmailVerified(boolean emailVerified) { + this.emailVerified = emailVerified; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public Set getRequiredActions() { + return requiredActions; + } + + public void setRequiredActions(Set requiredActions) { + this.requiredActions = requiredActions; + } + + public Set getWebOrigins() { + return webOrigins; + } + + public void setWebOrigins(Set webOrigins) { + this.webOrigins = webOrigins; + } + + public Set getRedirectUris() { + return redirectUris; + } + + public void setRedirectUris(Set redirectUris) { + this.redirectUris = redirectUris; + } + + public RealmEntity getRealm() { + return realm; + } + + public void setRealm(RealmEntity realm) { + this.realm = realm; + } + + public Collection getCredentials() { + return credentials; + } + + public void setCredentials(Collection credentials) { + this.credentials = credentials; + } } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java index 51099df255..2befc8d310 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java @@ -1,24 +1,23 @@ package org.keycloak.models.jpa.entities; -import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; /** * @author Bill Burke * @version $Revision: 1 $ */ -@Entity -public class UserRoleMappingEntity { +@MappedSuperclass +public abstract class UserRoleMappingEntity { @Id @GeneratedValue - private long id; - + protected long id; @ManyToOne - private UserEntity user; + protected UserEntity user; @ManyToOne - private RoleEntity role; + protected RoleEntity role; public long getId() { return id; diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/NoSQL.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/NoSQL.java old mode 100644 new mode 100755 index 3bc62a590f..0a63606e2d --- a/model/mongo/src/main/java/org/keycloak/models/mongo/api/NoSQL.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/NoSQL.java @@ -1,10 +1,9 @@ package org.keycloak.models.mongo.api; -import java.util.List; - import org.keycloak.models.mongo.api.query.NoSQLQuery; import org.keycloak.models.mongo.api.query.NoSQLQueryBuilder; -import org.picketlink.common.properties.Property; + +import java.util.List; /** * @author Marek Posolda diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java old mode 100644 new mode 100755 index a7c12c0cfb..e097930774 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java @@ -3,8 +3,6 @@ package org.keycloak.models.mongo.api.types; import java.util.HashMap; import java.util.Map; -import org.picketlink.common.reflection.Reflections; - /** * Registry of converters, which allow to convert application object to database objects. TypeConverter is main entry point to be used by application. * Application can create instance of TypeConverter and then register required Converter objects. diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBImpl.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBImpl.java old mode 100644 new mode 100755 index 09ca78e378..6bacedb288 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBImpl.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBImpl.java @@ -1,12 +1,5 @@ package org.keycloak.models.mongo.impl; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DB; @@ -24,10 +17,10 @@ import org.keycloak.models.mongo.api.query.NoSQLQuery; import org.keycloak.models.mongo.api.query.NoSQLQueryBuilder; import org.keycloak.models.mongo.api.types.Converter; import org.keycloak.models.mongo.api.types.TypeConverter; -import org.keycloak.models.mongo.impl.types.EnumToStringConverter; -import org.keycloak.models.mongo.impl.types.ListConverter; import org.keycloak.models.mongo.impl.types.BasicDBListConverter; import org.keycloak.models.mongo.impl.types.BasicDBObjectConverter; +import org.keycloak.models.mongo.impl.types.EnumToStringConverter; +import org.keycloak.models.mongo.impl.types.ListConverter; import org.keycloak.models.mongo.impl.types.NoSQLObjectConverter; import org.keycloak.models.mongo.impl.types.SimpleConverter; import org.keycloak.models.mongo.impl.types.StringToEnumConverter; @@ -35,6 +28,13 @@ import org.picketlink.common.properties.Property; import org.picketlink.common.properties.query.AnnotatedPropertyCriteria; import org.picketlink.common.properties.query.PropertyQueries; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBQueryBuilder.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBQueryBuilder.java old mode 100644 new mode 100755 index f56c799aea..2d1f61de6a --- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBQueryBuilder.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBQueryBuilder.java @@ -1,13 +1,13 @@ package org.keycloak.models.mongo.impl; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - import com.mongodb.BasicDBObject; import org.bson.types.ObjectId; import org.keycloak.models.mongo.api.query.NoSQLQueryBuilder; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java old mode 100644 new mode 100755 index ae548a607a..b511626d9d --- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java @@ -1,14 +1,14 @@ package org.keycloak.models.mongo.impl; +import org.keycloak.models.mongo.api.NoSQLObject; +import org.picketlink.common.properties.Property; + import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.keycloak.models.mongo.api.NoSQLObject; -import org.picketlink.common.properties.Property; - /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java old mode 100644 new mode 100755 index 896257fa8b..04824ba5cb --- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java @@ -1,12 +1,12 @@ package org.keycloak.models.mongo.impl.types; -import java.util.ArrayList; - import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import org.keycloak.models.mongo.api.types.Converter; import org.keycloak.models.mongo.api.types.TypeConverter; +import java.util.ArrayList; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java old mode 100644 new mode 100755 index 8b72ca223c..49fb627f7a --- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java @@ -1,12 +1,12 @@ package org.keycloak.models.mongo.impl.types; -import java.util.List; - import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import org.keycloak.models.mongo.api.types.Converter; import org.keycloak.models.mongo.api.types.TypeConverter; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/NoSQLObjectConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/NoSQLObjectConverter.java old mode 100644 new mode 100755 index f7be7aea33..35596a9671 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/NoSQLObjectConverter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/NoSQLObjectConverter.java @@ -1,8 +1,5 @@ package org.keycloak.models.mongo.impl.types; -import java.util.Collection; -import java.util.Map; - import com.mongodb.BasicDBObject; import org.keycloak.models.mongo.api.AttributedNoSQLObject; import org.keycloak.models.mongo.api.NoSQLObject; @@ -12,6 +9,9 @@ import org.keycloak.models.mongo.impl.MongoDBImpl; import org.keycloak.models.mongo.impl.ObjectInfo; import org.picketlink.common.properties.Property; +import java.util.Collection; +import java.util.Map; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java new file mode 100755 index 0000000000..87c62af751 --- /dev/null +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java @@ -0,0 +1,15 @@ +package org.keycloak.models.mongo.keycloak; + +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.ModelProvider; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class MongoModelProvider implements ModelProvider { + @Override + public KeycloakSessionFactory createFactory() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java old mode 100644 new mode 100755 index 49bcd31b88..f145933c14 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java @@ -1,10 +1,5 @@ package org.keycloak.models.mongo.keycloak.adapters; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.keycloak.models.ApplicationModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; @@ -14,6 +9,11 @@ import org.keycloak.models.mongo.keycloak.data.ApplicationData; import org.keycloak.models.mongo.keycloak.data.RoleData; import org.keycloak.models.mongo.keycloak.data.UserData; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoDBSessionFactory.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoDBSessionFactory.java old mode 100644 new mode 100755 index b1ac5093ec..f964605cb8 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoDBSessionFactory.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoDBSessionFactory.java @@ -1,7 +1,5 @@ package org.keycloak.models.mongo.keycloak.adapters; -import java.net.UnknownHostException; - import com.mongodb.DB; import com.mongodb.MongoClient; import org.jboss.logging.Logger; @@ -9,6 +7,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.mongo.api.NoSQLObject; +import org.keycloak.models.mongo.impl.MongoDBImpl; import org.keycloak.models.mongo.keycloak.data.ApplicationData; import org.keycloak.models.mongo.keycloak.data.OAuthClientData; import org.keycloak.models.mongo.keycloak.data.RealmData; @@ -16,10 +15,11 @@ import org.keycloak.models.mongo.keycloak.data.RequiredCredentialData; import org.keycloak.models.mongo.keycloak.data.RoleData; import org.keycloak.models.mongo.keycloak.data.SocialLinkData; import org.keycloak.models.mongo.keycloak.data.UserData; -import org.keycloak.models.mongo.impl.MongoDBImpl; import org.keycloak.models.mongo.keycloak.data.credentials.OTPData; import org.keycloak.models.mongo.keycloak.data.credentials.PasswordData; +import java.net.UnknownHostException; + /** * NoSQL implementation based on MongoDB * diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/NoSQLSession.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/NoSQLSession.java old mode 100644 new mode 100755 index 2bc413de5f..bbd5ea4e61 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/NoSQLSession.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/NoSQLSession.java @@ -1,17 +1,17 @@ package org.keycloak.models.mongo.keycloak.adapters; -import java.util.ArrayList; -import java.util.List; - import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.mongo.api.query.NoSQLQuery; import org.keycloak.models.mongo.keycloak.data.RealmData; -import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.utils.KeycloakSessionUtils; +import java.util.ArrayList; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java old mode 100644 new mode 100755 index 34f455eb39..d522db9b9c --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java @@ -41,14 +41,4 @@ public class OAuthClientAdapter implements OAuthClientModel { return oauthAgent; } - @Override - public String getBaseUrl() { - return delegate.getBaseUrl(); - } - - @Override - public void setBaseUrl(String base) { - delegate.setBaseUrl(base); - noSQL.saveObject(delegate); - } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java old mode 100644 new mode 100755 index 837f985624..391334a8c5 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -1,16 +1,5 @@ package org.keycloak.models.mongo.keycloak.adapters; -import java.io.IOException; -import java.io.StringWriter; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.bouncycastle.openssl.PEMWriter; import org.keycloak.PemUtils; import org.keycloak.models.ApplicationModel; @@ -21,21 +10,31 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.SocialLinkModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; -import org.keycloak.models.mongo.api.query.NoSQLQueryBuilder; -import org.keycloak.models.mongo.keycloak.data.OAuthClientData; -import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.mongo.api.query.NoSQLQuery; +import org.keycloak.models.mongo.api.query.NoSQLQueryBuilder; import org.keycloak.models.mongo.keycloak.credentials.PasswordCredentialHandler; import org.keycloak.models.mongo.keycloak.credentials.TOTPCredentialHandler; import org.keycloak.models.mongo.keycloak.data.ApplicationData; +import org.keycloak.models.mongo.keycloak.data.OAuthClientData; import org.keycloak.models.mongo.keycloak.data.RealmData; import org.keycloak.models.mongo.keycloak.data.RequiredCredentialData; import org.keycloak.models.mongo.keycloak.data.RoleData; import org.keycloak.models.mongo.keycloak.data.SocialLinkData; import org.keycloak.models.mongo.keycloak.data.UserData; +import org.keycloak.representations.idm.CredentialRepresentation; import org.picketlink.idm.credential.Credentials; -import org.picketlink.idm.model.sample.User; + +import java.io.IOException; +import java.io.StringWriter; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author Marek Posolda @@ -596,20 +595,6 @@ public class RealmAdapter implements RealmModel { return result; } - @Override - public boolean isRealmAdmin(UserModel agent) { - List realmAdmins = realm.getRealmAdmins(); - String userId = ((UserAdapter)agent).getUser().getId(); - return realmAdmins.contains(userId); - } - - @Override - public void addRealmAdmin(UserModel agent) { - UserData userData = ((UserAdapter)agent).getUser(); - - noSQL.pushItemToList(realm, "realmAdmins", userData.getId()); - } - @Override public RoleModel getRoleById(String id) { RoleData role = noSQL.loadObject(RoleData.class, id); @@ -859,4 +844,24 @@ public class RealmAdapter implements RealmModel { } return userModels; } + + @Override + public Map getSmtpConfig() { + throw new RuntimeException("Not implemented"); + } + + @Override + public void setSmtpConfig(Map smtpConfig) { + throw new RuntimeException("Not implemented"); + } + + @Override + public Map getSocialConfig() { + throw new RuntimeException("Not implemented"); + } + + @Override + public void setSocialConfig(Map socialConfig) { + throw new RuntimeException("Not implemented"); + } } 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 old mode 100644 new mode 100755 index c047361f31..3b208484bc --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java @@ -1,15 +1,15 @@ package org.keycloak.models.mongo.keycloak.adapters; +import org.keycloak.models.UserModel; +import org.keycloak.models.mongo.api.NoSQL; +import org.keycloak.models.mongo.keycloak.data.UserData; + import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.keycloak.models.UserModel; -import org.keycloak.models.mongo.api.NoSQL; -import org.keycloak.models.mongo.keycloak.data.UserData; - /** * Wrapper around UserData object, which will persist wrapped object after each set operation (compatibility with picketlink based impl) * @@ -149,4 +149,44 @@ public class UserAdapter implements UserModel { user.setTotp(totp); noSQL.saveObject(user); } + + @Override + public Set getWebOrigins() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void setWebOrigins(Set webOrigins) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void addWebOrigin(String webOrigin) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void removeWebOrigin(String webOrigin) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Set getRedirectUris() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void setRedirectUris(Set redirectUris) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void addRedirectUri(String redirectUri) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void removeRedirectUri(String redirectUri) { + //To change body of implemented methods use File | Settings | File Templates. + } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/PasswordCredentialHandler.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/PasswordCredentialHandler.java old mode 100644 new mode 100755 index 719760a0f5..b6a3eb1171 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/PasswordCredentialHandler.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/PasswordCredentialHandler.java @@ -1,9 +1,5 @@ package org.keycloak.models.mongo.keycloak.credentials; -import java.util.Date; -import java.util.Map; -import java.util.UUID; - import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.mongo.api.query.NoSQLQuery; import org.keycloak.models.mongo.keycloak.data.UserData; @@ -12,6 +8,10 @@ import org.picketlink.idm.credential.Credentials; import org.picketlink.idm.credential.encoder.PasswordEncoder; import org.picketlink.idm.credential.encoder.SHAPasswordEncoder; +import java.util.Date; +import java.util.Map; +import java.util.UUID; + /** * Defacto forked from {@link org.picketlink.idm.credential.handler.PasswordCredentialHandler} * diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/TOTPCredentialHandler.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/TOTPCredentialHandler.java old mode 100644 new mode 100755 index b8f02e7d6e..45a76f757c --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/TOTPCredentialHandler.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/TOTPCredentialHandler.java @@ -1,8 +1,5 @@ package org.keycloak.models.mongo.keycloak.credentials; -import java.util.Date; -import java.util.Map; - import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.mongo.api.query.NoSQLQuery; import org.keycloak.models.mongo.keycloak.data.UserData; @@ -10,11 +7,11 @@ import org.keycloak.models.mongo.keycloak.data.credentials.OTPData; import org.picketlink.idm.credential.Credentials; import org.picketlink.idm.credential.util.TimeBasedOTP; +import java.util.Date; +import java.util.Map; + import static org.picketlink.common.util.StringUtil.isNullOrEmpty; -import static org.picketlink.idm.credential.util.TimeBasedOTP.DEFAULT_ALGORITHM; -import static org.picketlink.idm.credential.util.TimeBasedOTP.DEFAULT_DELAY_WINDOW; -import static org.picketlink.idm.credential.util.TimeBasedOTP.DEFAULT_INTERVAL_SECONDS; -import static org.picketlink.idm.credential.util.TimeBasedOTP.DEFAULT_NUMBER_DIGITS; +import static org.picketlink.idm.credential.util.TimeBasedOTP.*; /** * Defacto forked from {@link org.picketlink.idm.credential.handler.TOTPCredentialHandler} diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RealmData.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RealmData.java old mode 100644 new mode 100755 index 5247d60034..d9aa0ae59d --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RealmData.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RealmData.java @@ -1,7 +1,5 @@ package org.keycloak.models.mongo.keycloak.data; -import java.util.List; - import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.mongo.api.NoSQLCollection; import org.keycloak.models.mongo.api.NoSQLField; @@ -9,6 +7,8 @@ import org.keycloak.models.mongo.api.NoSQLId; import org.keycloak.models.mongo.api.NoSQLObject; import org.keycloak.models.mongo.api.query.NoSQLQuery; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RoleData.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RoleData.java old mode 100644 new mode 100755 index 29bc1f8930..9bd14d23f1 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RoleData.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RoleData.java @@ -1,7 +1,5 @@ package org.keycloak.models.mongo.keycloak.data; -import java.util.List; - import org.jboss.logging.Logger; import org.keycloak.models.mongo.api.NoSQL; import org.keycloak.models.mongo.api.NoSQLCollection; @@ -10,6 +8,8 @@ import org.keycloak.models.mongo.api.NoSQLId; import org.keycloak.models.mongo.api.NoSQLObject; import org.keycloak.models.mongo.api.query.NoSQLQuery; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/UserData.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/UserData.java old mode 100644 new mode 100755 index cfeb67d6d1..c57bca38e9 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/UserData.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/UserData.java @@ -1,7 +1,5 @@ package org.keycloak.models.mongo.keycloak.data; -import java.util.List; - import org.jboss.logging.Logger; import org.keycloak.models.UserModel; import org.keycloak.models.mongo.api.AbstractAttributedNoSQLObject; @@ -12,6 +10,8 @@ import org.keycloak.models.mongo.api.NoSQLId; import org.keycloak.models.mongo.api.query.NoSQLQuery; import org.keycloak.models.mongo.keycloak.data.credentials.PasswordData; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/OTPData.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/OTPData.java old mode 100644 new mode 100755 index 8ab31a65fc..6983f83c3a --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/OTPData.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/OTPData.java @@ -1,11 +1,11 @@ package org.keycloak.models.mongo.keycloak.data.credentials; -import java.util.Date; - import org.keycloak.models.mongo.api.AbstractNoSQLObject; import org.keycloak.models.mongo.api.NoSQLCollection; import org.keycloak.models.mongo.api.NoSQLField; +import java.util.Date; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/PasswordData.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/PasswordData.java old mode 100644 new mode 100755 index 7480e1fb87..6ac585f58c --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/PasswordData.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/PasswordData.java @@ -1,11 +1,11 @@ package org.keycloak.models.mongo.keycloak.data.credentials; -import java.util.Date; - import org.keycloak.models.mongo.api.AbstractNoSQLObject; import org.keycloak.models.mongo.api.NoSQLCollection; import org.keycloak.models.mongo.api.NoSQLField; +import java.util.Date; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java old mode 100644 new mode 100755 index 8f6b6f885e..386ca31127 --- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java +++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java @@ -1,10 +1,10 @@ package org.keycloak.models.mongo.test; -import java.util.List; - import org.keycloak.models.mongo.api.AbstractNoSQLObject; import org.keycloak.models.mongo.api.NoSQLField; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java old mode 100644 new mode 100755 index 262ade855b..3b8651a254 --- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java +++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java @@ -1,10 +1,5 @@ package org.keycloak.models.mongo.test; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import com.mongodb.DB; import com.mongodb.MongoClient; import org.junit.After; @@ -15,6 +10,11 @@ import org.keycloak.models.mongo.api.NoSQLObject; import org.keycloak.models.mongo.api.query.NoSQLQuery; import org.keycloak.models.mongo.impl.MongoDBImpl; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java old mode 100644 new mode 100755 index ab2ded3844..7f8d0d9c24 --- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java +++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java @@ -1,12 +1,12 @@ package org.keycloak.models.mongo.test; -import java.util.List; - import org.keycloak.models.mongo.api.AbstractNoSQLObject; import org.keycloak.models.mongo.api.NoSQLCollection; import org.keycloak.models.mongo.api.NoSQLField; import org.keycloak.models.mongo.api.NoSQLId; +import java.util.List; + /** * @author Marek Posolda */ diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/OAuthClientAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/OAuthClientAdapter.java index 310b4ce49a..474a0b54d4 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/OAuthClientAdapter.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/OAuthClientAdapter.java @@ -31,14 +31,4 @@ public class OAuthClientAdapter implements OAuthClientModel { return new UserAdapter(delegate.getOauthAgent(), idm); } - @Override - public String getBaseUrl() { - return delegate.getBaseUrl(); - } - - @Override - public void setBaseUrl(String base) { - delegate.setBaseUrl(base); - relationshipManager.update(delegate); - } } diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/PicketlinkKeycloakSession.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/PicketlinkKeycloakSession.java index 7d1733986c..fcaf72e2ed 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/PicketlinkKeycloakSession.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/PicketlinkKeycloakSession.java @@ -5,7 +5,6 @@ import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.picketlink.mappings.RealmData; -import org.keycloak.models.picketlink.relationships.RealmAdminRelationship; import org.keycloak.models.picketlink.relationships.RealmListingRelationship; import org.keycloak.models.utils.KeycloakSessionUtils; import org.picketlink.idm.PartitionManager; @@ -15,7 +14,6 @@ import org.picketlink.idm.query.RelationshipQuery; import javax.persistence.EntityManager; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicLong; /** * @author Bill Burke diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/PicketlinkModelProvider.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/PicketlinkModelProvider.java new file mode 100755 index 0000000000..67f4d7a15f --- /dev/null +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/PicketlinkModelProvider.java @@ -0,0 +1,77 @@ +package org.keycloak.models.picketlink; + +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.ModelProvider; +import org.keycloak.models.picketlink.mappings.ApplicationEntity; +import org.keycloak.models.picketlink.mappings.RealmEntity; +import org.picketlink.idm.PartitionManager; +import org.picketlink.idm.config.IdentityConfigurationBuilder; +import org.picketlink.idm.internal.DefaultPartitionManager; +import org.picketlink.idm.jpa.internal.JPAContextInitializer; +import org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity; +import org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PicketlinkModelProvider implements ModelProvider { + @Override + public KeycloakSessionFactory createFactory() { + EntityManagerFactory emf = Persistence.createEntityManagerFactory("picketlink-keycloak-identity-store"); + return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager()); + } + + public static PartitionManager buildPartitionManager() { + IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); + + builder + .named("KEYCLOAK_JPA_CONFIG") + .stores() + .jpa() + .mappedEntity( + AttributedTypeEntity.class, + AccountTypeEntity.class, + RoleTypeEntity.class, + GroupTypeEntity.class, + IdentityTypeEntity.class, + RelationshipTypeEntity.class, + RelationshipIdentityTypeEntity.class, + PartitionTypeEntity.class, + PasswordCredentialTypeEntity.class, + DigestCredentialTypeEntity.class, + X509CredentialTypeEntity.class, + OTPCredentialTypeEntity.class, + AttributeTypeEntity.class, + RealmEntity.class, + ApplicationEntity.class + ) + .supportGlobalRelationship(org.picketlink.idm.model.Relationship.class) + .addContextInitializer(new JPAContextInitializer(null) { + @Override + public EntityManager getEntityManager() { + return PicketlinkKeycloakSession.currentEntityManager.get(); + } + }) + .supportAllFeatures(); + + DefaultPartitionManager partitionManager = new DefaultPartitionManager(builder.buildAll()); + return partitionManager; + } + +} diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java index d3268d2fa9..ed7116590d 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java @@ -2,11 +2,25 @@ package org.keycloak.models.picketlink; import org.bouncycastle.openssl.PEMWriter; import org.keycloak.PemUtils; -import org.keycloak.models.*; -import org.keycloak.models.picketlink.mappings.RealmData; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.IdGenerator; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.OAuthClientModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RequiredCredentialModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.SocialLinkModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; import org.keycloak.models.picketlink.mappings.ApplicationData; -import org.keycloak.models.picketlink.relationships.*; +import org.keycloak.models.picketlink.mappings.RealmData; +import org.keycloak.models.picketlink.relationships.ApplicationRelationship; +import org.keycloak.models.picketlink.relationships.OAuthClientRelationship; +import org.keycloak.models.picketlink.relationships.OAuthClientRequiredCredentialRelationship; import org.keycloak.models.picketlink.relationships.RequiredApplicationCredentialRelationship; +import org.keycloak.models.picketlink.relationships.RequiredCredentialRelationship; +import org.keycloak.models.picketlink.relationships.ScopeRelationship; +import org.keycloak.models.picketlink.relationships.SocialLinkRelationship; import org.picketlink.idm.IdentityManager; import org.picketlink.idm.PartitionManager; import org.picketlink.idm.RelationshipManager; @@ -601,7 +615,6 @@ public class RealmAdapter implements RealmModel { idm.add(resourceUser); applicationData.setResourceUser(resourceUser); applicationData.setResourceName(name); - applicationData.setResourceUser(resourceUser); partitionManager.add(applicationData); ApplicationRelationship resourceRelationship = new ApplicationRelationship(); resourceRelationship.setRealm(realm.getName()); @@ -875,23 +888,23 @@ public class RealmAdapter implements RealmModel { } @Override - public HashMap getSmtpConfig() { + public Map getSmtpConfig() { return realm.getSmtpConfig(); } @Override - public void setSmtpConfig(HashMap smtpConfig) { + public void setSmtpConfig(Map smtpConfig) { realm.setSmtpConfig(smtpConfig); updateRealm(); } @Override - public HashMap getSocialConfig() { + public Map getSocialConfig() { return realm.getSocialConfig(); } @Override - public void setSocialConfig(HashMap socialConfig) { + public void setSocialConfig(Map socialConfig) { realm.setSocialConfig(socialConfig); updateRealm(); } diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/UserAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/UserAdapter.java index e44c92ac17..2555f74d5b 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/UserAdapter.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/UserAdapter.java @@ -1,17 +1,16 @@ package org.keycloak.models.picketlink; -import java.io.Serializable; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - import org.keycloak.models.UserModel; import org.picketlink.idm.IdentityManager; import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.sample.User; +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + /** * @author Bill Burke * @version $Revision: 1 $ diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java index 24e1f03ca7..06e073629b 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java @@ -3,8 +3,7 @@ package org.keycloak.models.picketlink.mappings; import org.picketlink.idm.model.AbstractPartition; import org.picketlink.idm.model.annotation.AttributeProperty; -import java.io.Serializable; -import java.util.HashMap; +import java.util.Map; /** * @author Bill Burke @@ -26,8 +25,8 @@ public class RealmData extends AbstractPartition { private String publicKeyPem; private String privateKeyPem; private String[] defaultRoles; - private HashMap smtpConfig; - private HashMap socialConfig; + private Map smtpConfig; + private Map socialConfig; public RealmData() { super(null); @@ -170,20 +169,20 @@ public class RealmData extends AbstractPartition { } @AttributeProperty - public HashMap getSmtpConfig() { + public Map getSmtpConfig() { return smtpConfig; } - public void setSmtpConfig(HashMap smtpConfig) { + public void setSmtpConfig(Map smtpConfig) { this.smtpConfig = smtpConfig; } @AttributeProperty - public HashMap getSocialConfig() { + public Map getSocialConfig() { return socialConfig; } - public void setSocialConfig(HashMap socialConfig) { + public void setSocialConfig(Map socialConfig) { this.socialConfig = socialConfig; } } diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java index 74a3f09e6d..4be31e64a0 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java @@ -5,7 +5,11 @@ import org.picketlink.idm.jpa.annotations.OwnerReference; import org.picketlink.idm.jpa.annotations.entity.IdentityManaged; import org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.OneToOne; import java.io.Serializable; import java.util.HashMap; diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/RealmListingRelationship.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/RealmListingRelationship.java index 26636e6b34..bb4856a695 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/RealmListingRelationship.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/RealmListingRelationship.java @@ -3,9 +3,7 @@ package org.keycloak.models.picketlink.relationships; import org.picketlink.idm.model.AbstractAttributedType; import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.Relationship; -import org.picketlink.idm.model.sample.User; import org.picketlink.idm.query.AttributeParameter; -import org.picketlink.idm.query.RelationshipQueryParameter; /** * Picketlink doesn't allow you to query for all partitions, thus this stupid relationship... diff --git a/model/pom.xml b/model/pom.xml index 7e2fca5b9b..3d0b0dc31b 100755 --- a/model/pom.xml +++ b/model/pom.xml @@ -37,6 +37,6 @@ api picketlink jpa - + mongo diff --git a/pom.xml b/pom.xml index da29152c6a..3b446c8ace 100755 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ pom - 3.0.4.Final + 3.0.5.Final 1.0.0.Beta12 2.5.0.Beta6 2.11.2 diff --git a/services/pom.xml b/services/pom.xml index 52d2ab85e4..1df957c261 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -33,6 +33,12 @@ org.keycloak keycloak-model-picketlink ${project.version} + provided +
+ + org.keycloak + keycloak-model-jpa + ${project.version} log4j log4j diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java index cceefaa356..d7782233f9 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java @@ -23,31 +23,41 @@ package org.keycloak.testutils; import io.undertow.Undertow; import io.undertow.Undertow.Builder; -import io.undertow.server.handlers.resource.*; +import io.undertow.server.handlers.resource.FileResource; +import io.undertow.server.handlers.resource.FileResourceManager; +import io.undertow.server.handlers.resource.Resource; +import io.undertow.server.handlers.resource.ResourceManager; +import io.undertow.server.handlers.resource.URLResource; import io.undertow.servlet.Servlets; import io.undertow.servlet.api.DefaultServletConfig; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.FilterInfo; - -import java.io.*; -import java.net.URL; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import javax.servlet.DispatcherType; - import org.jboss.resteasy.jwt.JsonSerialization; import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer; import org.jboss.resteasy.spi.ResteasyDeployment; -import org.keycloak.models.*; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.filters.KeycloakSessionServletFilter; import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; +import javax.servlet.DispatcherType; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + /** * @author Stian Thorgersen */ diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/MailServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/MailServer.java old mode 100644 new mode 100755 index 8ba730a012..fd9ad225e0 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/MailServer.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/MailServer.java @@ -1,11 +1,11 @@ package org.keycloak.testutils; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMessage.RecipientType; - import com.icegreen.greenmail.util.GreenMail; import com.icegreen.greenmail.util.ServerSetup; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMessage.RecipientType; + public class MailServer { public static void main(String[] args) throws Exception { diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/TotpGenerator.java b/testsuite/integration/src/main/java/org/keycloak/testutils/TotpGenerator.java old mode 100644 new mode 100755 index bc5f633b19..ff0ac20e1f --- a/testsuite/integration/src/main/java/org/keycloak/testutils/TotpGenerator.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/TotpGenerator.java @@ -1,11 +1,12 @@ package org.keycloak.testutils; +import org.keycloak.models.utils.Base32; +import org.keycloak.models.utils.TimeBasedOTP; + import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; -import org.picketlink.common.util.Base32; -import org.picketlink.idm.credential.util.TimeBasedOTP; public class TotpGenerator { diff --git a/testsuite/integration/src/main/resources/META-INF/persistence.xml b/testsuite/integration/src/main/resources/META-INF/persistence.xml index c17ec560a1..f299e7ec55 100755 --- a/testsuite/integration/src/main/resources/META-INF/persistence.xml +++ b/testsuite/integration/src/main/resources/META-INF/persistence.xml @@ -2,24 +2,22 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> - + org.hibernate.ejb.HibernatePersistence - org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity - org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity - org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity - org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity - org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity - org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity - org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity - org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity - org.keycloak.models.picketlink.mappings.RealmEntity - org.keycloak.models.picketlink.mappings.ApplicationEntity + org.keycloak.models.jpa.entities.ApplicationEntity + org.keycloak.models.jpa.entities.ApplicationScopeMappingEntity + org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity + org.keycloak.models.jpa.entities.CredentialEntity + org.keycloak.models.jpa.entities.OAuthClientEntity + org.keycloak.models.jpa.entities.RealmEntity + org.keycloak.models.jpa.entities.RealmScopeMappingEntity + org.keycloak.models.jpa.entities.RealmUserRoleMappingEntity + org.keycloak.models.jpa.entities.RequiredCredentialEntity + org.keycloak.models.jpa.entities.RoleEntity + org.keycloak.models.jpa.entities.SocialLinkEntity + org.keycloak.models.jpa.entities.UserEntity + org.keycloak.models.jpa.entities.UserRoleMappingEntity true diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java old mode 100644 new mode 100755 index 4202cc1ff7..9aa9655696 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java @@ -21,19 +21,18 @@ */ package org.keycloak.testsuite; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; /** * @author Stian Thorgersen diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java old mode 100644 new mode 100755 index 056e651773..d2964982a8 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java @@ -1,7 +1,5 @@ package org.keycloak.testsuite; -import java.util.UUID; - import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthRequest; import org.keycloak.social.AuthRequestBuilder; @@ -10,6 +8,8 @@ import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderException; import org.keycloak.social.SocialUser; +import java.util.UUID; + public class DummySocial implements SocialProvider { private static final String AUTH_PATH = "http://localhost:8081/dummy-social/auth"; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java old mode 100644 new mode 100755 index d7b1e37b07..f6cc0d0558 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java @@ -1,17 +1,16 @@ package org.keycloak.testsuite; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.charset.Charset; -import java.util.List; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.Charset; +import java.util.List; public class DummySocialServlet extends HttpServlet { diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java old mode 100644 new mode 100755 index dcda888985..4522d9e1fa --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java @@ -21,17 +21,6 @@ */ package org.keycloak.testsuite; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.Charset; -import java.security.PublicKey; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import javax.ws.rs.core.UriBuilder; - import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; @@ -49,6 +38,16 @@ import org.keycloak.representations.SkeletonKeyToken; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + /** * @author Stian Thorgersen */ diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthGrantServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthGrantServlet.java old mode 100644 new mode 100755 index c0ae537816..abfa4c3c2c --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthGrantServlet.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthGrantServlet.java @@ -21,22 +21,21 @@ */ package org.keycloak.testsuite; -import java.io.IOException; -import java.io.PrintWriter; -import java.security.PublicKey; -import java.util.Map; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.keycloak.PemUtils; +import org.keycloak.RSATokenVerifier; +import org.keycloak.representations.SkeletonKeyToken; +import org.keycloak.servlet.ServletOAuthClient; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.UriBuilder; - -import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; -import org.keycloak.PemUtils; -import org.keycloak.RSATokenVerifier; -import org.keycloak.representations.SkeletonKeyToken; -import org.keycloak.servlet.ServletOAuthClient; +import java.io.IOException; +import java.io.PrintWriter; +import java.security.PublicKey; +import java.util.Map; /** * @author Viliam Rockai diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java index 8b27729aff..7909d8378b 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java @@ -21,21 +21,14 @@ */ package org.keycloak.testsuite.actions; -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; - import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.keycloak.services.managers.RealmManager; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; +import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginPage; @@ -48,6 +41,12 @@ import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * @author Stian Thorgersen */ diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java index 51ba94435e..eca156e105 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java @@ -25,10 +25,10 @@ import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.keycloak.services.managers.RealmManager; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; +import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginPage; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java index f4cb070c5f..b8a7b7ad4a 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java @@ -25,10 +25,10 @@ import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.keycloak.services.managers.RealmManager; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; +import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage.RequestType; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java index bb70d976cf..48291582d4 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java @@ -25,11 +25,10 @@ import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.managers.RealmManager; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserModel.RequiredAction; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.pages.AccountTotpPage; import org.keycloak.testsuite.pages.AppPage; @@ -42,7 +41,6 @@ import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; -import org.picketlink.idm.credential.util.TimeBasedOTP; /** * @author Stian Thorgersen diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java index c567e99104..0b66340928 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java @@ -22,13 +22,12 @@ package org.keycloak.testsuite.actions; import org.junit.Assert; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.keycloak.services.managers.RealmManager; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; +import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginPage; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java index 5b1a82725d..b03594560e 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java @@ -21,12 +21,17 @@ */ package org.keycloak.testsuite.forms; -import org.junit.*; -import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.services.managers.RealmManager; +import org.junit.After; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.TimeBasedOTP; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.pages.AccountPasswordPage; import org.keycloak.testsuite.pages.AccountTotpPage; @@ -39,9 +44,6 @@ import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.picketlink.idm.credential.util.TimeBasedOTP; /** * @author Stian Thorgersen diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java index 54451f428f..c637062f4d 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java @@ -21,18 +21,17 @@ */ package org.keycloak.testsuite.forms; -import java.net.MalformedURLException; - import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.services.managers.RealmManager; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.TimeBasedOTP; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginPage; @@ -43,7 +42,8 @@ import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; -import org.picketlink.idm.credential.util.TimeBasedOTP; + +import java.net.MalformedURLException; /** * @author Stian Thorgersen diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java old mode 100644 new mode 100755 index 396f3d9411..2610ff5ef0 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java @@ -21,11 +21,6 @@ */ package org.keycloak.testsuite.forms; -import java.io.IOException; - -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; - import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; @@ -42,6 +37,10 @@ import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import java.io.IOException; + /** * @author Stian Thorgersen */ diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java old mode 100644 new mode 100755 index 2ccd506a9b..017aca52b3 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java @@ -21,9 +21,6 @@ */ package org.keycloak.testsuite.oauth; -import java.io.IOException; - -import org.apache.http.client.ClientProtocolException; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; @@ -41,6 +38,8 @@ import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; +import java.io.IOException; + /** * @author Stian Thorgersen */ diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java old mode 100644 new mode 100755 index 0513f6707d..3a60ae6897 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java @@ -21,8 +21,6 @@ */ package org.keycloak.testsuite.oauth; -import java.io.IOException; - import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -37,6 +35,8 @@ import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; +import java.io.IOException; + /** * @author Viliam Rockai */ diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java old mode 100644 new mode 100755 index 9860b14078..d849330333 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java @@ -21,9 +21,6 @@ */ package org.keycloak.testsuite.pages; -import org.junit.Assert; -import org.keycloak.testsuite.rule.WebResource; -import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/GreenMailRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/GreenMailRule.java old mode 100644 new mode 100755 index 386ddb740c..bd52a59c7c --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/GreenMailRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/GreenMailRule.java @@ -21,18 +21,13 @@ */ package org.keycloak.testsuite.rule; -import java.lang.Thread.UncaughtExceptionHandler; -import java.net.SocketException; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Properties; - -import javax.mail.internet.MimeMessage; - -import org.junit.rules.ExternalResource; - import com.icegreen.greenmail.util.GreenMail; import com.icegreen.greenmail.util.ServerSetup; +import org.junit.rules.ExternalResource; + +import javax.mail.internet.MimeMessage; +import java.lang.Thread.UncaughtExceptionHandler; +import java.net.SocketException; /** * @author Stian Thorgersen diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java index 010b1cccff..cdbcfe10ed 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java @@ -23,13 +23,6 @@ package org.keycloak.testsuite.rule; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.ServletInfo; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import javax.servlet.Servlet; - import org.jboss.resteasy.jwt.JsonSerialization; import org.junit.rules.ExternalResource; import org.keycloak.models.Constants; @@ -40,6 +33,11 @@ import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.ApplicationServlet; import org.keycloak.testutils.KeycloakServer; +import javax.servlet.Servlet; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + /** * @author Stian Thorgersen */ diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java old mode 100644 new mode 100755 index 88301c5417..ae12ea95ed --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java @@ -21,8 +21,7 @@ */ package org.keycloak.testsuite.rule; -import java.lang.reflect.Field; - +import com.gargoylesoftware.htmlunit.WebClient; import org.junit.rules.ExternalResource; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.pages.AbstractPage; @@ -31,7 +30,7 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.PageFactory; -import com.gargoylesoftware.htmlunit.WebClient; +import java.lang.reflect.Field; /** * @author Stian Thorgersen diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java index 4f87a1058d..f7af3c509f 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java @@ -26,9 +26,9 @@ import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.keycloak.models.RealmModel; import org.keycloak.representations.SkeletonKeyToken; import org.keycloak.services.managers.RealmManager; -import org.keycloak.models.RealmModel; import org.keycloak.testsuite.DummySocialServlet; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.OAuthClient.AccessTokenResponse; @@ -44,7 +44,6 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import java.util.HashMap; -import java.util.Map; /** * @author Stian Thorgersen diff --git a/testsuite/performance/pom.xml b/testsuite/performance/pom.xml old mode 100644 new mode 100755 index 1cb822099b..f9dc957cb7 --- a/testsuite/performance/pom.xml +++ b/testsuite/performance/pom.xml @@ -146,6 +146,7 @@ jboss-logging ${jboss.logging.version} + org.mongodb mongo-java-driver diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java old mode 100644 new mode 100755 index efe57c128a..882f8bb7d9 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java @@ -1,9 +1,5 @@ package org.keycloak.testsuite.performance; -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; -import java.util.concurrent.atomic.AtomicInteger; - import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; @@ -12,6 +8,10 @@ import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakTransaction; import org.keycloak.services.resources.KeycloakApplication; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author Marek Posolda */ diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java old mode 100644 new mode 100755 index 27499982b2..c839337926 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java @@ -1,7 +1,5 @@ package org.keycloak.testsuite.performance; -import java.util.concurrent.atomic.AtomicInteger; - import org.apache.jmeter.samplers.SampleResult; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; @@ -11,6 +9,8 @@ import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.managers.RealmManager; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author Marek Posolda */ diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java old mode 100644 new mode 100755 index 29bd33cc92..ece002b48f --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java @@ -1,7 +1,5 @@ package org.keycloak.testsuite.performance; -import java.util.concurrent.atomic.AtomicInteger; - import org.apache.jmeter.samplers.SampleResult; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; @@ -13,6 +11,8 @@ import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.CredentialRepresentation; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author Marek Posolda */ diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java old mode 100644 new mode 100755 index 416cd6029f..737fc21312 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java @@ -1,7 +1,5 @@ package org.keycloak.testsuite.performance; -import java.util.concurrent.atomic.AtomicInteger; - import org.apache.jmeter.samplers.SampleResult; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; @@ -10,6 +8,8 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.SocialLinkModel; import org.keycloak.models.UserModel; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author Marek Posolda */ diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java old mode 100644 new mode 100755 index 262ad932a6..b13b7d1521 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java @@ -1,13 +1,12 @@ package org.keycloak.testsuite.performance; -import java.util.concurrent.atomic.AtomicInteger; - import org.apache.jmeter.samplers.SampleResult; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.services.utils.PropertiesManager; + +import java.util.concurrent.atomic.AtomicInteger; /** * @author Marek Posolda diff --git a/testsuite/performance/src/test/resources/META-INF/persistence.xml b/testsuite/performance/src/test/resources/META-INF/persistence.xml old mode 100644 new mode 100755 index 1dff64183f..8083104f78 --- a/testsuite/performance/src/test/resources/META-INF/persistence.xml +++ b/testsuite/performance/src/test/resources/META-INF/persistence.xml @@ -2,7 +2,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> - + org.hibernate.ejb.HibernatePersistence org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity @@ -18,8 +18,39 @@ org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity - org.keycloak.services.models.picketlink.mappings.RealmEntity - org.keycloak.services.models.picketlink.mappings.ApplicationEntity + org.keycloak.models.picketlink.mappings.RealmEntity + org.keycloak.models.picketlink.mappings.ApplicationEntity + + true + + + + + + + + + + + + + + + org.hibernate.ejb.HibernatePersistence + + org.keycloak.models.jpa.entities.ApplicationEntity + org.keycloak.models.jpa.entities.ApplicationScopeMappingEntity + org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity + org.keycloak.models.jpa.entities.CredentialEntity + org.keycloak.models.jpa.entities.OAuthClientEntity + org.keycloak.models.jpa.entities.RealmEntity + org.keycloak.models.jpa.entities.RealmScopeMappingEntity + org.keycloak.models.jpa.entities.RealmUserRoleMappingEntity + org.keycloak.models.jpa.entities.RequiredCredentialEntity + org.keycloak.models.jpa.entities.RoleEntity + org.keycloak.models.jpa.entities.SocialLinkEntity + org.keycloak.models.jpa.entities.UserEntity + org.keycloak.models.jpa.entities.UserRoleMappingEntity true