From c4416a25e05f9d1f643959270da39ef1756e0f94 Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Thu, 12 Nov 2015 23:29:26 +0100 Subject: [PATCH 01/16] KEYCLOAK-2068 - Fix Potential NPE when using Servlet-Filter Adapter. When using the `org.keycloak.adapters.servlet.KeycloakOIDCFilter` a `NullPointerException` can be thrown in the `org.keycloak.adapters.servlet.FilterSessionStore` within the `getParam` method of the generated wrapper in `buildWrapper` when the `content-type` is not set. Since the `content-type` is only used to parse the body. We just check whether the `body` is `null` and if so avoid touching the `content-type` which prevents the NPE. If the `body` is null we return an empty `MultivaluedHashMap` for the parameters. --- .../java/org/keycloak/adapters/servlet/FilterSessionStore.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java index aad0ec5ec5..641ca709ea 100755 --- a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java +++ b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java @@ -90,6 +90,9 @@ public class FilterSessionStore implements AdapterSessionStore { MultivaluedHashMap getParams() { if (parameters != null) return parameters; + + if (body == null) return new MultivaluedHashMap(); + String contentType = getContentType(); contentType = contentType.toLowerCase(); if (contentType.startsWith("application/x-www-form-urlencoded")) { From 2e16a5581502dfd6ef6c3825b2700ccb8e90a255 Mon Sep 17 00:00:00 2001 From: bystrikhorvath Date: Fri, 13 Nov 2015 14:00:20 +0100 Subject: [PATCH 02/16] KEYCLOAK-2077: correcting paths in custom-attributes.xml --- .../en/en-US/modules/custom-attributes.xml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/custom-attributes.xml b/docbook/auth-server-docs/reference/en/en-US/modules/custom-attributes.xml index 7aefdb676d..49d58ad945 100755 --- a/docbook/auth-server-docs/reference/en/en-US/modules/custom-attributes.xml +++ b/docbook/auth-server-docs/reference/en/en-US/modules/custom-attributes.xml @@ -8,7 +8,7 @@ - Create a new theme within the themes/admin/mytheme directory in your distribution. + Create a new theme within the themes/mytheme/admin directory in your distribution. Where mytheme is whatever you want to name your theme. @@ -19,15 +19,15 @@ import=common/keycloak ]]> - Copy the file themes/admin/base/resources/partials/user-attribute-entry.html into the - a mirror directory in your theme: themes/admin/mytheme/resources/partials/user-attribute-entry.html. + Copy the file themes/base/admin/resources/partials/user-attributes.html into the + a mirror directory in your theme: themes/mytheme/admin/resources/partials/user-attributes.html. What you are doing here is overriding the user attribute entry page in the admin console and putting in what attributes you want. This file already contains an example of entering address data. You can remove this if you want and replace it with something else. Also, if you want to edit this file directly instead of creating a new theme, you can. - In the user-attribute-entry.html file add your custom user attribute entry form item. For example + In the user-attributes.html file add your custom user attribute entry form item. For example
@@ -52,7 +52,7 @@ import=common/keycloak - Create a new theme within the themes/login/mytheme directory in your distribution. + Create a new theme within the themes/mytheme/login directory in your distribution. Where mytheme is whatever you want to name your theme. @@ -63,8 +63,8 @@ import=common/keycloak styles= ../patternfly/lib/patternfly/css/patternfly.css ../patternfly/css/login.css ../patternfly/lib/zocial/zocial.css css/login.css]]> - Copy the file themes/login/base/register.ftl into the - a mirror directory in your theme: themes/login/mytheme/register.ftl. + Copy the file themes/base/login/register.ftl into the + a mirror directory in your theme: themes/mytheme/login/register.ftl. What you are doing here is overriding the registration page and adding what attributes you want. This file already contains an example of entering address data. You can remove this if you want and replace it with something else. Also, if you want to edit this file directly instead @@ -101,7 +101,7 @@ styles= ../patternfly/lib/patternfly/css/patternfly.css ../patternfly/css/login. - Create a new theme within the themes/account/mytheme directory in your distribution. + Create a new theme within the themes/mytheme/account directory in your distribution. Where mytheme is whatever you want to name your theme. @@ -113,8 +113,8 @@ import=common/keycloak styles= ../patternfly/lib/patternfly/css/patternfly.css ../patternfly/css/account.css css/account.css]]> - Copy the file themes/account/base/account.ftl into the - a mirror directory in your theme: themes/account/mytheme/account.ftl. + Copy the file themes/base/account/account.ftl into the + a mirror directory in your theme: themes/mytheme/account/account.ftl. What you are doing here is overriding the profile page and adding what attributes you want to manage. This file already contains an example of entering address data. You can remove this if you want and replace it with something else. Also, if you want to edit this file directly instead From 13a52c1bb2bb79646d4996bc14817a56f18c59fb Mon Sep 17 00:00:00 2001 From: Lukas Kubik Date: Fri, 13 Nov 2015 15:43:11 +0100 Subject: [PATCH 03/16] KEYCLOAK-2081 Change scope of jetty dependencies to provided --- integration/jetty/jetty-core/pom.xml | 6 +++--- integration/jetty/jetty8.1/pom.xml | 6 +++--- integration/jetty/jetty9.1/pom.xml | 6 +++--- integration/jetty/jetty9.2/pom.xml | 6 +++--- saml/client-adapter/jetty/jetty-core/pom.xml | 6 +++--- saml/client-adapter/jetty/jetty8.1/pom.xml | 6 +++--- saml/client-adapter/jetty/jetty9.1/pom.xml | 6 +++--- saml/client-adapter/jetty/jetty9.2/pom.xml | 6 +++--- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/integration/jetty/jetty-core/pom.xml b/integration/jetty/jetty-core/pom.xml index f1029a955d..5c0700cf2e 100755 --- a/integration/jetty/jetty-core/pom.xml +++ b/integration/jetty/jetty-core/pom.xml @@ -71,21 +71,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided diff --git a/integration/jetty/jetty8.1/pom.xml b/integration/jetty/jetty8.1/pom.xml index 580f9c0d8b..b7577ec3a4 100755 --- a/integration/jetty/jetty8.1/pom.xml +++ b/integration/jetty/jetty8.1/pom.xml @@ -70,21 +70,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided diff --git a/integration/jetty/jetty9.1/pom.xml b/integration/jetty/jetty9.1/pom.xml index f0c4a5b256..0ad77eaa7a 100755 --- a/integration/jetty/jetty9.1/pom.xml +++ b/integration/jetty/jetty9.1/pom.xml @@ -81,21 +81,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided diff --git a/integration/jetty/jetty9.2/pom.xml b/integration/jetty/jetty9.2/pom.xml index ab82620684..7ae5028261 100755 --- a/integration/jetty/jetty9.2/pom.xml +++ b/integration/jetty/jetty9.2/pom.xml @@ -67,21 +67,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided diff --git a/saml/client-adapter/jetty/jetty-core/pom.xml b/saml/client-adapter/jetty/jetty-core/pom.xml index 52cc27366d..87bf21a642 100755 --- a/saml/client-adapter/jetty/jetty-core/pom.xml +++ b/saml/client-adapter/jetty/jetty-core/pom.xml @@ -59,21 +59,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided diff --git a/saml/client-adapter/jetty/jetty8.1/pom.xml b/saml/client-adapter/jetty/jetty8.1/pom.xml index 937b8ee67f..1e888f59d6 100755 --- a/saml/client-adapter/jetty/jetty8.1/pom.xml +++ b/saml/client-adapter/jetty/jetty8.1/pom.xml @@ -54,21 +54,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided diff --git a/saml/client-adapter/jetty/jetty9.1/pom.xml b/saml/client-adapter/jetty/jetty9.1/pom.xml index f9bc11b04f..b8ca67b6ea 100755 --- a/saml/client-adapter/jetty/jetty9.1/pom.xml +++ b/saml/client-adapter/jetty/jetty9.1/pom.xml @@ -69,21 +69,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided diff --git a/saml/client-adapter/jetty/jetty9.2/pom.xml b/saml/client-adapter/jetty/jetty9.2/pom.xml index d7dc8b89ba..9382c4a0fd 100755 --- a/saml/client-adapter/jetty/jetty9.2/pom.xml +++ b/saml/client-adapter/jetty/jetty9.2/pom.xml @@ -69,21 +69,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - compile + provided org.eclipse.jetty jetty-util ${jetty9.version} - compile + provided org.eclipse.jetty jetty-security ${jetty9.version} - compile + provided From 4b0e33e388c19152fa728125acfc8923651e6dfc Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 13 Nov 2015 09:38:09 -0200 Subject: [PATCH 04/16] KEYCLOAK-2056: Removal of file model provider --- dependencies/server-all/pom.xml | 4 - .../keycloak-model-file/main/module.xml | 18 - .../WEB-INF/jboss-deployment-structure.xml | 3 +- .../keycloak-services/main/module.xml | 1 - .../eap6/eap6-server-modules/build.xml | 10 +- .../WEB-INF/jboss-deployment-structure.xml | 3 +- .../keycloak-model-file/main/module.xml | 16 - .../keycloak-services/main/module.xml | 1 - model/file/pom.xml | 62 - .../models/file/FileRealmProvider.java | 119 -- .../models/file/FileRealmProviderFactory.java | 58 - .../models/file/FileUserProvider.java | 520 ----- .../models/file/FileUserProviderFactory.java | 56 - .../models/file/adapter/ClientAdapter.java | 663 ------ .../models/file/adapter/GroupAdapter.java | 213 -- .../file/adapter/MigrationModelAdapter.java | 26 - .../models/file/adapter/RealmAdapter.java | 1840 ----------------- .../models/file/adapter/RoleAdapter.java | 188 -- .../models/file/adapter/UserAdapter.java | 614 ------ .../org.keycloak.models.RealmProviderFactory | 1 - .../org.keycloak.models.UserProviderFactory | 1 - model/pom.xml | 1 - pom.xml | 11 +- 23 files changed, 7 insertions(+), 4422 deletions(-) delete mode 100755 distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-model-file/main/module.xml delete mode 100755 distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-model-file/main/module.xml delete mode 100755 model/file/pom.xml delete mode 100755 model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java delete mode 100644 model/file/src/main/java/org/keycloak/models/file/FileRealmProviderFactory.java delete mode 100755 model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java delete mode 100644 model/file/src/main/java/org/keycloak/models/file/FileUserProviderFactory.java delete mode 100755 model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java delete mode 100755 model/file/src/main/java/org/keycloak/models/file/adapter/GroupAdapter.java delete mode 100755 model/file/src/main/java/org/keycloak/models/file/adapter/MigrationModelAdapter.java delete mode 100755 model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java delete mode 100755 model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java delete mode 100755 model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java delete mode 100644 model/file/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory delete mode 100644 model/file/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index 81ce0957c5..ad786a3cc0 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -36,10 +36,6 @@ org.keycloak keycloak-model-jpa - - org.keycloak - keycloak-model-file - org.keycloak keycloak-model-sessions-infinispan diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-model-file/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-model-file/main/module.xml deleted file mode 100755 index 2612e06451..0000000000 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-model-file/main/module.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml index c339f44671..d8b1aa2ca0 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml @@ -33,7 +33,6 @@ - @@ -70,4 +69,4 @@ - \ No newline at end of file + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml index 77ce3ad8dc..6b83a7e81d 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml @@ -43,7 +43,6 @@ - diff --git a/distribution/server-overlay/eap6/eap6-server-modules/build.xml b/distribution/server-overlay/eap6/eap6-server-modules/build.xml index 60ccb65be0..924a32a6ac 100755 --- a/distribution/server-overlay/eap6/eap6-server-modules/build.xml +++ b/distribution/server-overlay/eap6/eap6-server-modules/build.xml @@ -224,11 +224,11 @@ - + - + @@ -250,12 +250,6 @@ - - - - - - diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml index c339f44671..d8b1aa2ca0 100755 --- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml +++ b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml @@ -33,7 +33,6 @@ - @@ -70,4 +69,4 @@ - \ No newline at end of file + diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-model-file/main/module.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-model-file/main/module.xml deleted file mode 100755 index 46f8ffd25d..0000000000 --- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-model-file/main/module.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml index 44703f8a5d..bf230faab4 100755 --- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml @@ -43,7 +43,6 @@ - diff --git a/model/file/pom.xml b/model/file/pom.xml deleted file mode 100755 index bb5333e042..0000000000 --- a/model/file/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - keycloak-parent - org.keycloak - 1.7.0.Final-SNAPSHOT - ../../pom.xml - - 4.0.0 - - keycloak-model-file - Keycloak Model File - - - - - org.keycloak - keycloak-export-import-api - - - org.keycloak - keycloak-export-import-single-file - - - org.keycloak - keycloak-core - provided - - - org.keycloak - keycloak-model-api - - - org.keycloak - keycloak-connections-file - - - org.codehaus.jackson - jackson-mapper-asl - provided - - - org.jboss.logging - jboss-logging - provided - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - ${maven.compiler.source} - ${maven.compiler.target} - - - - - - diff --git a/model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java b/model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java deleted file mode 100755 index 33d4fa36d3..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file; - -import org.keycloak.connections.file.FileConnectionProvider; -import org.keycloak.connections.file.InMemoryModel; -import org.keycloak.migration.MigrationModel; -import org.keycloak.models.ClientModel; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RealmProvider; -import org.keycloak.models.RoleModel; -import org.keycloak.models.entities.RealmEntity; -import org.keycloak.models.file.adapter.MigrationModelAdapter; -import org.keycloak.models.file.adapter.RealmAdapter; -import org.keycloak.models.utils.KeycloakModelUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Realm Provider for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class FileRealmProvider implements RealmProvider { - - private final KeycloakSession session; - private FileConnectionProvider fcProvider; - private final InMemoryModel inMemoryModel; - - public FileRealmProvider(KeycloakSession session, FileConnectionProvider fcProvider) { - this.session = session; - this.fcProvider = fcProvider; - session.enlistForClose(this); - this.inMemoryModel = fcProvider.getModel(); - } - - @Override - public void close() { - fcProvider.sessionClosed(session); - } - - @Override - public MigrationModel getMigrationModel() { - return new MigrationModelAdapter(inMemoryModel); - } - - @Override - public RealmModel createRealm(String name) { - return createRealm(KeycloakModelUtils.generateId(), name); - } - - @Override - public RealmModel createRealm(String id, String name) { - if (getRealmByName(name) != null) throw new ModelDuplicateException("Realm " + name + " already exists."); - RealmEntity realmEntity = new RealmEntity(); - realmEntity.setName(name); - realmEntity.setId(id); - RealmAdapter realm = new RealmAdapter(session, realmEntity, inMemoryModel); - inMemoryModel.putRealm(id, realm); - - return realm; - } - - @Override - public GroupModel getGroupById(String id, RealmModel realm) { - return null; - } - - @Override - public RealmModel getRealm(String id) { - RealmModel model = inMemoryModel.getRealm(id); - return model; - } - - @Override - public List getRealms() { - return new ArrayList(inMemoryModel.getRealms()); - } - - @Override - public RealmModel getRealmByName(String name) { - RealmModel model = inMemoryModel.getRealmByName(name); - return model; - } - - @Override - public boolean removeRealm(String id) { - return inMemoryModel.removeRealm(id); - } - - @Override - public RoleModel getRoleById(String id, RealmModel realm) { - return realm.getRoleById(id); - } - - @Override - public ClientModel getClientById(String id, RealmModel realm) { - return realm.getClientById(id); - } - -} diff --git a/model/file/src/main/java/org/keycloak/models/file/FileRealmProviderFactory.java b/model/file/src/main/java/org/keycloak/models/file/FileRealmProviderFactory.java deleted file mode 100644 index 211d8a5e64..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/FileRealmProviderFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file; - -import org.keycloak.Config; -import org.keycloak.connections.file.FileConnectionProvider; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmProvider; -import org.keycloak.models.RealmProviderFactory; - - -/** - * RealmProviderFactory for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class FileRealmProviderFactory implements RealmProviderFactory { - - @Override - public void init(Config.Scope config) { - - } - - @Override - public String getId() { - return "file"; - } - - @Override - public RealmProvider create(KeycloakSession session) { - FileConnectionProvider fcProvider = session.getProvider(FileConnectionProvider.class); - return new FileRealmProvider(session, fcProvider); - } - - @Override - public void close() { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - -} diff --git a/model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java b/model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java deleted file mode 100755 index e4b29d3dbf..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file; - -import org.keycloak.connections.file.FileConnectionProvider; -import org.keycloak.connections.file.InMemoryModel; -import org.keycloak.models.ClientModel; -import org.keycloak.models.CredentialValidationOutput; -import org.keycloak.models.FederatedIdentityModel; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ModelException; -import org.keycloak.models.session.PersistentClientSessionModel; -import org.keycloak.models.session.PersistentUserSessionModel; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RequiredActionProviderModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserProvider; -import org.keycloak.models.entities.FederatedIdentityEntity; -import org.keycloak.models.entities.PersistentClientSessionEntity; -import org.keycloak.models.entities.PersistentUserSessionEntity; -import org.keycloak.models.entities.UserEntity; -import org.keycloak.models.file.adapter.UserAdapter; -import org.keycloak.models.utils.CredentialValidation; -import org.keycloak.models.utils.KeycloakModelUtils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * UserProvider for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class FileUserProvider implements UserProvider { - - private final KeycloakSession session; - private FileConnectionProvider fcProvider; - private final InMemoryModel inMemoryModel; - - public FileUserProvider(KeycloakSession session, FileConnectionProvider fcProvider) { - this.session = session; - this.fcProvider = fcProvider; - session.enlistForClose(this); - this.inMemoryModel = fcProvider.getModel(); - } - - @Override - public void close() { - fcProvider.sessionClosed(session); - } - - @Override - public UserModel getUserById(String userId, RealmModel realm) { - return inMemoryModel.getUser(realm.getId(), userId); - } - - @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { - return null; - } - - @Override - public List getGroupMembers(RealmModel realm, GroupModel group) { - return null; - } - - @Override - public void preRemove(RealmModel realm, GroupModel group) { - - } - - @Override - public UserModel getUserByUsername(String username, RealmModel realm) { - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - if (user.getUsername() == null) continue; - if (user.getUsername().equals(username.toLowerCase())) return user; - } - - return null; - } - - @Override - public UserModel getUserByEmail(String email, RealmModel realm) { - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - if (user.getEmail() == null) continue; - if (user.getEmail().equals(email.toLowerCase())) return user; - } - - return null; - } - - @Override - public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) { - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - Set identities = this.getFederatedIdentities(user, realm); - for (FederatedIdentityModel idModel : identities) { - if (idModel.getUserId().equals(socialLink.getUserId())) return user; - } - } - - return null; - } - - @Override - public UserModel getUserByServiceAccountClient(ClientModel client) { - for (UserModel user : inMemoryModel.getUsers(client.getRealm().getId())) { - if (client.getId().equals(user.getServiceAccountClientLink())) { - return user; - } - } - return null; - } - - @Override - public List getUsers(RealmModel realm, boolean includeServiceAccounts) { - return getUsers(realm, -1, -1, includeServiceAccounts); - } - - @Override - public int getUsersCount(RealmModel realm) { - return inMemoryModel.getUsers(realm.getId()).size(); - } - - @Override - public List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) { - List users = new ArrayList<>(inMemoryModel.getUsers(realm.getId())); - - if (!includeServiceAccounts) { - users = filterServiceAccountUsers(users); - } - - List sortedList = sortedSubList(users, firstResult, maxResults); - return sortedList; - } - - private List filterServiceAccountUsers(List users) { - List result = new ArrayList<>(); - for (UserModel user : users) { - if (user.getServiceAccountClientLink() == null) { - result.add(user); - } - } - return result; - } - - protected List sortedSubList(List list, int firstResult, int maxResults) { - if (list.isEmpty()) return list; - - Collections.sort(list); - int first = (firstResult <= 0) ? 0 : firstResult; - int last = first + maxResults; // could be int overflow - if ((maxResults > list.size() - first) || (last > list.size())) { // int overflow or regular overflow - last = list.size(); - } - - if (maxResults <= 0) { - last = list.size(); - } - - return list.subList(first, last); - } - - @Override - public List searchForUser(String search, RealmModel realm) { - return searchForUser(search, realm, -1, -1); - } - - @Override - public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { - search = search.trim(); - Pattern caseInsensitivePattern = Pattern.compile("(?i:.*" + search + ".*)", Pattern.CASE_INSENSITIVE); - - int spaceInd = search.lastIndexOf(" "); - boolean isFirstAndLastSearch = spaceInd != -1; - Pattern firstNamePattern = null; - Pattern lastNamePattern = null; - if (isFirstAndLastSearch) { - String firstNamePatternString = search.substring(0, spaceInd); - String lastNamePatternString = search.substring(spaceInd + 1); - firstNamePattern = Pattern.compile("(?i:.*" + firstNamePatternString + ".*$)", Pattern.CASE_INSENSITIVE); - lastNamePattern = Pattern.compile("(?i:^.*" + lastNamePatternString + ".*)", Pattern.CASE_INSENSITIVE); - } - - List found = new ArrayList(); - - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - String firstName = user.getFirstName(); - String lastName = user.getLastName(); - // Case when we have search string like "ohn Bow". Then firstName must end with "ohn" AND lastName must start with "bow" (everything case-insensitive) - if (isFirstAndLastSearch) { - if (isAMatch(firstNamePattern, firstName) && - isAMatch(lastNamePattern, lastName)) { - found.add(user); - continue; - } - } - - if (isAMatch(caseInsensitivePattern, firstName) || - isAMatch(caseInsensitivePattern, lastName) || - isAMatch(caseInsensitivePattern, user.getUsername()) || - isAMatch(caseInsensitivePattern, user.getEmail())) { - found.add(user); - } - } - - // Remove users with service account link - found = filterServiceAccountUsers(found); - - return sortedSubList(found, firstResult, maxResults); - } - - @Override - public List searchForUserByAttributes(Map attributes, RealmModel realm) { - return searchForUserByAttributes(attributes, realm, -1, -1); - } - - protected boolean isAMatch(Pattern pattern, String value) { - return (value != null) && (pattern != null) && pattern.matcher(value).matches(); - } - - @Override - public List searchForUserByAttributes(Map attributes, RealmModel realm, int firstResult, int maxResults) { - Pattern usernamePattern = null; - Pattern firstNamePattern = null; - Pattern lastNamePattern = null; - Pattern emailPattern = null; - for (Map.Entry entry : attributes.entrySet()) { - if (entry.getKey().equalsIgnoreCase(UserModel.USERNAME)) { - usernamePattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE); - } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) { - firstNamePattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE); - } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) { - lastNamePattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE); - } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) { - emailPattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE); - } - } - - List found = new ArrayList(); - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - if (isAMatch(usernamePattern, user.getUsername()) || - isAMatch(firstNamePattern, user.getFirstName()) || - isAMatch(lastNamePattern, user.getLastName()) || - isAMatch(emailPattern, user.getEmail())) { - found.add(user); - } - } - - return sortedSubList(found, firstResult, maxResults); - } - - @Override - public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { - Collection users = inMemoryModel.getUsers(realm.getId()); - - List matchedUsers = new ArrayList<>(); - for (UserModel user : users) { - List vals = user.getAttribute(attrName); - if (vals.contains(attrValue)) { - matchedUsers.add(user); - } - } - - return matchedUsers; - } - - @Override - public Set getFederatedIdentities(UserModel userModel, RealmModel realm) { - UserEntity userEntity = ((UserAdapter)getUserById(userModel.getId(), realm)).getUserEntity(); - List linkEntities = userEntity.getFederatedIdentities(); - - if (linkEntities == null) { - return Collections.EMPTY_SET; - } - - Set result = new HashSet(); - for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) { - FederatedIdentityModel model = new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), - federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName()); - result.add(model); - } - return result; - } - - private FederatedIdentityEntity findSocialLink(UserModel userModel, String socialProvider, RealmModel realm) { - UserModel user = getUserById(userModel.getId(), realm); - UserEntity userEntity = ((UserAdapter)getUserById(userModel.getId(), realm)).getUserEntity(); - List linkEntities = userEntity.getFederatedIdentities(); - if (linkEntities == null) { - return null; - } - - for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) { - if (federatedIdentityEntity.getIdentityProvider().equals(socialProvider)) { - return federatedIdentityEntity; - } - } - return null; - } - - - @Override - public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) { - FederatedIdentityEntity federatedIdentityEntity = findSocialLink(user, socialProvider, realm); - return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName()) : null; - } - - @Override - public UserAdapter addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions) { - if (inMemoryModel.hasUserWithUsername(realm.getId(), username.toLowerCase())) - throw new ModelDuplicateException("User with username " + username + " already exists in realm."); - - UserAdapter userModel = addUserEntity(realm, id, username.toLowerCase()); - - if (addDefaultRoles) { - for (String r : realm.getDefaultRoles()) { - userModel.grantRole(realm.getRole(r)); - } - - for (ClientModel application : realm.getClients()) { - for (String r : application.getDefaultRoles()) { - userModel.grantRole(application.getRole(r)); - } - } - } - - if (addDefaultRequiredActions) { - for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) { - if (r.isEnabled() && r.isDefaultAction()) { - userModel.addRequiredAction(r.getAlias()); - } - } - } - - - return userModel; - } - - protected UserAdapter addUserEntity(RealmModel realm, String userId, String username) { - if (realm == null) throw new NullPointerException("realm == null"); - if (username == null) throw new NullPointerException("username == null"); - - if (userId == null) userId = KeycloakModelUtils.generateId(); - - UserEntity userEntity = new UserEntity(); - userEntity.setId(userId); - userEntity.setCreatedTimestamp(System.currentTimeMillis()); - userEntity.setUsername(username); - // Compatibility with JPA model, which has user disabled by default - // userEntity.setEnabled(true); - userEntity.setRealmId(realm.getId()); - - UserAdapter user = new UserAdapter(realm, userEntity, inMemoryModel); - inMemoryModel.putUser(realm.getId(), userId, user); - - return user; - } - - @Override - public boolean removeUser(RealmModel realm, UserModel user) { - return inMemoryModel.removeUser(realm.getId(), user.getId()); - } - - - @Override - public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) { - UserAdapter userAdapter = (UserAdapter)getUserById(user.getId(), realm); - UserEntity userEntity = userAdapter.getUserEntity(); - FederatedIdentityEntity federatedIdentityEntity = new FederatedIdentityEntity(); - federatedIdentityEntity.setIdentityProvider(socialLink.getIdentityProvider()); - federatedIdentityEntity.setUserId(socialLink.getUserId()); - federatedIdentityEntity.setUserName(socialLink.getUserName().toLowerCase()); - - //check if it already exitsts - do I need to do this? - for (FederatedIdentityEntity fedIdent : userEntity.getFederatedIdentities()) { - if (fedIdent.equals(federatedIdentityEntity)) return; - } - - userEntity.getFederatedIdentities().add(federatedIdentityEntity); - } - - @Override - public boolean removeFederatedIdentity(RealmModel realm, UserModel userModel, String socialProvider) { - UserModel user = getUserById(userModel.getId(), realm); - UserEntity userEntity = ((UserAdapter) user).getUserEntity(); - FederatedIdentityEntity federatedIdentityEntity = findSocialLink(userEntity, socialProvider); - if (federatedIdentityEntity == null) { - return false; - } - - userEntity.getFederatedIdentities().remove(federatedIdentityEntity); - return true; - } - - private FederatedIdentityEntity findSocialLink(UserEntity userEntity, String socialProvider) { - List linkEntities = userEntity.getFederatedIdentities(); - if (linkEntities == null) { - return null; - } - - for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) { - if (federatedIdentityEntity.getIdentityProvider().equals(socialProvider)) { - return federatedIdentityEntity; - } - } - return null; - } - - @Override - public UserModel addUser(RealmModel realm, String username) { - return this.addUser(realm, KeycloakModelUtils.generateId(), username.toLowerCase(), true, true); - } - - @Override - public void grantToAllUsers(RealmModel realm, RoleModel role) { - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - user.grantRole(role); - } - } - - @Override - public void preRemove(RealmModel realm) { - // Nothing to do here? Federation links are attached to users, which are removed by InMemoryModel - } - - @Override - public void preRemove(RealmModel realm, UserFederationProviderModel link) { - Set toBeRemoved = new HashSet(); - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - String fedLink = user.getFederationLink(); - if (fedLink == null) continue; - if (fedLink.equals(link.getId())) toBeRemoved.add(user); - } - - for (UserModel user : toBeRemoved) { - inMemoryModel.removeUser(realm.getId(), user.getId()); - } - } - - @Override - public void preRemove(RealmModel realm, RoleModel role) { - // todo not sure what to do for this - } - - @Override - public void preRemove(RealmModel realm, ClientModel client) { - // TODO - } - - @Override - public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) { - // TODO - } - - @Override - public boolean validCredentials(RealmModel realm, UserModel user, List input) { - return CredentialValidation.validCredentials(realm, user, input); - } - - @Override - public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) { - return CredentialValidation.validCredentials(realm, user, input); - } - - @Override - public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) { - federatedUser = getUserById(federatedUser.getId(), realm); - UserEntity userEntity = ((UserAdapter) federatedUser).getUserEntity(); - FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, federatedIdentityModel.getIdentityProvider()); - - federatedIdentityEntity.setToken(federatedIdentityModel.getToken()); - } - - private FederatedIdentityEntity findFederatedIdentityLink(UserEntity userEntity, String identityProvider) { - List linkEntities = userEntity.getFederatedIdentities(); - if (linkEntities == null) { - return null; - } - - for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) { - if (federatedIdentityEntity.getIdentityProvider().equals(identityProvider)) { - return federatedIdentityEntity; - } - } - return null; - } - - @Override - public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) { - //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - return null; // not supported yet - } -} diff --git a/model/file/src/main/java/org/keycloak/models/file/FileUserProviderFactory.java b/model/file/src/main/java/org/keycloak/models/file/FileUserProviderFactory.java deleted file mode 100644 index e7f3674229..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/FileUserProviderFactory.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file; - -import org.keycloak.Config; -import org.keycloak.connections.file.FileConnectionProvider; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.UserProvider; -import org.keycloak.models.UserProviderFactory; - -/** - * UserProviderFactory for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class FileUserProviderFactory implements UserProviderFactory { - - @Override - public void init(Config.Scope config) { - } - - @Override - public String getId() { - return "file"; - } - - @Override - public UserProvider create(KeycloakSession session) { - FileConnectionProvider fcProvider = session.getProvider(FileConnectionProvider.class); - return new FileUserProvider(session, fcProvider); - } - - @Override - public void close() { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - -} diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java deleted file mode 100755 index 81e60ee295..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file.adapter; - -import org.keycloak.connections.file.InMemoryModel; -import org.keycloak.models.ClientModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.entities.ClientEntity; -import org.keycloak.models.entities.ProtocolMapperEntity; -import org.keycloak.models.entities.RoleEntity; -import org.keycloak.models.utils.KeycloakModelUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * ApplicationModel used for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class ClientAdapter implements ClientModel { - - private final RealmModel realm; - private KeycloakSession session; - private final ClientEntity entity; - private final InMemoryModel inMemoryModel; - - private final Map allRoles = new HashMap(); - private final Map allScopeMappings = new HashMap(); - - public ClientAdapter(KeycloakSession session, RealmModel realm, ClientEntity entity, InMemoryModel inMemoryModel) { - this.realm = realm; - this.session = session; - this.entity = entity; - this.inMemoryModel = inMemoryModel; - } - - @Override - public void updateClient() { - } - - @Override - public String getId() { - return entity.getId(); - } - - @Override - public String getName() { - return entity.getName(); - } - - @Override - public void setName(String name) { - entity.setName(name); - } - - @Override - public String getDescription() { return entity.getDescription(); } - - @Override - public void setDescription(String description) { entity.setDescription(description); } - - @Override - public Set getWebOrigins() { - Set result = new HashSet(); - if (entity.getWebOrigins() != null) { - result.addAll(entity.getWebOrigins()); - } - return result; - } - - @Override - public void setWebOrigins(Set webOrigins) { - List result = new ArrayList(); - result.addAll(webOrigins); - entity.setWebOrigins(result); - } - - @Override - public void addWebOrigin(String webOrigin) { - Set webOrigins = getWebOrigins(); - webOrigins.add(webOrigin); - setWebOrigins(webOrigins); - } - - @Override - public void removeWebOrigin(String webOrigin) { - Set webOrigins = getWebOrigins(); - webOrigins.remove(webOrigin); - setWebOrigins(webOrigins); - } - - @Override - public Set getRedirectUris() { - Set result = new HashSet(); - if (entity.getRedirectUris() != null) { - result.addAll(entity.getRedirectUris()); - } - return result; - } - - @Override - public void setRedirectUris(Set redirectUris) { - List result = new ArrayList(); - result.addAll(redirectUris); - entity.setRedirectUris(result); - } - - @Override - public void addRedirectUri(String redirectUri) { - if (entity.getRedirectUris().contains(redirectUri)) return; - entity.getRedirectUris().add(redirectUri); - } - - @Override - public void removeRedirectUri(String redirectUri) { - entity.getRedirectUris().remove(redirectUri); - } - - @Override - public boolean isEnabled() { - return entity.isEnabled(); - } - - @Override - public void setEnabled(boolean enabled) { - entity.setEnabled(enabled); - } - - @Override - public String getClientAuthenticatorType() { - return entity.getClientAuthenticatorType(); - } - - @Override - public void setClientAuthenticatorType(String clientAuthenticatorType) { - entity.setClientAuthenticatorType(clientAuthenticatorType); - } - - @Override - public boolean validateSecret(String secret) { - return secret.equals(entity.getSecret()); - } - - @Override - public String getSecret() { - return entity.getSecret(); - } - - @Override - public void setSecret(String secret) { - entity.setSecret(secret); - } - - @Override - public boolean isPublicClient() { - return entity.isPublicClient(); - } - - @Override - public void setPublicClient(boolean flag) { - entity.setPublicClient(flag); - } - - - @Override - public boolean isFrontchannelLogout() { - return entity.isFrontchannelLogout(); - } - - @Override - public void setFrontchannelLogout(boolean flag) { - entity.setFrontchannelLogout(flag); - } - - @Override - public boolean isFullScopeAllowed() { - return entity.isFullScopeAllowed(); - } - - @Override - public void setFullScopeAllowed(boolean value) { - entity.setFullScopeAllowed(value); - } - - @Override - public RealmModel getRealm() { - return realm; - } - - @Override - public int getNotBefore() { - return entity.getNotBefore(); - } - - @Override - public void setNotBefore(int notBefore) { - entity.setNotBefore(notBefore); - } - - @Override - public Set getScopeMappings() { - return new HashSet(allScopeMappings.values()); - } - - @Override - public Set getRealmScopeMappings() { - Set allScopes = getScopeMappings(); - - Set realmRoles = new HashSet(); - for (RoleModel role : allScopes) { - RoleAdapter roleAdapter = (RoleAdapter)role; - if (roleAdapter.isRealmRole()) { - realmRoles.add(role); - } - } - return realmRoles; - } - - @Override - public void addScopeMapping(RoleModel role) { - allScopeMappings.put(role.getId(), role); - } - - @Override - public void deleteScopeMapping(RoleModel role) { - allScopeMappings.remove(role.getId()); - } - - @Override - public String getProtocol() { - return entity.getProtocol(); - } - - @Override - public void setProtocol(String protocol) { - entity.setProtocol(protocol); - - } - - @Override - public void setAttribute(String name, String value) { - entity.getAttributes().put(name, value); - - } - - @Override - public void removeAttribute(String name) { - entity.getAttributes().remove(name); - } - - @Override - public String getAttribute(String name) { - return entity.getAttributes().get(name); - } - - @Override - public Map getAttributes() { - Map copy = new HashMap(); - copy.putAll(entity.getAttributes()); - return copy; - } - - @Override - public Set getProtocolMappers() { - Set result = new HashSet(); - for (ProtocolMapperEntity entity : this.entity.getProtocolMappers()) { - ProtocolMapperModel model = getProtocolMapperById(entity.getId()); - if (model != null) result.add(model); - } - return result; - } - - @Override - public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { - if (getProtocolMapperByName(model.getProtocol(), model.getName()) != null) { - throw new RuntimeException("protocol mapper name must be unique per protocol"); - } - ProtocolMapperEntity entity = new ProtocolMapperEntity(); - String id = model.getId() != null ? model.getId() : KeycloakModelUtils.generateId(); - entity.setId(id); - entity.setProtocol(model.getProtocol()); - entity.setName(model.getName()); - entity.setProtocolMapper(model.getProtocolMapper()); - entity.setConfig(model.getConfig()); - entity.setConsentRequired(model.isConsentRequired()); - entity.setConsentText(model.getConsentText()); - this.entity.getProtocolMappers().add(entity); - return entityToModel(entity); - } - - @Override - public void removeProtocolMapper(ProtocolMapperModel mapping) { - ProtocolMapperEntity toBeRemoved = null; - for (ProtocolMapperEntity e : entity.getProtocolMappers()) { - if (e.getId().equals(mapping.getId())) { - toBeRemoved = e; - break; - } - } - - entity.getProtocolMappers().remove(toBeRemoved); - } - - @Override - public void updateProtocolMapper(ProtocolMapperModel mapping) { - ProtocolMapperEntity entity = getProtocolMapperEntityById(mapping.getId()); - entity.setProtocolMapper(mapping.getProtocolMapper()); - entity.setConsentRequired(mapping.isConsentRequired()); - entity.setConsentText(mapping.getConsentText()); - if (entity.getConfig() != null) { - entity.getConfig().clear(); - entity.getConfig().putAll(mapping.getConfig()); - } else { - entity.setConfig(mapping.getConfig()); - } - } - - protected ProtocolMapperEntity getProtocolMapperEntityById(String id) { - for (ProtocolMapperEntity e : entity.getProtocolMappers()) { - if (e.getId().equals(id)) { - return e; - } - } - return null; - } - - protected ProtocolMapperEntity getProtocolMapperEntityByName(String protocol, String name) { - for (ProtocolMapperEntity e : entity.getProtocolMappers()) { - if (e.getProtocol().equals(protocol) && e.getName().equals(name)) { - return e; - } - } - return null; - - } - - @Override - public ProtocolMapperModel getProtocolMapperById(String id) { - ProtocolMapperEntity entity = getProtocolMapperEntityById(id); - if (entity == null) return null; - return entityToModel(entity); - } - - @Override - public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) { - ProtocolMapperEntity entity = getProtocolMapperEntityByName(protocol, name); - if (entity == null) return null; - return entityToModel(entity); - } - - protected ProtocolMapperModel entityToModel(ProtocolMapperEntity entity) { - ProtocolMapperModel mapping = new ProtocolMapperModel(); - mapping.setId(entity.getId()); - mapping.setName(entity.getName()); - mapping.setProtocol(entity.getProtocol()); - mapping.setProtocolMapper(entity.getProtocolMapper()); - mapping.setConsentRequired(entity.isConsentRequired()); - mapping.setConsentText(entity.getConsentText()); - Map config = new HashMap(); - if (entity.getConfig() != null) config.putAll(entity.getConfig()); - mapping.setConfig(config); - return mapping; - } - - @Override - public String getClientId() { - return entity.getClientId(); - } - - @Override - public void setClientId(String clientId) { - if (appNameExists(clientId)) throw new ModelDuplicateException("Application named " + clientId + " already exists."); - entity.setClientId(clientId); - } - - private boolean appNameExists(String name) { - for (ClientModel app : realm.getClients()) { - if (app == this) continue; - if (app.getClientId().equals(name)) return true; - } - - return false; - } - - @Override - public boolean isSurrogateAuthRequired() { - return entity.isSurrogateAuthRequired(); - } - - @Override - public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { - entity.setSurrogateAuthRequired(surrogateAuthRequired); - } - - @Override - public String getManagementUrl() { - return entity.getManagementUrl(); - } - - @Override - public void setManagementUrl(String url) { - entity.setManagementUrl(url); - } - - @Override - public void setRootUrl(String url) { - entity.setRootUrl(url); - } - - @Override - public String getRootUrl() { - return entity.getRootUrl(); - } - - @Override - public void setBaseUrl(String url) { - entity.setBaseUrl(url); - } - - @Override - public String getBaseUrl() { - return entity.getBaseUrl(); - } - - @Override - public boolean isBearerOnly() { - return entity.isBearerOnly(); - } - - @Override - public void setBearerOnly(boolean only) { - entity.setBearerOnly(only); - } - - @Override - public boolean isConsentRequired() { - return entity.isConsentRequired(); - } - - @Override - public void setConsentRequired(boolean consentRequired) { - entity.setConsentRequired(consentRequired); - } - - @Override - public boolean isServiceAccountsEnabled() { - return entity.isServiceAccountsEnabled(); - } - - @Override - public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { - entity.setServiceAccountsEnabled(serviceAccountsEnabled); - } - - @Override - public boolean isDirectGrantsOnly() { - return entity.isDirectGrantsOnly(); - } - - @Override - public void setDirectGrantsOnly(boolean flag) { - entity.setDirectGrantsOnly(flag); - } - - @Override - public RoleAdapter getRole(String name) { - for (RoleAdapter role : allRoles.values()) { - if (role.getName().equals(name)) return role; - } - return null; - } - - @Override - public RoleAdapter addRole(String name) { - return this.addRole(KeycloakModelUtils.generateId(), name); - } - - @Override - public RoleAdapter addRole(String id, String name) { - if (roleNameExists(name)) throw new ModelDuplicateException("Role named " + name + " already exists."); - RoleEntity roleEntity = new RoleEntity(); - roleEntity.setId(id); - roleEntity.setName(name); - roleEntity.setClientId(getId()); - - RoleAdapter role = new RoleAdapter(getRealm(), roleEntity, this); - allRoles.put(id, role); - - return role; - } - - private boolean roleNameExists(String name) { - for (RoleModel role : allRoles.values()) { - if (role.getName().equals(name)) return true; - } - - return false; - } - - @Override - public boolean removeRole(RoleModel role) { - boolean removed = (allRoles.remove(role.getId()) != null); - - // remove application roles from users - for (UserModel user : inMemoryModel.getUsers(realm.getId())) { - user.deleteRoleMapping(role); - } - - // delete scope mappings from applications - for (ClientModel app : realm.getClients()) { - app.deleteScopeMapping(role); - } - - // remove role from the realm - realm.removeRole(role); - - this.deleteScopeMapping(role); - - return removed; - } - - @Override - public Set getRoles() { - return new HashSet(allRoles.values()); - } - - @Override - public boolean hasScope(RoleModel role) { - if (isFullScopeAllowed()) return true; - Set roles = getScopeMappings(); - if (roles.contains(role)) return true; - - for (RoleModel mapping : roles) { - if (mapping.hasRole(role)) return true; - } - roles = getRoles(); - if (roles.contains(role)) return true; - - for (RoleModel mapping : roles) { - if (mapping.hasRole(role)) return true; - } - return false; - } - - @Override - public Set getClientScopeMappings(ClientModel client) { - Set allScopes = client.getScopeMappings(); - - Set appRoles = new HashSet(); - for (RoleModel role : allScopes) { - RoleAdapter roleAdapter = (RoleAdapter)role; - if (getId().equals(roleAdapter.getRoleEntity().getClientId())) { - appRoles.add(role); - } - } - return appRoles; - } - - @Override - public List getDefaultRoles() { - return entity.getDefaultRoles(); - } - - @Override - public void addDefaultRole(String name) { - RoleModel role = getRole(name); - if (role == null) { - addRole(name); - } - - List defaultRoles = getDefaultRoles(); - if (defaultRoles.contains(name)) return; - - String[] defaultRoleNames = defaultRoles.toArray(new String[defaultRoles.size() + 1]); - defaultRoleNames[defaultRoleNames.length - 1] = name; - updateDefaultRoles(defaultRoleNames); - } - - @Override - public void updateDefaultRoles(String[] defaultRoles) { - List roleNames = new ArrayList(); - for (String roleName : defaultRoles) { - RoleModel role = getRole(roleName); - if (role == null) { - addRole(roleName); - } - - roleNames.add(roleName); - } - - entity.setDefaultRoles(roleNames); - } - - @Override - public int getNodeReRegistrationTimeout() { - return entity.getNodeReRegistrationTimeout(); - } - - @Override - public void setNodeReRegistrationTimeout(int timeout) { - entity.setNodeReRegistrationTimeout(timeout); - } - - @Override - public Map getRegisteredNodes() { - return entity.getRegisteredNodes() == null ? Collections.emptyMap() : Collections.unmodifiableMap(entity.getRegisteredNodes()); - } - - @Override - public void registerNode(String nodeHost, int registrationTime) { - if (entity.getRegisteredNodes() == null) { - entity.setRegisteredNodes(new HashMap()); - } - - entity.getRegisteredNodes().put(nodeHost, registrationTime); - } - - @Override - public void unregisterNode(String nodeHost) { - if (entity.getRegisteredNodes() == null) return; - - entity.getRegisteredNodes().remove(nodeHost); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !(o instanceof ClientModel)) return false; - - ClientModel that = (ClientModel) o; - return that.getId().equals(getId()); - } - - @Override - public int hashCode() { - return getId().hashCode(); - } -} diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/GroupAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/GroupAdapter.java deleted file mode 100755 index 14f92cb45e..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/GroupAdapter.java +++ /dev/null @@ -1,213 +0,0 @@ -package org.keycloak.models.file.adapter; - -import org.keycloak.models.ClientModel; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.entities.GroupEntity; -import org.keycloak.models.utils.KeycloakModelUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * - * @author Marek Posolda - */ -public class GroupAdapter implements GroupModel { - - private final GroupEntity group; - private RealmModel realm; - private KeycloakSession session; - - public GroupAdapter(KeycloakSession session, RealmModel realm, GroupEntity group) { - this.group = group; - this.realm = realm; - this.session = session; - } - - @Override - public String getId() { - return group.getId(); - } - - @Override - public String getName() { - return group.getName(); - } - - @Override - public void setName(String name) { - group.setName(name); - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !(o instanceof GroupModel)) return false; - - GroupModel that = (GroupModel) o; - return that.getId().equals(getId()); - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public void setSingleAttribute(String name, String value) { - if (group.getAttributes() == null) { - group.setAttributes(new HashMap>()); - } - - List attrValues = new ArrayList<>(); - attrValues.add(value); - group.getAttributes().put(name, attrValues); - } - - @Override - public void setAttribute(String name, List values) { - if (group.getAttributes() == null) { - group.setAttributes(new HashMap>()); - } - - group.getAttributes().put(name, values); - } - - @Override - public void removeAttribute(String name) { - if (group.getAttributes() == null) return; - - group.getAttributes().remove(name); - } - - @Override - public String getFirstAttribute(String name) { - if (group.getAttributes()==null) return null; - - List attrValues = group.getAttributes().get(name); - return (attrValues==null || attrValues.isEmpty()) ? null : attrValues.get(0); - } - - @Override - public List getAttribute(String name) { - if (group.getAttributes()==null) return Collections.emptyList(); - List attrValues = group.getAttributes().get(name); - return (attrValues == null) ? Collections.emptyList() : Collections.unmodifiableList(attrValues); - } - - @Override - public Map> getAttributes() { - return group.getAttributes()==null ? Collections.>emptyMap() : Collections.unmodifiableMap((Map) group.getAttributes()); - } - - @Override - public boolean hasRole(RoleModel role) { - Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); - } - - @Override - public void grantRole(RoleModel role) { - if (group.getRoleIds() == null) { - group.setRoleIds(new LinkedList()); - } - if (group.getRoleIds().contains(role.getId())) { - return; - } - group.getRoleIds().add(role.getId()); - } - - @Override - public Set getRoleMappings() { - if (group.getRoleIds() == null || group.getRoleIds().isEmpty()) return Collections.EMPTY_SET; - Set roles = new HashSet<>(); - for (String id : group.getRoleIds()) { - roles.add(realm.getRoleById(id)); - } - return roles; - } - - @Override - public Set getRealmRoleMappings() { - Set allRoles = getRoleMappings(); - - // Filter to retrieve just realm roles - Set realmRoles = new HashSet(); - for (RoleModel role : allRoles) { - if (role.getContainer() instanceof RealmModel) { - realmRoles.add(role); - } - } - return realmRoles; - } - - @Override - public void deleteRoleMapping(RoleModel role) { - if (group == null || role == null) return; - if (group.getRoleIds() == null) return; - group.getRoleIds().remove(role.getId()); - } - - @Override - public Set getClientRoleMappings(ClientModel app) { - Set result = new HashSet(); - Set roles = getRoleMappings(); - - for (RoleModel role : roles) { - if (app.equals(role.getContainer())) { - result.add(role); - } - } - return result; - } - - @Override - public GroupModel getParent() { - if (group.getParentId() == null) return null; - return realm.getGroupById(group.getParentId()); - } - - @Override - public String getParentId() { - return group.getParentId(); - } - - @Override - public Set getSubGroups() { - Set subGroups = new HashSet<>(); - for (GroupModel groupModel : realm.getGroups()) { - if (groupModel.getParent().equals(this)) { - subGroups.add(groupModel); - } - } - return subGroups; - } - - @Override - public void setParent(GroupModel group) { - this.group.setParentId(group.getId()); - - } - - @Override - public void addChild(GroupModel subGroup) { - subGroup.setParent(this); - - } - - @Override - public void removeChild(GroupModel subGroup) { - subGroup.setParent(null); - - } -} diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/MigrationModelAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/MigrationModelAdapter.java deleted file mode 100755 index 13695db46b..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/MigrationModelAdapter.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.keycloak.models.file.adapter; - -import org.keycloak.connections.file.InMemoryModel; -import org.keycloak.migration.MigrationModel; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class MigrationModelAdapter implements MigrationModel { - protected InMemoryModel em; - - public MigrationModelAdapter(InMemoryModel em) { - this.em = em; - } - - @Override - public String getStoredVersion() { - return em.getModelVersion(); - } - - @Override - public void setStoredVersion(String version) { - em.setModelVersion(version); - } -} diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java deleted file mode 100755 index 643c3c8560..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java +++ /dev/null @@ -1,1840 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file.adapter; - -import org.keycloak.connections.file.InMemoryModel; -import org.keycloak.common.enums.SslRequired; -import org.keycloak.models.AuthenticationExecutionModel; -import org.keycloak.models.AuthenticationFlowModel; -import org.keycloak.models.AuthenticatorConfigModel; -import org.keycloak.models.ClientModel; -import org.keycloak.models.GroupModel; -import org.keycloak.models.IdentityProviderMapperModel; -import org.keycloak.models.IdentityProviderModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.OTPPolicy; -import org.keycloak.models.PasswordPolicy; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RequiredActionProviderModel; -import org.keycloak.models.RequiredCredentialModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserFederationMapperEventImpl; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProviderCreationEventImpl; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.entities.AuthenticationExecutionEntity; -import org.keycloak.models.entities.AuthenticationFlowEntity; -import org.keycloak.models.entities.AuthenticatorConfigEntity; -import org.keycloak.models.entities.ClientEntity; -import org.keycloak.models.entities.IdentityProviderMapperEntity; -import org.keycloak.models.entities.RealmEntity; -import org.keycloak.models.entities.RequiredActionProviderEntity; -import org.keycloak.models.entities.RequiredCredentialEntity; -import org.keycloak.models.entities.RoleEntity; -import org.keycloak.models.entities.UserFederationMapperEntity; -import org.keycloak.models.entities.UserFederationProviderEntity; -import org.keycloak.models.utils.KeycloakModelUtils; - -import java.security.Key; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * RealmModel for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class RealmAdapter implements RealmModel { - - private final InMemoryModel inMemoryModel; - private final RealmEntity realm; - - protected volatile transient PublicKey publicKey; - protected volatile transient PrivateKey privateKey; - protected volatile transient X509Certificate certificate; - protected volatile transient Key codeSecretKey; - - private volatile transient PasswordPolicy passwordPolicy; - private volatile transient OTPPolicy otpPolicy; - private volatile transient KeycloakSession session; - - private final Map allApps = new HashMap(); - private ClientModel masterAdminApp = null; - private final Map allRoles = new HashMap(); - private final Map allGroups = new HashMap(); - private final Map allIdProviders = new HashMap(); - - public RealmAdapter(KeycloakSession session, RealmEntity realm, InMemoryModel inMemoryModel) { - this.session = session; - this.realm = realm; - this.inMemoryModel = inMemoryModel; - } - - public RealmEntity getRealmEnity() { - return realm; - } - - @Override - public String getId() { - return realm.getId(); - } - - @Override - public String getName() { - return realm.getName(); - } - - @Override - public void setName(String name) { - if (getName() == null) { - realm.setName(name); - return; - } - - if (getName().equals(name)) return; // allow setting name to same value - - if (inMemoryModel.getRealmByName(name) != null) throw new ModelDuplicateException("Realm " + name + " already exists."); - realm.setName(name); - } - - @Override - public boolean isEnabled() { - return realm.isEnabled(); - } - - @Override - public void setEnabled(boolean enabled) { - realm.setEnabled(enabled); - } - - @Override - public SslRequired getSslRequired() { - return SslRequired.valueOf(realm.getSslRequired()); - } - - @Override - public void setSslRequired(SslRequired sslRequired) { - realm.setSslRequired(sslRequired.name()); - } - - @Override - public boolean isRegistrationAllowed() { - return realm.isRegistrationAllowed(); - } - - @Override - public void setRegistrationAllowed(boolean registrationAllowed) { - realm.setRegistrationAllowed(registrationAllowed); - } - - @Override - public boolean isRegistrationEmailAsUsername() { - return realm.isRegistrationEmailAsUsername(); - } - - @Override - public void setRegistrationEmailAsUsername(boolean registrationEmailAsUsername) { - realm.setRegistrationEmailAsUsername(registrationEmailAsUsername); - } - - @Override - public boolean isRememberMe() { - return realm.isRememberMe(); - } - - @Override - public void setRememberMe(boolean rememberMe) { - realm.setRememberMe(rememberMe); - } - - @Override - public boolean isBruteForceProtected() { - return realm.isBruteForceProtected(); - } - - @Override - public void setBruteForceProtected(boolean value) { - realm.setBruteForceProtected(value); - } - - @Override - public int getMaxFailureWaitSeconds() { - return realm.getMaxFailureWaitSeconds(); - } - - @Override - public void setMaxFailureWaitSeconds(int val) { - realm.setMaxFailureWaitSeconds(val); - } - - @Override - public int getWaitIncrementSeconds() { - return realm.getWaitIncrementSeconds(); - } - - @Override - public void setWaitIncrementSeconds(int val) { - realm.setWaitIncrementSeconds(val); - } - - @Override - public long getQuickLoginCheckMilliSeconds() { - return realm.getQuickLoginCheckMilliSeconds(); - } - - @Override - public void setQuickLoginCheckMilliSeconds(long val) { - realm.setQuickLoginCheckMilliSeconds(val); - } - - @Override - public int getMinimumQuickLoginWaitSeconds() { - return realm.getMinimumQuickLoginWaitSeconds(); - } - - @Override - public void setMinimumQuickLoginWaitSeconds(int val) { - realm.setMinimumQuickLoginWaitSeconds(val); - } - - - @Override - public int getMaxDeltaTimeSeconds() { - return realm.getMaxDeltaTimeSeconds(); - } - - @Override - public void setMaxDeltaTimeSeconds(int val) { - realm.setMaxDeltaTimeSeconds(val); - } - - @Override - public int getFailureFactor() { - return realm.getFailureFactor(); - } - - @Override - public void setFailureFactor(int failureFactor) { - realm.setFailureFactor(failureFactor); - } - - - @Override - public boolean isVerifyEmail() { - return realm.isVerifyEmail(); - } - - @Override - public void setVerifyEmail(boolean verifyEmail) { - realm.setVerifyEmail(verifyEmail); - } - - @Override - public boolean isResetPasswordAllowed() { - return realm.isResetPasswordAllowed(); - } - - @Override - public void setResetPasswordAllowed(boolean resetPassword) { - realm.setResetPasswordAllowed(resetPassword); - } - - @Override - public boolean isEditUsernameAllowed() { - return realm.isEditUsernameAllowed(); - } - - @Override - public void setEditUsernameAllowed(boolean editUsernameAllowed) { - realm.setEditUsernameAllowed(editUsernameAllowed); - } - - @Override - public PasswordPolicy getPasswordPolicy() { - if (passwordPolicy == null) { - passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy()); - } - return passwordPolicy; - } - - @Override - public void setPasswordPolicy(PasswordPolicy policy) { - this.passwordPolicy = policy; - realm.setPasswordPolicy(policy.toString()); - } - - @Override - public OTPPolicy getOTPPolicy() { - if (otpPolicy == null) { - otpPolicy = new OTPPolicy(); - otpPolicy.setDigits(realm.getOtpPolicyDigits()); - otpPolicy.setAlgorithm(realm.getOtpPolicyAlgorithm()); - otpPolicy.setInitialCounter(realm.getOtpPolicyInitialCounter()); - otpPolicy.setLookAheadWindow(realm.getOtpPolicyLookAheadWindow()); - otpPolicy.setType(realm.getOtpPolicyType()); - otpPolicy.setPeriod(realm.getOtpPolicyPeriod()); - } - return otpPolicy; - } - - @Override - public void setOTPPolicy(OTPPolicy policy) { - realm.setOtpPolicyAlgorithm(policy.getAlgorithm()); - realm.setOtpPolicyDigits(policy.getDigits()); - realm.setOtpPolicyInitialCounter(policy.getInitialCounter()); - realm.setOtpPolicyLookAheadWindow(policy.getLookAheadWindow()); - realm.setOtpPolicyType(policy.getType()); - realm.setOtpPolicyPeriod(policy.getPeriod()); - - } - - @Override - public int getNotBefore() { - return realm.getNotBefore(); - } - - @Override - public void setNotBefore(int notBefore) { - realm.setNotBefore(notBefore); - } - - - @Override - public boolean isRevokeRefreshToken() { - return realm.isRevokeRefreshToken(); - } - - @Override - public void setRevokeRefreshToken(boolean revokeRefreshToken) { - realm.setRevokeRefreshToken(revokeRefreshToken); - } - - @Override - public int getSsoSessionIdleTimeout() { - return realm.getSsoSessionIdleTimeout(); - } - - @Override - public void setSsoSessionIdleTimeout(int seconds) { - realm.setSsoSessionIdleTimeout(seconds); - } - - @Override - public int getSsoSessionMaxLifespan() { - return realm.getSsoSessionMaxLifespan(); - } - - @Override - public void setSsoSessionMaxLifespan(int seconds) { - realm.setSsoSessionMaxLifespan(seconds); - } - - @Override - public int getOfflineSessionIdleTimeout() { - return realm.getOfflineSessionIdleTimeout(); - } - - @Override - public void setOfflineSessionIdleTimeout(int seconds) { - realm.setOfflineSessionIdleTimeout(seconds); - } - - @Override - public int getAccessTokenLifespan() { - return realm.getAccessTokenLifespan(); - } - - @Override - public void setAccessTokenLifespan(int tokenLifespan) { - realm.setAccessTokenLifespan(tokenLifespan); - } - - @Override - public int getAccessCodeLifespan() { - return realm.getAccessCodeLifespan(); - } - - @Override - public void setAccessCodeLifespan(int accessCodeLifespan) { - realm.setAccessCodeLifespan(accessCodeLifespan); - } - - @Override - public int getAccessCodeLifespanUserAction() { - return realm.getAccessCodeLifespanUserAction(); - } - - @Override - public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) { - realm.setAccessCodeLifespanUserAction(accessCodeLifespanUserAction); - } - - @Override - public String getPublicKeyPem() { - return realm.getPublicKeyPem(); - } - - @Override - public void setPublicKeyPem(String publicKeyPem) { - realm.setPublicKeyPem(publicKeyPem); - this.publicKey = null; - } - - @Override - public X509Certificate getCertificate() { - if (certificate != null) return certificate; - certificate = KeycloakModelUtils.getCertificate(getCertificatePem()); - return certificate; - } - - @Override - public void setCertificate(X509Certificate certificate) { - this.certificate = certificate; - String certificatePem = KeycloakModelUtils.getPemFromCertificate(certificate); - setCertificatePem(certificatePem); - } - - @Override - public String getCertificatePem() { - return realm.getCertificatePem(); - } - - @Override - public void setCertificatePem(String certificate) { - realm.setCertificatePem(certificate); - - } - - - @Override - public String getPrivateKeyPem() { - return realm.getPrivateKeyPem(); - } - - @Override - public void setPrivateKeyPem(String privateKeyPem) { - realm.setPrivateKeyPem(privateKeyPem); - this.privateKey = null; - } - - @Override - public PublicKey getPublicKey() { - if (publicKey != null) return publicKey; - publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem()); - return publicKey; - } - - @Override - public void setPublicKey(PublicKey publicKey) { - this.publicKey = publicKey; - String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey); - setPublicKeyPem(publicKeyPem); - } - - @Override - public PrivateKey getPrivateKey() { - if (privateKey != null) return privateKey; - privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem()); - return privateKey; - } - - @Override - public void setPrivateKey(PrivateKey privateKey) { - this.privateKey = privateKey; - String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey); - setPrivateKeyPem(privateKeyPem); - } - - @Override - public String getCodeSecret() { - return realm.getCodeSecret(); - } - - @Override - public Key getCodeSecretKey() { - if (codeSecretKey == null) { - codeSecretKey = KeycloakModelUtils.getSecretKey(getCodeSecret()); - } - return codeSecretKey; - } - - @Override - public void setCodeSecret(String codeSecret) { - realm.setCodeSecret(codeSecret); - } - - @Override - public String getLoginTheme() { - return realm.getLoginTheme(); - } - - @Override - public void setLoginTheme(String name) { - realm.setLoginTheme(name); - } - - @Override - public String getAccountTheme() { - return realm.getAccountTheme(); - } - - @Override - public void setAccountTheme(String name) { - realm.setAccountTheme(name); - } - - @Override - public String getAdminTheme() { - return realm.getAdminTheme(); - } - - @Override - public void setAdminTheme(String name) { - realm.setAdminTheme(name); - } - - @Override - public String getEmailTheme() { - return realm.getEmailTheme(); - } - - @Override - public void setEmailTheme(String name) { - realm.setEmailTheme(name); - } - - @Override - public RoleAdapter getRole(String name) { - for (RoleAdapter role : allRoles.values()) { - if (role.getName().equals(name)) return role; - } - return null; - } - - @Override - public RoleModel addRole(String name) { - return this.addRole(KeycloakModelUtils.generateId(), name); - } - - @Override - public RoleModel addRole(String id, String name) { - if (id == null) throw new NullPointerException("id == null"); - if (name == null) throw new NullPointerException("name == null"); - if (hasRoleWithName(name)) throw new ModelDuplicateException("Realm already contains role with name " + name + "."); - - RoleEntity roleEntity = new RoleEntity(); - roleEntity.setId(id); - roleEntity.setName(name); - roleEntity.setRealmId(getId()); - - RoleAdapter roleModel = new RoleAdapter(this, roleEntity, this); - allRoles.put(id, roleModel); - return roleModel; - } - - @Override - public boolean removeRole(RoleModel role) { - return removeRoleById(role.getId()); - } - - @Override - public boolean removeRoleById(String id) { - if (id == null) throw new NullPointerException("id == null"); - - // try realm roles first - if (allRoles.remove(id) != null) return true; - - for (ClientModel app : getClients()) { - for (RoleModel appRole : app.getRoles()) { - if (id.equals(appRole.getId())) { - app.removeRole(appRole); - return true; - } - } - } - - return false; - } - - @Override - public Set getRoles() { - return new HashSet(allRoles.values()); - } - - @Override - public RoleModel getRoleById(String id) { - RoleModel found = allRoles.get(id); - if (found != null) return found; - - for (ClientModel app : getClients()) { - for (RoleModel appRole : app.getRoles()) { - if (appRole.getId().equals(id)) return appRole; - } - } - - return null; - } - - @Override - public GroupModel getGroupById(String id) { - GroupModel found = allGroups.get(id); - if (found != null) return found; - return null; - } - - @Override - public void moveGroup(GroupModel group, GroupModel toParent) { - if (group.getParentId() != null) { - group.getParent().removeChild(group); - } - group.setParent(toParent); - if (toParent != null) toParent.addChild(group); - else addTopLevelGroup(group); - } - @Override - public List getGroups() { - List list = new LinkedList<>(); - for (GroupAdapter group : allGroups.values()) { - list.add(group); - } - return list; - } - - @Override - public List getTopLevelGroups() { - List list = new LinkedList<>(); - for (GroupAdapter group : allGroups.values()) { - if (group.getParent() == null) list.add(group); - } - return list; - } - - @Override - public boolean removeGroup(GroupModel group) { - return allGroups.remove(group.getId()) != null; - } - - @Override - public List getDefaultRoles() { - return realm.getDefaultRoles(); - } - - @Override - public void addDefaultRole(String name) { - RoleModel role = getRole(name); - if (role == null) { - addRole(name); - } - - List roleNames = getDefaultRoles(); - if (roleNames.contains(name)) throw new IllegalArgumentException("Realm " + realm.getName() + " already contains default role named " + name); - - roleNames.add(name); - realm.setDefaultRoles(roleNames); - } - - boolean hasRoleWithName(String name) { - for (RoleModel role : allRoles.values()) { - if (role.getName().equals(name)) return true; - } - - return false; - } - - @Override - public void updateDefaultRoles(String[] defaultRoles) { - List roleNames = new ArrayList(); - for (String roleName : defaultRoles) { - RoleModel role = getRole(roleName); - if (role == null) { - addRole(roleName); - } - - roleNames.add(roleName); - } - - realm.setDefaultRoles(roleNames); - } - - @Override - public ClientModel getClientById(String id) { - return allApps.get(id); - } - - @Override - public ClientModel getClientByClientId(String clientId) { - for (ClientModel app : getClients()) { - if (app.getClientId().equals(clientId)) return app; - } - - return null; - } - - @Override - public Map getClientNameMap() { - Map resourceMap = new HashMap(); - for (ClientModel resource : getClients()) { - resourceMap.put(resource.getClientId(), resource); - } - return resourceMap; - } - - @Override - public List getClients() { - return new ArrayList(allApps.values()); - } - - @Override - public ClientModel addClient(String name) { - return this.addClient(KeycloakModelUtils.generateId(), name); - } - - @Override - public ClientModel addClient(String id, String clientId) { - if (clientId == null) throw new NullPointerException("name == null"); - if (id == null) throw new NullPointerException("id == null"); - - if (getClientNameMap().containsKey(clientId)) { - throw new ModelDuplicateException("Application named '" + clientId + "' already exists."); - } - - ClientEntity appEntity = new ClientEntity(); - appEntity.setId(id); - appEntity.setClientId(clientId); - appEntity.setRealmId(getId()); - appEntity.setEnabled(true); - - final ClientModel app = new ClientAdapter(session, this, appEntity, inMemoryModel); - session.getKeycloakSessionFactory().publish(new ClientCreationEvent() { - @Override - public ClientModel getCreatedClient() { - return app; - } - }); - - allApps.put(id, app); - - return app; - } - - @Override - public boolean removeClient(String id) { - ClientModel appToBeRemoved = this.getClientById(id); - if (appToBeRemoved == null) return false; - - // remove any composite role assignments for this app - for (RoleModel role : this.getRoles()) { - RoleAdapter roleAdapter = (RoleAdapter)role; - roleAdapter.removeApplicationComposites(id); - } - - for (RoleModel role : appToBeRemoved.getRoles()) { - appToBeRemoved.removeRole(role); - } - - return (allApps.remove(id) != null); - } - - boolean hasUserWithEmail(String email) { - for (UserModel user : inMemoryModel.getUsers(getId())) { - if (user.getEmail() == null) continue; - if (user.getEmail().equals(email)) return true; - } - - return false; - } - - @Override - public void addRequiredCredential(String type) { - if (type == null) throw new NullPointerException("Credential type can not be null"); - - RequiredCredentialModel credentialModel = initRequiredCredentialModel(type); - - List requiredCredList = realm.getRequiredCredentials(); - for (RequiredCredentialEntity cred : requiredCredList) { - if (type.equals(cred.getType())) return; - } - - addRequiredCredential(credentialModel, requiredCredList); - } - - protected void addRequiredCredential(RequiredCredentialModel credentialModel, List persistentCollection) { - RequiredCredentialEntity credEntity = new RequiredCredentialEntity(); - credEntity.setType(credentialModel.getType()); - credEntity.setFormLabel(credentialModel.getFormLabel()); - credEntity.setInput(credentialModel.isInput()); - credEntity.setSecret(credentialModel.isSecret()); - - persistentCollection.add(credEntity); - } - - @Override - public void updateRequiredCredentials(Set creds) { - updateRequiredCredentials(creds, realm.getRequiredCredentials()); - } - - protected void updateRequiredCredentials(Set creds, List credsEntities) { - Set already = new HashSet(); - Set toRemove = new HashSet(); - for (RequiredCredentialEntity entity : credsEntities) { - if (!creds.contains(entity.getType())) { - toRemove.add(entity); - } else { - already.add(entity.getType()); - } - } - for (RequiredCredentialEntity entity : toRemove) { - credsEntities.remove(entity); - } - for (String cred : creds) { - if (!already.contains(cred)) { - RequiredCredentialModel credentialModel = initRequiredCredentialModel(cred); - addRequiredCredential(credentialModel, credsEntities); - } - } - } - - @Override - public List getRequiredCredentials() { - return convertRequiredCredentialEntities(realm.getRequiredCredentials()); - } - - protected List convertRequiredCredentialEntities(Collection credEntities) { - - List result = new ArrayList(); - for (RequiredCredentialEntity entity : credEntities) { - RequiredCredentialModel credentialModel = new RequiredCredentialModel(); - credentialModel.setFormLabel(entity.getFormLabel()); - credentialModel.setInput(entity.isInput()); - credentialModel.setSecret(entity.isSecret()); - credentialModel.setType(entity.getType()); - - result.add(credentialModel); - } - return result; - } - - protected RequiredCredentialModel initRequiredCredentialModel(String type) { - RequiredCredentialModel credentialModel = RequiredCredentialModel.BUILT_IN.get(type); - if (credentialModel == null) { - throw new RuntimeException("Unknown credential type " + type); - } - return credentialModel; - } - - @Override - public Map getBrowserSecurityHeaders() { - return realm.getBrowserSecurityHeaders(); - } - - @Override - public void setBrowserSecurityHeaders(Map headers) { - realm.setBrowserSecurityHeaders(headers); - } - - @Override - public Map getSmtpConfig() { - return realm.getSmtpConfig(); - } - - @Override - public void setSmtpConfig(Map smtpConfig) { - realm.setSmtpConfig(smtpConfig); - } - - @Override - public List getIdentityProviders() { - return new ArrayList(allIdProviders.values()); - } - - @Override - public IdentityProviderModel getIdentityProviderByAlias(String alias) { - for (IdentityProviderModel identityProviderModel : getIdentityProviders()) { - if (identityProviderModel.getAlias().equals(alias)) { - return identityProviderModel; - } - } - - return null; - } - - @Override - public void addIdentityProvider(IdentityProviderModel identityProvider) { - if (identityProvider.getAlias() == null) throw new NullPointerException("identityProvider.getAlias() == null"); - if (identityProvider.getInternalId() == null) identityProvider.setInternalId(KeycloakModelUtils.generateId()); - allIdProviders.put(identityProvider.getInternalId(), identityProvider); - } - - @Override - public void removeIdentityProviderByAlias(String alias) { - for (IdentityProviderModel provider : getIdentityProviders()) { - if (provider.getAlias().equals(alias)) { - allIdProviders.remove(provider.getInternalId()); - break; - } - } - } - - @Override - public void updateIdentityProvider(IdentityProviderModel identityProvider) { - removeIdentityProviderByAlias(identityProvider.getAlias()); - addIdentityProvider(identityProvider); - } - - @Override - public UserFederationProviderModel addUserFederationProvider(String providerName, Map config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) { - KeycloakModelUtils.ensureUniqueDisplayName(displayName, null, getUserFederationProviders()); - - UserFederationProviderEntity entity = new UserFederationProviderEntity(); - entity.setId(KeycloakModelUtils.generateId()); - entity.setPriority(priority); - entity.setProviderName(providerName); - entity.setConfig(config); - if (displayName == null) { - displayName = entity.getId(); - } - entity.setDisplayName(displayName); - entity.setFullSyncPeriod(fullSyncPeriod); - entity.setChangedSyncPeriod(changedSyncPeriod); - entity.setLastSync(lastSync); - realm.getUserFederationProviders().add(entity); - - UserFederationProviderModel providerModel = new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync); - - session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, providerModel)); - - return providerModel; - } - - @Override - public void removeUserFederationProvider(UserFederationProviderModel provider) { - Iterator it = realm.getUserFederationProviders().iterator(); - while (it.hasNext()) { - UserFederationProviderEntity entity = it.next(); - if (entity.getId().equals(provider.getId())) { - session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(), - entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync())); - - Set mappers = getUserFederationMapperEntitiesByFederationProvider(provider.getId()); - for (UserFederationMapperEntity mapper : mappers) { - realm.getUserFederationMappers().remove(mapper); - } - - it.remove(); - } - } - } - - @Override - public void updateUserFederationProvider(UserFederationProviderModel model) { - KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), model, getUserFederationProviders()); - - Iterator it = realm.getUserFederationProviders().iterator(); - while (it.hasNext()) { - UserFederationProviderEntity entity = it.next(); - if (entity.getId().equals(model.getId())) { - entity.setProviderName(model.getProviderName()); - entity.setConfig(model.getConfig()); - entity.setPriority(model.getPriority()); - String displayName = model.getDisplayName(); - if (displayName != null) { - entity.setDisplayName(model.getDisplayName()); - } - entity.setFullSyncPeriod(model.getFullSyncPeriod()); - entity.setChangedSyncPeriod(model.getChangedSyncPeriod()); - entity.setLastSync(model.getLastSync()); - } - } - } - - @Override - public List getUserFederationProviders() { - List entities = realm.getUserFederationProviders(); - List copy = new LinkedList(); - for (UserFederationProviderEntity entity : entities) { - copy.add(entity); - - } - Collections.sort(copy, new Comparator() { - - @Override - public int compare(UserFederationProviderEntity o1, UserFederationProviderEntity o2) { - return o1.getPriority() - o2.getPriority(); - } - - }); - List result = new LinkedList(); - for (UserFederationProviderEntity entity : copy) { - result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(), - entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync())); - } - - return result; - } - - @Override - public void setUserFederationProviders(List providers) { - for (UserFederationProviderModel currentProvider : providers) { - KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers); - } - - List entities = new LinkedList(); - for (UserFederationProviderModel model : providers) { - UserFederationProviderEntity entity = new UserFederationProviderEntity(); - if (model.getId() != null) { - entity.setId(model.getId()); - } else { - String id = KeycloakModelUtils.generateId(); - entity.setId(id); - model.setId(id); - } - entity.setProviderName(model.getProviderName()); - entity.setConfig(model.getConfig()); - entity.setPriority(model.getPriority()); - String displayName = model.getDisplayName(); - if (displayName == null) { - entity.setDisplayName(entity.getId()); - } - entity.setDisplayName(displayName); - entity.setFullSyncPeriod(model.getFullSyncPeriod()); - entity.setChangedSyncPeriod(model.getChangedSyncPeriod()); - entity.setLastSync(model.getLastSync()); - entities.add(entity); - - session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, model)); - } - - realm.setUserFederationProviders(entities); - } - - @Override - public boolean isEventsEnabled() { - return realm.isEventsEnabled(); - } - - @Override - public void setEventsEnabled(boolean enabled) { - realm.setEventsEnabled(enabled); - } - - @Override - public long getEventsExpiration() { - return realm.getEventsExpiration(); - } - - @Override - public void setEventsExpiration(long expiration) { - realm.setEventsExpiration(expiration); - } - - @Override - public Set getEventsListeners() { - return new HashSet(realm.getEventsListeners()); - } - - @Override - public void setEventsListeners(Set listeners) { - if (listeners != null) { - realm.setEventsListeners(new ArrayList(listeners)); - } else { - realm.setEventsListeners(Collections.EMPTY_LIST); - } - } - - @Override - public Set getEnabledEventTypes() { - return new HashSet(realm.getEnabledEventTypes()); - } - - @Override - public void setEnabledEventTypes(Set enabledEventTypes) { - if (enabledEventTypes != null) { - realm.setEnabledEventTypes(new ArrayList(enabledEventTypes)); - } else { - realm.setEnabledEventTypes(Collections.EMPTY_LIST); - } - } - - @Override - public boolean isAdminEventsEnabled() { - return realm.isAdminEventsEnabled(); - } - - @Override - public void setAdminEventsEnabled(boolean enabled) { - realm.setAdminEventsEnabled(enabled); - } - - @Override - public boolean isAdminEventsDetailsEnabled() { - return realm.isAdminEventsDetailsEnabled(); - } - - @Override - public void setAdminEventsDetailsEnabled(boolean enabled) { - realm.setAdminEventsDetailsEnabled(enabled); - } - - @Override - public ClientModel getMasterAdminClient() { - return this.masterAdminApp; - } - - @Override - public void setMasterAdminClient(ClientModel client) { - if (client == null) { - realm.setMasterAdminClient(null); - this.masterAdminApp = null; - } else { - String appId = client.getId(); - if (appId == null) { - throw new IllegalStateException("Master Admin app not initialized."); - } - realm.setMasterAdminClient(appId); - this.masterAdminApp = client; - } - } - - @Override - public boolean isIdentityFederationEnabled() { - //TODO: not sure if we will support identity federation storage for file - return getIdentityProviders() != null && !getIdentityProviders().isEmpty(); - } - - @Override - public int getAccessCodeLifespanLogin() { - return realm.getAccessCodeLifespanLogin(); - } - - @Override - public void setAccessCodeLifespanLogin(int accessCodeLifespanLogin) { - realm.setAccessCodeLifespanLogin(accessCodeLifespanLogin); - } - - @Override - public boolean isInternationalizationEnabled() { - return realm.isInternationalizationEnabled(); - } - - @Override - public void setInternationalizationEnabled(boolean enabled) { - realm.setInternationalizationEnabled(enabled); - } - - @Override - public Set getSupportedLocales() { - return new HashSet<>(realm.getSupportedLocales()); - } - - @Override - public void setSupportedLocales(Set locales) { - realm.setSupportedLocales(new ArrayList<>(locales)); - } - - @Override - public String getDefaultLocale() { - return realm.getDefaultLocale(); - } - - @Override - public void setDefaultLocale(String locale) { - realm.setDefaultLocale(locale); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !(o instanceof RealmModel)) return false; - - RealmModel that = (RealmModel) o; - return that.getId().equals(getId()); - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public Set getIdentityProviderMappers() { - Set mappings = new HashSet<>(); - for (IdentityProviderMapperEntity entity : this.realm.getIdentityProviderMappers()) { - IdentityProviderMapperModel mapping = entityToModel(entity); - mappings.add(mapping); - } - return mappings; - } - @Override - public Set getIdentityProviderMappersByAlias(String brokerAlias) { - Set mappings = new HashSet<>(); - for (IdentityProviderMapperEntity entity : this.realm.getIdentityProviderMappers()) { - if (!entity.getIdentityProviderAlias().equals(brokerAlias)) { - continue; - } - IdentityProviderMapperModel mapping = entityToModel(entity); - mappings.add(mapping); - } - return mappings; - } - - @Override - public IdentityProviderMapperModel addIdentityProviderMapper(IdentityProviderMapperModel model) { - if (getIdentityProviderMapperByName(model.getIdentityProviderAlias(), model.getIdentityProviderMapper()) != null) { - throw new RuntimeException("identity provider mapper name must be unique per identity provider"); - } - String id = KeycloakModelUtils.generateId(); - IdentityProviderMapperEntity entity = new IdentityProviderMapperEntity(); - entity.setId(id); - entity.setName(model.getName()); - entity.setIdentityProviderAlias(model.getIdentityProviderAlias()); - entity.setIdentityProviderMapper(model.getIdentityProviderMapper()); - entity.setConfig(model.getConfig()); - - this.realm.getIdentityProviderMappers().add(entity); - return entityToModel(entity); - } - - protected IdentityProviderMapperEntity getIdentityProviderMapperEntity(String id) { - for (IdentityProviderMapperEntity entity : this.realm.getIdentityProviderMappers()) { - if (entity.getId().equals(id)) { - return entity; - } - } - return null; - - } - - protected IdentityProviderMapperEntity getIdentityProviderMapperEntityByName(String alias, String name) { - for (IdentityProviderMapperEntity entity : this.realm.getIdentityProviderMappers()) { - if (entity.getIdentityProviderAlias().equals(alias) && entity.getName().equals(name)) { - return entity; - } - } - return null; - - } - - @Override - public void removeIdentityProviderMapper(IdentityProviderMapperModel mapping) { - IdentityProviderMapperEntity toDelete = getIdentityProviderMapperEntity(mapping.getId()); - if (toDelete != null) { - this.realm.getIdentityProviderMappers().remove(toDelete); - } - - } - - @Override - public void updateIdentityProviderMapper(IdentityProviderMapperModel mapping) { - IdentityProviderMapperEntity entity = getIdentityProviderMapperEntity(mapping.getId()); - entity.setIdentityProviderAlias(mapping.getIdentityProviderAlias()); - entity.setIdentityProviderMapper(mapping.getIdentityProviderMapper()); - if (entity.getConfig() == null) { - entity.setConfig(mapping.getConfig()); - } else { - entity.getConfig().clear(); - entity.getConfig().putAll(mapping.getConfig()); - } - - } - - @Override - public IdentityProviderMapperModel getIdentityProviderMapperById(String id) { - IdentityProviderMapperEntity entity = getIdentityProviderMapperEntity(id); - if (entity == null) return null; - return entityToModel(entity); - } - - @Override - public IdentityProviderMapperModel getIdentityProviderMapperByName(String alias, String name) { - IdentityProviderMapperEntity entity = getIdentityProviderMapperEntityByName(alias, name); - if (entity == null) return null; - return entityToModel(entity); - } - - protected IdentityProviderMapperModel entityToModel(IdentityProviderMapperEntity entity) { - IdentityProviderMapperModel mapping = new IdentityProviderMapperModel(); - mapping.setId(entity.getId()); - mapping.setName(entity.getName()); - mapping.setIdentityProviderAlias(entity.getIdentityProviderAlias()); - mapping.setIdentityProviderMapper(entity.getIdentityProviderMapper()); - Map config = new HashMap(); - if (entity.getConfig() != null) config.putAll(entity.getConfig()); - mapping.setConfig(config); - return mapping; - } - - @Override - public AuthenticationFlowModel getBrowserFlow() { - String flowId = realm.getBrowserFlow(); - if (flowId == null) return null; - return getAuthenticationFlowById(flowId); - } - - @Override - public void setBrowserFlow(AuthenticationFlowModel flow) { - realm.setBrowserFlow(flow.getId()); - - } - - @Override - public AuthenticationFlowModel getRegistrationFlow() { - String flowId = realm.getRegistrationFlow(); - if (flowId == null) return null; - return getAuthenticationFlowById(flowId); - } - - @Override - public void setRegistrationFlow(AuthenticationFlowModel flow) { - realm.setRegistrationFlow(flow.getId()); - - } - - @Override - public AuthenticationFlowModel getDirectGrantFlow() { - String flowId = realm.getDirectGrantFlow(); - if (flowId == null) return null; - return getAuthenticationFlowById(flowId); - } - - @Override - public void setDirectGrantFlow(AuthenticationFlowModel flow) { - realm.setDirectGrantFlow(flow.getId()); - - } - - @Override - public AuthenticationFlowModel getResetCredentialsFlow() { - String flowId = realm.getResetCredentialsFlow(); - if (flowId == null) return null; - return getAuthenticationFlowById(flowId); - } - - @Override - public void setResetCredentialsFlow(AuthenticationFlowModel flow) { - realm.setResetCredentialsFlow(flow.getId()); - } - - public AuthenticationFlowModel getClientAuthenticationFlow() { - String flowId = realm.getClientAuthenticationFlow(); - if (flowId == null) return null; - return getAuthenticationFlowById(flowId); - } - - - public void setClientAuthenticationFlow(AuthenticationFlowModel flow) { - realm.setClientAuthenticationFlow(flow.getId()); - } - - @Override - public List getAuthenticationFlows() { - List flows = realm.getAuthenticationFlows(); - if (flows.size() == 0) return Collections.EMPTY_LIST; - List models = new LinkedList<>(); - for (AuthenticationFlowEntity entity : flows) { - AuthenticationFlowModel model = entityToModel(entity); - models.add(model); - } - return models; - } - - - - @Override - public AuthenticationFlowModel getFlowByAlias(String alias) { - for (AuthenticationFlowModel flow : getAuthenticationFlows()) { - if (flow.getAlias().equals(alias)) { - return flow; - } - } - return null; - } - - - protected AuthenticationFlowModel entityToModel(AuthenticationFlowEntity entity) { - AuthenticationFlowModel model = new AuthenticationFlowModel(); - model.setId(entity.getId()); - model.setAlias(entity.getAlias()); - model.setDescription(entity.getDescription()); - model.setProviderId(entity.getProviderId()); - model.setBuiltIn(entity.isBuiltIn()); - model.setTopLevel(entity.isTopLevel()); - return model; - } - - @Override - public AuthenticationFlowModel getAuthenticationFlowById(String id) { - for (AuthenticationFlowModel model : getAuthenticationFlows()) { - if (model.getId().equals(id)) return model; - } - return null; - } - - protected AuthenticationFlowEntity getFlowEntity(String id) { - List flows = realm.getAuthenticationFlows(); - for (AuthenticationFlowEntity entity : flows) { - if (id.equals(entity.getId())) return entity; - } - return null; - - } - - @Override - public void removeAuthenticationFlow(AuthenticationFlowModel model) { - AuthenticationFlowEntity toDelete = getFlowEntity(model.getId()); - if (toDelete == null) return; - realm.getAuthenticationFlows().remove(toDelete); - } - - @Override - public void updateAuthenticationFlow(AuthenticationFlowModel model) { - AuthenticationFlowEntity toUpdate = getFlowEntity(model.getId()); - if (toUpdate == null) return; - toUpdate.setAlias(model.getAlias()); - toUpdate.setDescription(model.getDescription()); - toUpdate.setProviderId(model.getProviderId()); - toUpdate.setBuiltIn(model.isBuiltIn()); - toUpdate.setTopLevel(model.isTopLevel()); - - } - - @Override - public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) { - AuthenticationFlowEntity entity = new AuthenticationFlowEntity(); - String id = (model.getId() == null) ? KeycloakModelUtils.generateId(): model.getId(); - entity.setId(id); - entity.setAlias(model.getAlias()); - entity.setDescription(model.getDescription()); - entity.setProviderId(model.getProviderId()); - entity.setBuiltIn(model.isBuiltIn()); - entity.setTopLevel(model.isTopLevel()); - realm.getAuthenticationFlows().add(entity); - model.setId(entity.getId()); - return model; - } - - @Override - public List getAuthenticationExecutions(String flowId) { - AuthenticationFlowEntity flow = getFlowEntity(flowId); - if (flow == null) return Collections.EMPTY_LIST; - - List queryResult = flow.getExecutions(); - List executions = new LinkedList<>(); - for (AuthenticationExecutionEntity entity : queryResult) { - AuthenticationExecutionModel model = entityToModel(entity); - executions.add(model); - } - Collections.sort(executions, AuthenticationExecutionModel.ExecutionComparator.SINGLETON); - return executions; - } - - public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) { - AuthenticationExecutionModel model = new AuthenticationExecutionModel(); - model.setId(entity.getId()); - model.setRequirement(entity.getRequirement()); - model.setPriority(entity.getPriority()); - model.setAuthenticator(entity.getAuthenticator()); - model.setParentFlow(entity.getParentFlow()); - model.setFlowId(entity.getFlowId()); - model.setAuthenticatorFlow(entity.isAuthenticatorFlow()); - model.setAuthenticatorConfig(entity.getAuthenticatorConfig()); - return model; - } - - @Override - public AuthenticationExecutionModel getAuthenticationExecutionById(String id) { - AuthenticationExecutionEntity execution = getAuthenticationExecutionEntity(id); - return entityToModel(execution); - } - - public AuthenticationExecutionEntity getAuthenticationExecutionEntity(String id) { - List flows = realm.getAuthenticationFlows(); - for (AuthenticationFlowEntity entity : flows) { - for (AuthenticationExecutionEntity exe : entity.getExecutions()) { - if (exe.getId().equals(id)) { - return exe; - } - } - } - return null; - } - - @Override - public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) { - AuthenticationExecutionEntity entity = new AuthenticationExecutionEntity(); - String id = (model.getId() == null) ? KeycloakModelUtils.generateId(): model.getId(); - entity.setId(id); - entity.setAuthenticator(model.getAuthenticator()); - entity.setPriority(model.getPriority()); - entity.setRequirement(model.getRequirement()); - entity.setAuthenticatorFlow(model.isAuthenticatorFlow()); - entity.setFlowId(model.getFlowId()); - entity.setAuthenticatorConfig(model.getAuthenticatorConfig()); - AuthenticationFlowEntity flow = getFlowEntity(model.getId()); - flow.getExecutions().add(entity); - model.setId(entity.getId()); - return model; - - } - - @Override - public void updateAuthenticatorExecution(AuthenticationExecutionModel model) { - AuthenticationExecutionEntity entity = null; - AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow()); - for (AuthenticationExecutionEntity exe : flow.getExecutions()) { - if (exe.getId().equals(model.getId())) { - entity = exe; - } - } - if (entity == null) return; - entity.setAuthenticatorFlow(model.isAuthenticatorFlow()); - entity.setAuthenticator(model.getAuthenticator()); - entity.setPriority(model.getPriority()); - entity.setRequirement(model.getRequirement()); - entity.setFlowId(model.getFlowId()); - entity.setAuthenticatorConfig(model.getAuthenticatorConfig()); - } - - @Override - public void removeAuthenticatorExecution(AuthenticationExecutionModel model) { - AuthenticationExecutionEntity entity = null; - AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow()); - for (AuthenticationExecutionEntity exe : flow.getExecutions()) { - if (exe.getId().equals(model.getId())) { - entity = exe; - } - } - if (entity == null) return; - flow.getExecutions().remove(entity); - - } - - @Override - public List getAuthenticatorConfigs() { - List authenticators = new LinkedList<>(); - for (AuthenticatorConfigEntity entity : realm.getAuthenticatorConfigs()) { - authenticators.add(entityToModel(entity)); - } - return authenticators; - } - - @Override - public AuthenticatorConfigModel getAuthenticatorConfigByAlias(String alias) { - for (AuthenticatorConfigModel config : getAuthenticatorConfigs()) { - if (config.getAlias().equals(alias)) { - return config; - } - } - return null; - } - - - @Override - public AuthenticatorConfigModel addAuthenticatorConfig(AuthenticatorConfigModel model) { - AuthenticatorConfigEntity auth = new AuthenticatorConfigEntity(); - String id = (model.getId() == null) ? KeycloakModelUtils.generateId(): model.getId(); - auth.setId(id); - auth.setAlias(model.getAlias()); - auth.setConfig(model.getConfig()); - realm.getAuthenticatorConfigs().add(auth); - model.setId(auth.getId()); - return model; - } - - @Override - public void removeAuthenticatorConfig(AuthenticatorConfigModel model) { - AuthenticatorConfigEntity entity = getAuthenticatorEntity(model.getId()); - if (entity == null) return; - realm.getAuthenticatorConfigs().remove(entity); - - } - - @Override - public AuthenticatorConfigModel getAuthenticatorConfigById(String id) { - AuthenticatorConfigEntity entity = getAuthenticatorEntity(id); - if (entity == null) return null; - return entityToModel(entity); - } - - public AuthenticatorConfigEntity getAuthenticatorEntity(String id) { - AuthenticatorConfigEntity entity = null; - for (AuthenticatorConfigEntity auth : realm.getAuthenticatorConfigs()) { - if (auth.getId().equals(id)) { - entity = auth; - break; - } - } - return entity; - } - - public AuthenticatorConfigModel entityToModel(AuthenticatorConfigEntity entity) { - AuthenticatorConfigModel model = new AuthenticatorConfigModel(); - model.setId(entity.getId()); - model.setAlias(entity.getAlias()); - Map config = new HashMap<>(); - if (entity.getConfig() != null) config.putAll(entity.getConfig()); - model.setConfig(config); - return model; - } - - @Override - public void updateAuthenticatorConfig(AuthenticatorConfigModel model) { - AuthenticatorConfigEntity entity = getAuthenticatorEntity(model.getId()); - if (entity == null) return; - entity.setAlias(model.getAlias()); - if (entity.getConfig() == null) { - entity.setConfig(model.getConfig()); - } else { - entity.getConfig().clear(); - entity.getConfig().putAll(model.getConfig()); - } - } - - @Override - public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) { - RequiredActionProviderEntity auth = new RequiredActionProviderEntity(); - auth.setId(KeycloakModelUtils.generateId()); - auth.setAlias(model.getAlias()); - auth.setName(model.getName()); - auth.setProviderId(model.getProviderId()); - auth.setConfig(model.getConfig()); - auth.setEnabled(model.isEnabled()); - auth.setDefaultAction(model.isDefaultAction()); - realm.getRequiredActionProviders().add(auth); - model.setId(auth.getId()); - return model; - } - - @Override - public void removeRequiredActionProvider(RequiredActionProviderModel model) { - RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId()); - if (entity == null) return; - realm.getRequiredActionProviders().remove(entity); - } - - @Override - public RequiredActionProviderModel getRequiredActionProviderById(String id) { - RequiredActionProviderEntity entity = getRequiredActionProviderEntity(id); - if (entity == null) return null; - return entityToModel(entity); - } - - public RequiredActionProviderModel entityToModel(RequiredActionProviderEntity entity) { - RequiredActionProviderModel model = new RequiredActionProviderModel(); - model.setId(entity.getId()); - model.setProviderId(entity.getProviderId()); - model.setAlias(entity.getAlias()); - model.setName(entity.getName()); - model.setEnabled(entity.isEnabled()); - model.setDefaultAction(entity.isDefaultAction()); - Map config = new HashMap<>(); - if (entity.getConfig() != null) config.putAll(entity.getConfig()); - model.setConfig(config); - return model; - } - - @Override - public void updateRequiredActionProvider(RequiredActionProviderModel model) { - RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId()); - if (entity == null) return; - entity.setAlias(model.getAlias()); - entity.setProviderId(model.getProviderId()); - entity.setEnabled(model.isEnabled()); - entity.setName(model.getName()); - entity.setDefaultAction(model.isDefaultAction()); - if (entity.getConfig() == null) { - entity.setConfig(model.getConfig()); - } else { - entity.getConfig().clear(); - entity.getConfig().putAll(model.getConfig()); - } - } - - @Override - public List getRequiredActionProviders() { - List actions = new LinkedList<>(); - for (RequiredActionProviderEntity entity : realm.getRequiredActionProviders()) { - actions.add(entityToModel(entity)); - } - return actions; - } - - public RequiredActionProviderEntity getRequiredActionProviderEntity(String id) { - RequiredActionProviderEntity entity = null; - for (RequiredActionProviderEntity auth : realm.getRequiredActionProviders()) { - if (auth.getId().equals(id)) { - entity = auth; - break; - } - } - return entity; - } - - @Override - public RequiredActionProviderModel getRequiredActionProviderByAlias(String alias) { - for (RequiredActionProviderModel action : getRequiredActionProviders()) { - if (action.getAlias().equals(alias)) return action; - } - return null; - } - - - - - @Override - public Set getUserFederationMappers() { - Set mappers = new HashSet(); - for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) { - UserFederationMapperModel mapper = entityToModel(entity); - mappers.add(mapper); - } - return mappers; - } - - @Override - public Set getUserFederationMappersByFederationProvider(String federationProviderId) { - Set mappers = new HashSet(); - Set mapperEntities = getUserFederationMapperEntitiesByFederationProvider(federationProviderId); - for (UserFederationMapperEntity entity : mapperEntities) { - mappers.add(entityToModel(entity)); - } - return mappers; - } - - @Override - public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) { - if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) { - throw new ModelDuplicateException("User federation mapper must be unique per federation provider. There is already: " + model.getName()); - } - String id = KeycloakModelUtils.generateId(); - UserFederationMapperEntity entity = new UserFederationMapperEntity(); - entity.setId(id); - entity.setName(model.getName()); - entity.setFederationProviderId(model.getFederationProviderId()); - entity.setFederationMapperType(model.getFederationMapperType()); - entity.setConfig(model.getConfig()); - - this.realm.getUserFederationMappers().add(entity); - UserFederationMapperModel mapperModel = entityToModel(entity); - - session.getKeycloakSessionFactory().publish(new UserFederationMapperEventImpl(mapperModel, this, session)); - - return mapperModel; - } - - protected UserFederationMapperEntity getUserFederationMapperEntity(String id) { - for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) { - if (entity.getId().equals(id)) { - return entity; - } - } - return null; - - } - - protected UserFederationMapperEntity getUserFederationMapperEntityByName(String federationProviderId, String name) { - for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) { - if (entity.getFederationProviderId().equals(federationProviderId) && entity.getName().equals(name)) { - return entity; - } - } - return null; - - } - - protected Set getUserFederationMapperEntitiesByFederationProvider(String federationProviderId) { - Set mappers = new HashSet(); - for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) { - if (federationProviderId.equals(entity.getFederationProviderId())) { - mappers.add(entity); - } - } - return mappers; - } - - @Override - public void removeUserFederationMapper(UserFederationMapperModel mapper) { - UserFederationMapperEntity toDelete = getUserFederationMapperEntity(mapper.getId()); - if (toDelete != null) { - this.realm.getUserFederationMappers().remove(toDelete); - } - } - - @Override - public void updateUserFederationMapper(UserFederationMapperModel mapper) { - UserFederationMapperEntity entity = getUserFederationMapperEntity(mapper.getId()); - entity.setFederationProviderId(mapper.getFederationProviderId()); - entity.setFederationMapperType(mapper.getFederationMapperType()); - if (entity.getConfig() == null) { - entity.setConfig(mapper.getConfig()); - } else { - entity.getConfig().clear(); - entity.getConfig().putAll(mapper.getConfig()); - } - - session.getKeycloakSessionFactory().publish(new UserFederationMapperEventImpl(mapper, this, session)); - } - - @Override - public UserFederationMapperModel getUserFederationMapperById(String id) { - UserFederationMapperEntity entity = getUserFederationMapperEntity(id); - if (entity == null) return null; - return entityToModel(entity); - } - - @Override - public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) { - UserFederationMapperEntity entity = getUserFederationMapperEntityByName(federationProviderId, name); - if (entity == null) return null; - return entityToModel(entity); - } - - protected UserFederationMapperModel entityToModel(UserFederationMapperEntity entity) { - UserFederationMapperModel mapper = new UserFederationMapperModel(); - mapper.setId(entity.getId()); - mapper.setName(entity.getName()); - mapper.setFederationProviderId(entity.getFederationProviderId()); - mapper.setFederationMapperType(entity.getFederationMapperType()); - Map config = new HashMap(); - if (entity.getConfig() != null) config.putAll(entity.getConfig()); - mapper.setConfig(config); - return mapper; - } - - @Override - public GroupModel createGroup(String name) { - return null; - } - - @Override - public void addTopLevelGroup(GroupModel subGroup) { - - } -} diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java deleted file mode 100755 index 7706459df7..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file.adapter; - -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleContainerModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.entities.RoleEntity; -import org.keycloak.models.utils.KeycloakModelUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * RoleModel for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class RoleAdapter implements RoleModel { - - private final RoleEntity role; - private RoleContainerModel roleContainer; - private final RealmModel realm; - - private final Set compositeRoles = new HashSet(); - - public RoleAdapter(RealmModel realm, RoleEntity roleEntity) { - this(realm, roleEntity, null); - } - - public RoleAdapter(RealmModel realm, RoleEntity roleEntity, RoleContainerModel roleContainer) { - this.role = roleEntity; - this.roleContainer = roleContainer; - this.realm = realm; - } - - public RoleEntity getRoleEntity() { - return this.role; - } - - public boolean isRealmRole() { - return role.getRealmId() != null; - } - - @Override - public String getId() { - return role.getId(); - } - - @Override - public String getName() { - return role.getName(); - } - - @Override - public void setName(String name) { - RealmAdapter realmAdapter = (RealmAdapter)realm; - if (role.getName().equals(name)) return; - if (realmAdapter.hasRoleWithName(name)) throw new ModelDuplicateException("Role name " + name + " already exists."); - role.setName(name); - } - - @Override - public String getDescription() { - return role.getDescription(); - } - - @Override - public void setDescription(String description) { - role.setDescription(description); - } - - @Override - public boolean isScopeParamRequired() { - return role.isScopeParamRequired(); - } - - @Override - public void setScopeParamRequired(boolean scopeParamRequired) { - role.setScopeParamRequired(scopeParamRequired); - } - - @Override - public boolean isComposite() { - return role.getCompositeRoleIds() != null && role.getCompositeRoleIds().size() > 0; - } - - @Override - public void addCompositeRole(RoleModel childRole) { - List compositeRoleIds = role.getCompositeRoleIds(); - if (compositeRoleIds == null) compositeRoleIds = new ArrayList(); - compositeRoleIds.add(childRole.getId()); - role.setCompositeRoleIds(compositeRoleIds); - compositeRoles.add(childRole); - } - - /** - * Recursively remove composite roles for the specified app - * @param appId - */ - public void removeApplicationComposites(String appId) { - if (!isComposite()) return; - Set toBeRemoved = new HashSet(); - for (RoleModel compositeRole : getComposites()) { - RoleAdapter roleAdapter = (RoleAdapter)compositeRole; - if (appId.equals(roleAdapter.getRoleEntity().getClientId())) { - toBeRemoved.add(compositeRole); - } else { - roleAdapter.removeApplicationComposites(appId); - } - } - - for (RoleModel compositeRole : toBeRemoved) { - removeCompositeRole(compositeRole); - } - } - - @Override - public void removeCompositeRole(RoleModel childRole) { - compositeRoles.remove(childRole); - List compositeRoleIds = role.getCompositeRoleIds(); - if (compositeRoleIds == null) return; // shouldn't happen - compositeRoleIds.remove(childRole.getId()); - role.setCompositeRoleIds(compositeRoleIds); - } - - @Override - public Set getComposites() { - return Collections.unmodifiableSet(compositeRoles); - } - - @Override - public RoleContainerModel getContainer() { - if (roleContainer == null) { - // Compute it - if (role.getRealmId() != null) { - roleContainer = realm;//new RealmAdapter(session, realm); - } else if (role.getClientId() != null) { - roleContainer = realm.getClientById(role.getClientId());//new ApplicationAdapter(session, realm, appEntity); - } else { - throw new IllegalStateException("Both realmId and applicationId are null for role: " + this); - } - } - return roleContainer; - } - - @Override - public boolean hasRole(RoleModel role) { - if (this.equals(role)) return true; - if (!isComposite()) return false; - - Set visited = new HashSet(); - return KeycloakModelUtils.searchFor(role, this, visited); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !(o instanceof RoleModel)) return false; - - RoleModel that = (RoleModel) o; - return that.getId().equals(getId()); - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - -} diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java deleted file mode 100755 index d0dc92b756..0000000000 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java +++ /dev/null @@ -1,614 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.models.file.adapter; - -import org.keycloak.connections.file.InMemoryModel; -import org.keycloak.models.ClientModel; - -import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt; - -import org.keycloak.models.GroupModel; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.OTPPolicy; -import org.keycloak.models.UserConsentModel; -import org.keycloak.models.PasswordPolicy; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserCredentialValueModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.entities.CredentialEntity; -import org.keycloak.models.entities.FederatedIdentityEntity; -import org.keycloak.models.entities.RoleEntity; -import org.keycloak.models.entities.UserEntity; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.models.utils.Pbkdf2PasswordEncoder; -import org.keycloak.common.util.Time; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * UserModel for JSON persistence. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class UserAdapter implements UserModel, Comparable { - - private final InMemoryModel inMemoryModel; - private final UserEntity user; - private final RealmModel realm; - - private final Set allRoles = new HashSet(); - private final Set allGroups = new HashSet(); - - public UserAdapter(RealmModel realm, UserEntity userEntity, InMemoryModel inMemoryModel) { - this.user = userEntity; - this.realm = realm; - if (userEntity.getFederatedIdentities() == null) { - userEntity.setFederatedIdentities(new ArrayList()); - } - this.inMemoryModel = inMemoryModel; - } - - public UserEntity getUserEntity() { - return this.user; - } - - @Override - public String getId() { - return user.getId(); - } - - @Override - public String getUsername() { - return user.getUsername(); - } - - @Override - public void setUsername(String username) { - username = KeycloakModelUtils.toLowerCaseSafe(username); - - if (getUsername() == null) { - user.setUsername(username); - return; - } - - if (getUsername().equals(username)) return; // allow setting to same name - - if (inMemoryModel.hasUserWithUsername(realm.getId(), username)) - throw new ModelDuplicateException("User with username " + username + " already exists in realm."); - user.setUsername(username); - } - - @Override - public Long getCreatedTimestamp() { - return user.getCreatedTimestamp(); - } - - @Override - public void setCreatedTimestamp(Long timestamp) { - user.setCreatedTimestamp(timestamp); - } - - @Override - public boolean isEnabled() { - return user.isEnabled(); - } - - @Override - public void setEnabled(boolean enabled) { - user.setEnabled(enabled); - } - - @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) { - email = KeycloakModelUtils.toLowerCaseSafe(email); - - if (email == null) { - user.setEmail(email); - return; - } - - if (email.equals(getEmail())) return; - - RealmAdapter realmAdapter = (RealmAdapter)realm; - if (realmAdapter.hasUserWithEmail(email)) throw new ModelDuplicateException("User with email address " + email + " already exists."); - user.setEmail(email); - } - - @Override - public boolean isEmailVerified() { - return user.isEmailVerified(); - } - - @Override - public void setEmailVerified(boolean verified) { - user.setEmailVerified(verified); - } - - @Override - public void setSingleAttribute(String name, String value) { - if (user.getAttributes() == null) { - user.setAttributes(new HashMap>()); - } - - List attrValues = new ArrayList<>(); - attrValues.add(value); - user.getAttributes().put(name, attrValues); - } - - @Override - public void setAttribute(String name, List values) { - if (user.getAttributes() == null) { - user.setAttributes(new HashMap>()); - } - - user.getAttributes().put(name, values); - } - - @Override - public void removeAttribute(String name) { - if (user.getAttributes() == null) return; - - user.getAttributes().remove(name); - } - - @Override - public String getFirstAttribute(String name) { - if (user.getAttributes()==null) return null; - - List attrValues = user.getAttributes().get(name); - return (attrValues==null || attrValues.isEmpty()) ? null : attrValues.get(0); - } - - @Override - public List getAttribute(String name) { - if (user.getAttributes()==null) return Collections.emptyList(); - List attrValues = user.getAttributes().get(name); - return (attrValues == null) ? Collections.emptyList() : Collections.unmodifiableList(attrValues); - } - - @Override - public Map> getAttributes() { - return user.getAttributes()==null ? Collections.>emptyMap() : Collections.unmodifiableMap((Map) user.getAttributes()); - } - - @Override - public Set getRequiredActions() { - List requiredActions = user.getRequiredActions(); - if (requiredActions == null) requiredActions = new ArrayList(); - return new HashSet(requiredActions); - } - - @Override - public void addRequiredAction(RequiredAction action) { - String actionName = action.name(); - addRequiredAction(actionName); - } - - @Override - public void addRequiredAction(String actionName) { - List requiredActions = user.getRequiredActions(); - if (requiredActions == null) requiredActions = new ArrayList<>(); - if (!requiredActions.contains(actionName)) { - requiredActions.add(actionName); - } - user.setRequiredActions(requiredActions); - } - - @Override - public void removeRequiredAction(RequiredAction action) { - String actionName = action.name(); - removeRequiredAction(actionName); - } - - @Override - public void removeRequiredAction(String actionName) { - List requiredActions = user.getRequiredActions(); - if (requiredActions == null) return; - requiredActions.remove(actionName); - user.setRequiredActions(requiredActions); - } - - @Override - public boolean isOtpEnabled() { - return user.isTotp(); - } - - @Override - public void setOtpEnabled(boolean totp) { - user.setTotp(totp); - } - - @Override - public void updateCredential(UserCredentialModel cred) { - - if (cred.getType().equals(UserCredentialModel.PASSWORD)) { - updatePasswordCredential(cred); - } else if (UserCredentialModel.isOtp(cred.getType())){ - updateOtpCredential(cred); - - }else { - CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType()); - - if (credentialEntity == null) { - credentialEntity = setCredentials(user, cred); - credentialEntity.setValue(cred.getValue()); - user.getCredentials().add(credentialEntity); - } else { - credentialEntity.setValue(cred.getValue()); - } - } - } - - private void updateOtpCredential(UserCredentialModel cred) { - CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType()); - - if (credentialEntity == null) { - credentialEntity = setCredentials(user, cred); - credentialEntity.setValue(cred.getValue()); - OTPPolicy otpPolicy = realm.getOTPPolicy(); - credentialEntity.setAlgorithm(otpPolicy.getAlgorithm()); - credentialEntity.setDigits(otpPolicy.getDigits()); - credentialEntity.setCounter(otpPolicy.getInitialCounter()); - credentialEntity.setPeriod(otpPolicy.getPeriod()); - user.getCredentials().add(credentialEntity); - } else { - credentialEntity.setValue(cred.getValue()); - OTPPolicy policy = realm.getOTPPolicy(); - credentialEntity.setDigits(policy.getDigits()); - credentialEntity.setCounter(policy.getInitialCounter()); - credentialEntity.setAlgorithm(policy.getAlgorithm()); - credentialEntity.setPeriod(policy.getPeriod()); - } - } - - - private void updatePasswordCredential(UserCredentialModel cred) { - CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType()); - - if (credentialEntity == null) { - credentialEntity = setCredentials(user, cred); - setValue(credentialEntity, cred); - user.getCredentials().add(credentialEntity); - } else { - - int expiredPasswordsPolicyValue = -1; - PasswordPolicy policy = realm.getPasswordPolicy(); - if(policy != null) { - expiredPasswordsPolicyValue = policy.getExpiredPasswords(); - } - - if (expiredPasswordsPolicyValue != -1) { - user.getCredentials().remove(credentialEntity); - credentialEntity.setType(UserCredentialModel.PASSWORD_HISTORY); - user.getCredentials().add(credentialEntity); - - List credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY); - if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) { - user.getCredentials().removeAll(credentialEntities.subList(expiredPasswordsPolicyValue - 1, credentialEntities.size())); - } - - credentialEntity = setCredentials(user, cred); - setValue(credentialEntity, cred); - user.getCredentials().add(credentialEntity); - } else { - List credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY); - if (credentialEntities != null && credentialEntities.size() > 0) { - user.getCredentials().removeAll(credentialEntities); - } - setValue(credentialEntity, cred); - } - } - } - - private CredentialEntity setCredentials(UserEntity user, UserCredentialModel cred) { - CredentialEntity credentialEntity = new CredentialEntity(); - credentialEntity.setType(cred.getType()); - credentialEntity.setDevice(cred.getDevice()); - return credentialEntity; - } - - private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) { - byte[] salt = getSalt(); - int hashIterations = 1; - PasswordPolicy policy = realm.getPasswordPolicy(); - if (policy != null) { - hashIterations = policy.getHashIterations(); - if (hashIterations == -1) - hashIterations = 1; - } - credentialEntity.setCreatedDate(Time.toMillis(Time.currentTime())); - credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations)); - credentialEntity.setSalt(salt); - credentialEntity.setHashIterations(hashIterations); - } - - private CredentialEntity getCredentialEntity(UserEntity userEntity, String credType) { - for (CredentialEntity entity : userEntity.getCredentials()) { - if (entity.getType().equals(credType)) { - return entity; - } - } - - return null; - } - - private List getCredentialEntities(UserEntity userEntity, String credType) { - List credentialEntities = new ArrayList(); - for (CredentialEntity entity : userEntity.getCredentials()) { - if (entity.getType().equals(credType)) { - credentialEntities.add(entity); - } - } - - // Avoiding direct use of credSecond.getCreatedDate() - credFirst.getCreatedDate() to prevent Integer Overflow - // Orders from most recent to least recent - Collections.sort(credentialEntities, new Comparator() { - public int compare(CredentialEntity credFirst, CredentialEntity credSecond) { - if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) { - return -1; - } else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) { - return 1; - } else { - return 0; - } - } - }); - return credentialEntities; - } - - @Override - public List getCredentialsDirectly() { - List credentials = new ArrayList(user.getCredentials()); - List result = new ArrayList(); - - for (CredentialEntity credEntity : credentials) { - UserCredentialValueModel credModel = new UserCredentialValueModel(); - credModel.setType(credEntity.getType()); - credModel.setDevice(credEntity.getDevice()); - credModel.setCreatedDate(credEntity.getCreatedDate()); - credModel.setValue(credEntity.getValue()); - credModel.setSalt(credEntity.getSalt()); - credModel.setHashIterations(credEntity.getHashIterations()); - if (UserCredentialModel.isOtp(credEntity.getType())) { - credModel.setCounter(credEntity.getCounter()); - if (credEntity.getAlgorithm() == null) { - // for migration where these values would be null - credModel.setAlgorithm(realm.getOTPPolicy().getAlgorithm()); - } else { - credModel.setAlgorithm(credEntity.getAlgorithm()); - } - if (credEntity.getDigits() == 0) { - // for migration where these values would be 0 - credModel.setDigits(realm.getOTPPolicy().getDigits()); - } else { - credModel.setDigits(credEntity.getDigits()); - } - - if (credEntity.getPeriod() == 0) { - // for migration where these values would be 0 - credModel.setPeriod(realm.getOTPPolicy().getPeriod()); - } else { - credModel.setPeriod(credEntity.getPeriod()); - } - } - - result.add(credModel); - } - - return result; - } - - @Override - public void updateCredentialDirectly(UserCredentialValueModel credModel) { - CredentialEntity credentialEntity = getCredentialEntity(user, credModel.getType()); - - if (credentialEntity == null) { - credentialEntity = new CredentialEntity(); - // credentialEntity.setId(KeycloakModelUtils.generateId()); - credentialEntity.setType(credModel.getType()); - // credentialEntity.setUser(user); - credModel.setCreatedDate(credModel.getCreatedDate()); - user.getCredentials().add(credentialEntity); - } - - credentialEntity.setValue(credModel.getValue()); - credentialEntity.setSalt(credModel.getSalt()); - credentialEntity.setDevice(credModel.getDevice()); - credentialEntity.setHashIterations(credModel.getHashIterations()); - credentialEntity.setCounter(credModel.getCounter()); - credentialEntity.setAlgorithm(credModel.getAlgorithm()); - credentialEntity.setDigits(credModel.getDigits()); - credentialEntity.setPeriod(credModel.getPeriod()); - } - - @Override - public Set getGroups() { - return Collections.unmodifiableSet(allGroups); - } - - @Override - public void joinGroup(GroupModel group) { - allGroups.add(group); - - } - - @Override - public void leaveGroup(GroupModel group) { - if (user == null || group == null) return; - allGroups.remove(group); - - } - - @Override - public boolean isMemberOf(GroupModel group) { - return KeycloakModelUtils.isMember(getGroups(), group); - } - - @Override - public boolean hasRole(RoleModel role) { - Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); - } - - @Override - public void grantRole(RoleModel role) { - allRoles.add(role); - } - - @Override - public Set getRoleMappings() { - return Collections.unmodifiableSet(allRoles); - } - - @Override - public Set getRealmRoleMappings() { - Set allRoleMappings = getRoleMappings(); - - // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user? - Set realmRoles = new HashSet(); - for (RoleModel role : allRoleMappings) { - RoleEntity roleEntity = ((RoleAdapter) role).getRoleEntity(); - - if (realm.getId().equals(roleEntity.getRealmId())) { - realmRoles.add(role); - } - } - return realmRoles; - } - - @Override - public void deleteRoleMapping(RoleModel role) { - if (user == null || role == null) return; - allRoles.remove(role); - } - - @Override - public Set getClientRoleMappings(ClientModel app) { - Set result = new HashSet(); - - for (RoleModel role : allRoles) { - RoleEntity roleEntity = ((RoleAdapter)role).getRoleEntity(); - if (app.getId().equals(roleEntity.getClientId())) { - result.add(new RoleAdapter(realm, roleEntity, app)); - } - } - return result; - } - - @Override - public String getFederationLink() { - return user.getFederationLink(); - } - - @Override - public void setFederationLink(String link) { - user.setFederationLink(link); - } - - @Override - public String getServiceAccountClientLink() { - return user.getServiceAccountClientLink(); - } - - @Override - public void setServiceAccountClientLink(String clientInternalId) { - user.setServiceAccountClientLink(clientInternalId); - } - - @Override - public void addConsent(UserConsentModel consent) { - // TODO - } - - @Override - public UserConsentModel getConsentByClient(String clientId) { - // TODO - return null; - } - - @Override - public List getConsents() { - // TODO - return null; - } - - @Override - public void updateConsent(UserConsentModel consent) { - // TODO - } - - @Override - public boolean revokeConsentForClient(String clientId) { - // TODO - return false; - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !(o instanceof UserModel)) return false; - - UserModel that = (UserModel) o; - return that.getId().equals(getId()); - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public int compareTo(Object user) { - if (this == user) return 0; - return (getUsername().compareTo(((UserModel)user).getUsername())); - } -} diff --git a/model/file/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory b/model/file/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory deleted file mode 100644 index 173ba2d258..0000000000 --- a/model/file/src/main/resources/META-INF/services/org.keycloak.models.RealmProviderFactory +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.models.file.FileRealmProviderFactory \ No newline at end of file diff --git a/model/file/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory b/model/file/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory deleted file mode 100644 index 691ada44ce..0000000000 --- a/model/file/src/main/resources/META-INF/services/org.keycloak.models.UserProviderFactory +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.models.file.FileUserProviderFactory \ No newline at end of file diff --git a/model/pom.xml b/model/pom.xml index 2f78805ae2..0322abb35b 100755 --- a/model/pom.xml +++ b/model/pom.xml @@ -29,7 +29,6 @@ invalidation-cache jpa mongo - file sessions-infinispan diff --git a/pom.xml b/pom.xml index 85409fe308..15e9f1cf15 100755 --- a/pom.xml +++ b/pom.xml @@ -838,7 +838,7 @@ org.keycloak keycloak-wf9-server-subsystem ${project.version} - + org.keycloak keycloak-subsystem @@ -959,11 +959,6 @@ keycloak-model-api ${project.version} - - org.keycloak - keycloak-model-file - ${project.version} - org.keycloak keycloak-invalidation-cache-infinispan @@ -1441,7 +1436,7 @@ org.wildfly.build wildfly-feature-pack-build-maven-plugin ${wildfly.build-tools.version} - + org.wildfly.build wildfly-server-provisioning-maven-plugin @@ -1462,7 +1457,7 @@ 3.1.1 - + From f7b1af7e11a5dd88c1b926d7cbcf476a88ca2119 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 13 Nov 2015 13:22:42 -0200 Subject: [PATCH 05/16] Removal of module keycloak-connections-file --- connections/file/pom.xml | 43 ---- .../file/DefaultFileConnectionProvider.java | 86 ------- .../DefaultFileConnectionProviderFactory.java | 210 ------------------ .../file/FileConnectionProvider.java | 31 --- .../file/FileConnectionProviderFactory.java | 25 --- .../connections/file/FileConnectionSpi.java | 32 --- .../connections/file/InMemoryModel.java | 109 --------- .../org/keycloak/connections/file/Model.java | 30 --- ...ections.file.FileConnectionProviderFactory | 1 - .../services/org.keycloak.provider.Spi | 1 - connections/pom.xml | 1 - .../keycloak-connections-file/main/module.xml | 20 -- .../WEB-INF/jboss-deployment-structure.xml | 1 - .../keycloak-services/main/module.xml | 1 - .../eap6/eap6-server-modules/build.xml | 4 - .../WEB-INF/jboss-deployment-structure.xml | 1 - .../keycloak-connections-file/main/module.xml | 20 -- .../keycloak-services/main/module.xml | 1 - pom.xml | 5 - 19 files changed, 622 deletions(-) delete mode 100755 connections/file/pom.xml delete mode 100644 connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProvider.java delete mode 100755 connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java delete mode 100644 connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProvider.java delete mode 100644 connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProviderFactory.java delete mode 100644 connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java delete mode 100755 connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java delete mode 100755 connections/file/src/main/java/org/keycloak/connections/file/Model.java delete mode 100644 connections/file/src/main/resources/META-INF/services/org.keycloak.connections.file.FileConnectionProviderFactory delete mode 100644 connections/file/src/main/resources/META-INF/services/org.keycloak.provider.Spi delete mode 100755 distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-connections-file/main/module.xml delete mode 100755 distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-connections-file/main/module.xml diff --git a/connections/file/pom.xml b/connections/file/pom.xml deleted file mode 100755 index a4a749021f..0000000000 --- a/connections/file/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - keycloak-parent - org.keycloak - 1.7.0.Final-SNAPSHOT - ../../pom.xml - - 4.0.0 - - keycloak-connections-file - Keycloak Connections File - - - - - org.keycloak - keycloak-export-import-api - - - org.keycloak - keycloak-export-import-single-file - - - org.keycloak - keycloak-core - - - org.keycloak - keycloak-model-api - - - org.codehaus.jackson - jackson-mapper-asl - provided - - - org.jboss.logging - jboss-logging - - - diff --git a/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProvider.java b/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProvider.java deleted file mode 100644 index b821943901..0000000000 --- a/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProvider.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.connections.file; - -import org.keycloak.models.KeycloakSession; - -/** - * Provides the InMemoryModel and notifies the factory to save it when - * the session is done. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class DefaultFileConnectionProvider implements FileConnectionProvider { - - private final DefaultFileConnectionProviderFactory factory; - private final KeycloakSession session; - private final InMemoryModel inMemoryModel; - - private boolean isRollbackOnly = false; - - public DefaultFileConnectionProvider(DefaultFileConnectionProviderFactory factory, - KeycloakSession session, - InMemoryModel inMemoryModel) { - this.factory = factory; - this.session = session; - this.inMemoryModel = inMemoryModel; - } - - @Override - public InMemoryModel getModel() { - return inMemoryModel; - } - - @Override - public void sessionClosed(KeycloakSession session) { - factory.sessionClosed(session); - } - - @Override - public void close() { - } - - @Override - public void begin() { - } - - @Override - public void commit() { - factory.commit(session); - } - - @Override - public void rollback() { - factory.rollback(session); - } - - @Override - public void setRollbackOnly() { - isRollbackOnly = true; - } - - @Override - public boolean getRollbackOnly() { - return isRollbackOnly; - } - - @Override - public boolean isActive() { - return factory.isActive(session); - } - -} diff --git a/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java b/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java deleted file mode 100755 index 9bfe36248c..0000000000 --- a/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.connections.file; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.codehaus.jackson.JsonToken; -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.exportimport.Strategy; -import org.keycloak.exportimport.util.ExportUtils; -import org.keycloak.exportimport.util.ImportUtils; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.util.JsonSerialization; - -/** - * This class dispenses a FileConnectionProvider to Keycloak sessions. It - * makes sure that only one InMemoryModel is provided for each session and it - * handles thread contention for the file where the model is read or saved. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class DefaultFileConnectionProviderFactory implements FileConnectionProviderFactory { - - protected static final Logger logger = Logger.getLogger(DefaultFileConnectionProviderFactory.class); - - private File kcdata; - private final Map allProviders = new HashMap(); - - @Override - public void init(Config.Scope config) { - String fileName = config.get("fileName"); - if (fileName == null) { - fileName = "keycloak-model.json"; - } - - String directory = config.get("directory"); - if (directory == null) { - directory = System.getProperty("jboss.server.data.dir"); - } - if (directory == null) { - directory = "."; - } - - kcdata = new File(directory, fileName); - } - - public void sessionClosed(KeycloakSession session) { - synchronized(allProviders) { - allProviders.remove(session); - //logger.info("Removed session " + session.hashCode()); - //logger.info("sessionClosed: Session count=" + allModels.size()); - } - } - - void readModelFile(KeycloakSession session) { - synchronized(allProviders) { - if (!kcdata.exists()) { - return; - } - - FileInputStream fis = null; - try { - fis = new FileInputStream(kcdata); - Model model = JsonSerialization.readValue(fis, Model.class); - ImportUtils.importFromStream(session, JsonSerialization.mapper, fis, Strategy.IGNORE_EXISTING); - session.realms().getMigrationModel().setStoredVersion(model.getModelVersion()); - - ImportUtils.importRealms(session, model.getRealms(), Strategy.IGNORE_EXISTING); - } catch (IOException ioe) { - logger.error("Unable to read model file " + kcdata.getAbsolutePath(), ioe); - } finally { - //logger.info("Read model file for session=" + session.hashCode()); - try { - if (fis != null) { - fis.close(); - } - } catch (IOException e) { - logger.error("Failed to close output stream.", e); - } - } - } - } - - void writeModelFile(KeycloakSession session) { - synchronized(allProviders) { - FileOutputStream outStream = null; - - try { - outStream = new FileOutputStream(kcdata); - exportModel(session, outStream); - } catch (IOException e) { - logger.error("Unable to write model file " + kcdata.getAbsolutePath(), e); - } finally { - //logger.info("Wrote model file for session=" + session.hashCode()); - try { - if (outStream != null) { - outStream.close(); - } - } catch (IOException e) { - logger.error("Failed to close output stream.", e); - } - } - } - } - - private void exportModel(KeycloakSession session, FileOutputStream outStream) throws IOException { - List realms = session.realms().getRealms(); - List reps = new ArrayList(); - for (RealmModel realm : realms) { - reps.add(ExportUtils.exportRealm(session, realm, true)); - } - Model model = new Model(); - model.setRealms(reps); - model.setModelVersion(session.realms().getMigrationModel().getStoredVersion()); - JsonSerialization.prettyMapper.writeValue(outStream, model); - } - - @Override - public FileConnectionProvider create(KeycloakSession session) { - synchronized (allProviders) { - FileConnectionProvider fcProvider = allProviders.get(session); - if (fcProvider == null) { - InMemoryModel model = new InMemoryModel(); - fcProvider = new DefaultFileConnectionProvider(this, session, model); - allProviders.put(session, fcProvider); - session.getTransaction().enlist(fcProvider); - readModelFile(session); - //logger.info("Added session " + session.hashCode() + " total sessions=" + allModels.size()); - } - - return fcProvider; - } - } - - // commitCount is used for debugging. This allows you to easily run a test - // to a particular point and then examine the JSON file. - //private static int commitCount = 0; - void commit(KeycloakSession session) { - //commitCount++; - synchronized (allProviders) { - // in case commit was somehow called twice on the same session - if (!allProviders.containsKey(session)) return; - - try { - writeModelFile(session); - } finally { - allProviders.remove(session); - //logger.info("Removed session " + session.hashCode()); - //logger.info("*** commitCount=" + commitCount); - //logger.info("commit(): Session count=" + allModels.size()); - } - - // if (commitCount == 16) {Thread.dumpStack();System.exit(0);} - } - } - - void rollback(KeycloakSession session) { - synchronized (allProviders) { - allProviders.remove(session); - //logger.info("rollback(): Session count=" + allModels.size()); - } - } - - boolean isActive(KeycloakSession session) { - synchronized (allProviders) { - return allProviders.containsKey(session); - } - } - - @Override - public void close() { - - } - - @Override - public String getId() { - return "default"; - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - - } - -} diff --git a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProvider.java b/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProvider.java deleted file mode 100644 index a3ecfeff59..0000000000 --- a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.connections.file; - -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakTransaction; -import org.keycloak.provider.Provider; - -/** - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public interface FileConnectionProvider extends Provider, KeycloakTransaction { - - InMemoryModel getModel(); - - void sessionClosed(KeycloakSession session); -} diff --git a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProviderFactory.java b/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProviderFactory.java deleted file mode 100644 index 92d161a9c8..0000000000 --- a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionProviderFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.connections.file; - -import org.keycloak.provider.ProviderFactory; - -/** - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public interface FileConnectionProviderFactory extends ProviderFactory { -} diff --git a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java b/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java deleted file mode 100644 index 5929a0ba61..0000000000 --- a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.keycloak.connections.file; - -import org.keycloak.provider.Provider; -import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.Spi; - -/** - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class FileConnectionSpi implements Spi { - - @Override - public boolean isInternal() { - return true; - } - - @Override - public String getName() { - return "connectionsFile"; - } - - @Override - public Class getProviderClass() { - return FileConnectionProvider.class; - } - - @Override - public Class getProviderFactoryClass() { - return FileConnectionProviderFactory.class; - } - -} diff --git a/connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java b/connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java deleted file mode 100755 index 2476b44e58..0000000000 --- a/connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.keycloak.connections.file; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; - -/** - * This class provides an in-memory copy of the entire model for each - * Keycloak session. At the start of the session, the model is read - * from JSON. When the session's transaction ends, the model is written back - * out. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc. - */ -public class InMemoryModel { - private final Map allRealms = new HashMap(); - - // realmId, userId, userModel - private final Map> allUsers = new HashMap>(); - - private String modelVersion; - - public InMemoryModel() { - } - - public void putRealm(String id, RealmModel realm) { - allRealms.put(id, realm); - allUsers.put(id, new HashMap()); - } - - public String getModelVersion() { - return modelVersion; - } - - public void setModelVersion(String modelVersion) { - this.modelVersion = modelVersion; - } - - public RealmModel getRealm(String id) { - return allRealms.get(id); - } - - public Collection getRealms() { - return allRealms.values(); - } - - public RealmModel getRealmByName(String name) { - for (RealmModel realm : getRealms()) { - if (realm.getName().equals(name)) return realm; - } - - return null; - } - - public boolean removeRealm(String id) { - allUsers.remove(id); - return (allRealms.remove(id) != null); - } - - protected Map realmUsers(String realmId) { - Map realmUsers = allUsers.get(realmId); - if (realmUsers == null) throw new NullPointerException("Realm users not found for id=" + realmId); - return realmUsers; - } - - public void putUser(String realmId, String userId, UserModel user) { - realmUsers(realmId).put(userId, user); - } - - public UserModel getUser(String realmId, String userId) { - return realmUsers(realmId).get(userId); - } - - public boolean hasUserWithUsername(String realmId, String username) { - for (UserModel user : getUsers(realmId)) { - if (user.getUsername().equals(username)) return true; - } - - return false; - } - - public Collection getUsers(String realmId) { - return realmUsers(realmId).values(); - } - - public boolean removeUser(String realmId, String userId) { - return (realmUsers(realmId).remove(userId) != null); - } - -} diff --git a/connections/file/src/main/java/org/keycloak/connections/file/Model.java b/connections/file/src/main/java/org/keycloak/connections/file/Model.java deleted file mode 100755 index a609cea3f7..0000000000 --- a/connections/file/src/main/java/org/keycloak/connections/file/Model.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.keycloak.connections.file; - -import org.keycloak.representations.idm.RealmRepresentation; - -import java.util.List; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class Model { - private String modelVersion; - private List realms; - - public String getModelVersion() { - return modelVersion; - } - - public void setModelVersion(String modelVersion) { - this.modelVersion = modelVersion; - } - - public List getRealms() { - return realms; - } - - public void setRealms(List realms) { - this.realms = realms; - } -} diff --git a/connections/file/src/main/resources/META-INF/services/org.keycloak.connections.file.FileConnectionProviderFactory b/connections/file/src/main/resources/META-INF/services/org.keycloak.connections.file.FileConnectionProviderFactory deleted file mode 100644 index d46ba7fb24..0000000000 --- a/connections/file/src/main/resources/META-INF/services/org.keycloak.connections.file.FileConnectionProviderFactory +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.connections.file.DefaultFileConnectionProviderFactory \ No newline at end of file diff --git a/connections/file/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/connections/file/src/main/resources/META-INF/services/org.keycloak.provider.Spi deleted file mode 100644 index b0ddd93218..0000000000 --- a/connections/file/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.connections.file.FileConnectionSpi \ No newline at end of file diff --git a/connections/pom.xml b/connections/pom.xml index 891cd59c65..e74e99cfaa 100755 --- a/connections/pom.xml +++ b/connections/pom.xml @@ -17,7 +17,6 @@ jpa-liquibase infinispan mongo - file mongo-update http-client diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-connections-file/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-connections-file/main/module.xml deleted file mode 100755 index 5400a883d3..0000000000 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-connections-file/main/module.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml index d8b1aa2ca0..7e61fb4bd6 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml @@ -8,7 +8,6 @@ - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml index 6b83a7e81d..340c3bff4c 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml @@ -18,7 +18,6 @@ - diff --git a/distribution/server-overlay/eap6/eap6-server-modules/build.xml b/distribution/server-overlay/eap6/eap6-server-modules/build.xml index 924a32a6ac..9f60836a8f 100755 --- a/distribution/server-overlay/eap6/eap6-server-modules/build.xml +++ b/distribution/server-overlay/eap6/eap6-server-modules/build.xml @@ -173,10 +173,6 @@ - - - - diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml index d8b1aa2ca0..7e61fb4bd6 100755 --- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml +++ b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml @@ -8,7 +8,6 @@ - diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-connections-file/main/module.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-connections-file/main/module.xml deleted file mode 100755 index a881b2b672..0000000000 --- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-connections-file/main/module.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml index bf230faab4..aa895e8b66 100755 --- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml @@ -18,7 +18,6 @@ - diff --git a/pom.xml b/pom.xml index 15e9f1cf15..d545efd5f9 100755 --- a/pom.xml +++ b/pom.xml @@ -597,11 +597,6 @@ keycloak-broker-saml ${project.version} - - org.keycloak - keycloak-connections-file - ${project.version} - org.keycloak keycloak-connections-infinispan From 600d1f8a5656eb4698772a1e583a27de42e7e751 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 13 Nov 2015 13:49:24 +0100 Subject: [PATCH 06/16] KEYCLOAK-1750 First broker login - docs --- .../reference/en/en-US/modules/auth-spi.xml | 8 + .../en/en-US/modules/identity-broker.xml | 172 +++++++++++++++--- .../messages/admin-messages_en.properties | 2 +- 3 files changed, 159 insertions(+), 23 deletions(-) diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/auth-spi.xml b/docbook/auth-server-docs/reference/en/en-US/modules/auth-spi.xml index 10cb89db71..12e2b1ac78 100755 --- a/docbook/auth-server-docs/reference/en/en-US/modules/auth-spi.xml +++ b/docbook/auth-server-docs/reference/en/en-US/modules/auth-spi.xml @@ -866,6 +866,14 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor +
+ Modifying First Broker Login Flow + + First Broker Login flow is used during first login with some identity provider. Term First Login means that there is not yet existing Keycloak account + linked with the particular authenticated identity provider account. More details about this flow are in the Identity provider chapter. + +
+
Authentication of clients Keycloak actually supports pluggable authentication for OpenID Connect diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/identity-broker.xml b/docbook/auth-server-docs/reference/en/en-US/modules/identity-broker.xml index a262b850fe..41c36f0c4f 100755 --- a/docbook/auth-server-docs/reference/en/en-US/modules/identity-broker.xml +++ b/docbook/auth-server-docs/reference/en/en-US/modules/identity-broker.xml @@ -66,7 +66,7 @@ -
+
Overview @@ -127,10 +127,11 @@ Now Keycloak is going to check if the response from the identity provider is valid. If valid, it will create an user - or just skip that if the user already exists. If it is a new user, Keycloak will ask informations about the user to the identity provider + or just skip that if the user already exists. If it is a new user, Keycloak may ask informations about the user to the identity provider (or just read that from a security token) and create the user locally. This is what we call identity federation. - If the user already exists Keycloak will ask him to link the identity returned from the identity provider - with his existing account. A process that we call account linking. + If the user already exists Keycloak may ask him to link the identity returned from the identity provider + with his existing account. A process that we call account linking. What exactly is done is configurable + and can be specified by setup of First Login Flow . At the end of this step, Keycloak authenticates the user and issues its own token in order to access the requested resource in the service provider. @@ -210,7 +211,7 @@ Social providers allows you to enable social authentication to your realm. Keycloak makes it easy to let users log in to your application using an existing account with a social network. - Currently Facebook, Google and Twitter are supported with more planned for the future. + Currently Facebook, Google, Twitter, GitHub, LinkedIn and StackOverflow are supported with more planned for the future. @@ -274,6 +275,15 @@ be used by any other means. + + + Authenticate By Default + + + If enabled, Keycloak will automatically redirect to this identity provider even before displaying login screen. + In other words, steps 3 and 4 from the base flow are skipped. + + Store Tokens @@ -293,20 +303,6 @@ to access any stored external tokens via the broker service. - - - Update Profile on First Login - - - Allows you to force users to update their profile right after the authentication finishes and - before the account is actually created in Keycloak. When "On", users will be always presented with the - update profile page asking for additional information in order to federate their identities. - When "On missing info", users will be presented with the update profile page only if some - mandatory information (email, first name, last name) is not provided by identity provider. - If "Off", the account will be created with the minimal information obtained from the identity provider - during the authentication process. - - Trust email @@ -326,6 +322,16 @@ You can put number into this field, providers with lower numbers are shown first. + + + First Login Flow + + + Alias of authentication flow, which is triggered during first login with this identity provider. Term First Login + means that there is not yet existing Keycloak account linked with the authenticated identity provider account. + More details in First Login section. + + @@ -340,8 +346,8 @@ Forcing users to register to your realm when they want to access applications is hard. So is trying to remember yet another username and password combination. Social identity providers makes it easy for users to register on your realm and quickly sign in using a social network. - Keycloak provides built-in support for the most common social networks out there, such as Google, Facebook, Twitter and - even Github. + Keycloak provides built-in support for the most common social networks out there, such as Google, Facebook, Twitter, + Github, LinkedId and StackOverflow.
@@ -1211,7 +1217,13 @@ Authorization: Bearer {keycloak_access_token}]]>
Automatically Select and Identity Provider - Applications can automatically select an identity provider in order to authenticate an user. In this case, the user will not be presented to the login page but automatically redirected to the identity provider. + Each Identity provider has option Authenticate By Default, which allows that Identity provider is automatically + selected during authentication. User won't even see Keycloak login page, but is automatically redirected to the identity provider. + + + Applications can also automatically select an identity provider in order to authenticate an user. + Selection per application is preferred over Authenticate By Default option if you need more control + on when exactly is Identity provider automatically selected. Keycloak supports a specific HTTP query parameter that you can use as a hint to tell the server which identity provider should be used to authenticate the user. @@ -1283,6 +1295,122 @@ keycloak.createLoginUrl({
+
+ First Login Flow + + When Keycloak successfully authenticates user through identity provider (step 8 in Overview chapter), + there can be two situations: + + + + There is already Keycloak user account linked with the authenticated identity provider account. In this case, + Keycloak will just authenticate as the existing user and redirect back to application (step 9 in Overview chapter). + + + + + There is not yet existing Keycloak user account linked with the identity provider account. This situation is more tricky. + Usually you just want to register new account into Keycloak database, but what if there is existing Keycloak account with same email like the identity provider account? + Automatically link identity provider account with existing Keycloak account is not very good option as there are possible security flaws related to that... + + + + + + Because we had various requirements what to do in second case, we changed the behaviour to be flexible and configurable + through Authentication Flows SPI. In admin console in Identity provider settings, there is option + First Login Flow, which allows you to choose, which workflow will be used after "first login" with this identity provider account. + By default it points to first broker login flow, but you can configure and use your own flow and use different flows for different identity providers etc. + + + The flow itself is configured in admin console under Authentication tab. When you choose First Broker Login flow, + you will see what authenticators are used by default. You can either re-configure existing flow (For example disable some authenticators, + mark some of them as required, configure some authenticators etc). Or you can even create new authentication flow and/or + write your own Authenticator implementations and use it in your flow. See Authentication Flows SPI for more details on how to do it. + + + For First Broker Login case, it might be useful if your Authenticator is subclass of org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator + so you have access to all details about authenticated Identity provider account. But it's not a requirement. + +
+ Default First Login Flow + + Let's describe the default behaviour provided by First Broker Login flow. There are those authenticators: + + + Review Profile + + + This authenticator might display the profile info page, where user can review his profile retrieved from identity provider. + The authenticator is configurable. You can set Update Profile On First Login option. + When On, users will be always presented with the profile page asking for additional information + in order to federate their identities. When missing, users will be presented with + the profile page only if some mandatory information (email, first name, last name) is not provided by identity provider. + If Off, the profile page won't be displayed, unless user clicks in later phase on Review profile info + link (page displayed in later phase by Confirm Link Existing Account authenticator) + + + + + + Create User If Unique + + + This authenticator checks if there is already existing Keycloak account with same email or username like + the account from identity provider. If it's not, then authenticator just creates new Keyclok account and + link it with identity provider and whole flow is finished. Otherwise it goes to the next Handle Existing Account subflow. + If you always want to ensure that there is no duplicated account, you can mark this authenticator as REQUIRED . + In this case, the user will see the error page if there is existing Keycloak account and user needs + to link his identity provider account through Account management. + + + This authenticator also has config option Require Password Update After Registration . + When enabled, user is required to update password after account is created. + + + + + + Confirm Link Existing Account + + + User will see the info page, that there is existing Keycloak account with same email. He can either + review his profile again and use different email or username (flow is restarted and goes back to Review Profile authenticator). + Or he can confirm that he wants to link identity provider account with his existing Keycloak account. + Disable this authenticator if you don't want users to see this confirmation page, but go straight + to linking identity provider account by email verification or re-authentication. + + + + + + Verify Existing Account By Email + + + This authenticator is ALTERNATIVE by default, so it's used only if realm has SMTP setup configured. + It will send mail to user, where he can confirm that he wants to link identity provider with his Keycloak account. + Disable this if you don't want to confirm linking by email, but instead you always want users to reauthenticate with their password (and alternatively OTP). + + + + + + Verify Existing Account By Re-authentication + + + This authenticator is used if email authenticator is disabled or non-available (SMTP not configured for realm). It + will display login screen where user needs to authenticate with his password to link his Keycloak account with Identity provider. + User can also re-authenticate with some different identity provider, which is already linked to his keycloak account. + You can also force users to use OTP, otherwise it's optional and used only if OTP is already set for user account. + + + + + + +
+
+
Examples diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index ebf3a83e46..bcbc98dc2d 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -394,7 +394,7 @@ update-profile-on-first-login.tooltip=Define conditions under which a user has t trust-email=Trust Email trust-email.tooltip=If enabled then email provided by this provider is not verified even if verification is enabled for the realm. gui-order.tooltip=Number defining order of the provider in GUI (eg. on Login page). -first-broker-login-flow.tooltip=Alias of authentication flow, which is triggered after first login with this identity provider. +first-broker-login-flow.tooltip=Alias of authentication flow, which is triggered after first login with this identity provider. Term 'First Login' means that there is not yet existing Keycloak account linked with the authenticated identity provider account. openid-connect-config=OpenID Connect Config openid-connect-config.tooltip=OIDC SP and external IDP configuration. authorization-url=Authorization URL From 4288260aa679ed51356269ac4f9b637ca6d8fed4 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 13 Nov 2015 17:37:08 +0100 Subject: [PATCH 07/16] KEYCLOAK-1822 Don't redirect to login theme when error during link identity in account mgmt. --- .../account/messages/messages_en.properties | 1 + .../login/messages/messages_en.properties | 1 - .../keycloak/models/utils/FormMessage.java | 3 ++ .../services/resources/AccountService.java | 17 +++++++++++ .../resources/IdentityBrokerService.java | 23 +++++++++++---- .../AbstractKeycloakIdentityProviderTest.java | 28 +++++++++++++++++++ .../pages/AccountFederatedIdentityPage.java | 9 ++++++ 7 files changed, 76 insertions(+), 6 deletions(-) diff --git a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties index 3d2eeea5fa..49801041cc 100755 --- a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties +++ b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties @@ -133,6 +133,7 @@ federatedIdentityLinkNotActiveMessage=This identity is not active anymore. federatedIdentityRemovingLastProviderMessage=You can''t remove last federated identity as you don''t have password. identityProviderRedirectErrorMessage=Failed to redirect to identity provider. identityProviderRemovedMessage=Identity provider removed successfully. +identityProviderAlreadyLinkedMessage=Federated identity returned by {0} is already linked to another user. accountDisabledMessage=Account is disabled, contact admin. diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties index 803ef2dcc0..68853082ca 100644 --- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties +++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties @@ -185,7 +185,6 @@ resetCredentialNotAllowedMessage=Reset Credential not allowed permissionNotApprovedMessage=Permission not approved. noRelayStateInResponseMessage=No relay state in response from identity provider. -identityProviderAlreadyLinkedMessage=The identity returned by the identity provider is already linked to another user. insufficientPermissionMessage=Insufficient permissions to link identities. couldNotProceedWithAuthenticationRequestMessage=Could not proceed with authentication request to identity provider. couldNotObtainTokenMessage=Could not obtain token from identity provider. diff --git a/model/api/src/main/java/org/keycloak/models/utils/FormMessage.java b/model/api/src/main/java/org/keycloak/models/utils/FormMessage.java index b840de6dee..9f5c5deeb6 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/FormMessage.java +++ b/model/api/src/main/java/org/keycloak/models/utils/FormMessage.java @@ -23,6 +23,9 @@ public class FormMessage { private String message; private Object[] parameters; + public FormMessage() { + } + /** * Create message. * diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index 2bebec3bfb..af24034223 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -61,6 +61,7 @@ import org.keycloak.services.messages.Messages; import org.keycloak.services.util.ResolveRelative; import org.keycloak.services.validation.Validation; import org.keycloak.common.util.UriUtils; +import org.keycloak.util.JsonSerialization; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -74,6 +75,8 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.Variant; + +import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; import java.util.HashSet; @@ -116,6 +119,9 @@ public class AccountService extends AbstractSecuredLocalService { public static final String KEYCLOAK_STATE_CHECKER = "KEYCLOAK_STATE_CHECKER"; + // Used when some other context (ie. IdentityBrokerService) wants to forward error to account management and display it here + public static final String ACCOUNT_MGMT_FORWARDED_ERROR_NOTE = "ACCOUNT_MGMT_FORWARDED_ERROR"; + private final AppAuthManager authManager; private EventBuilder event; private AccountProvider account; @@ -217,6 +223,17 @@ public class AccountService extends AbstractSecuredLocalService { setReferrerOnPage(); + String forwardedError = auth.getClientSession().getNote(ACCOUNT_MGMT_FORWARDED_ERROR_NOTE); + if (forwardedError != null) { + try { + FormMessage errorMessage = JsonSerialization.readValue(forwardedError, FormMessage.class); + account.setError(errorMessage.getMessage(), errorMessage.getParameters()); + auth.getClientSession().removeNote(ACCOUNT_MGMT_FORWARDED_ERROR_NOTE); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + return account.createResponse(page); } else { return login(path); diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index c8784bda2f..5912536c11 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -65,6 +65,7 @@ import org.keycloak.services.Urls; import org.keycloak.services.validation.Validation; import org.keycloak.social.SocialIdentityProvider; import org.keycloak.common.util.ObjectUtil; +import org.keycloak.util.JsonSerialization; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -74,6 +75,7 @@ import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; +import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; @@ -419,7 +421,6 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal return finishBrokerAuthentication(context, federatedUser, clientSession, providerId); } } catch (Exception e) { - // TODO? return redirectToErrorPage(Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR, e); } } @@ -465,7 +466,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal this.event.event(EventType.FEDERATED_IDENTITY_LINK); if (federatedUser != null) { - return redirectToErrorPage(Messages.IDENTITY_PROVIDER_ALREADY_LINKED, context.getIdpConfig().getAlias()); + return redirectToAccountErrorPage(clientSession, Messages.IDENTITY_PROVIDER_ALREADY_LINKED, context.getIdpConfig().getAlias()); } UserModel authenticatedUser = clientSession.getUserSession().getUser(); @@ -475,12 +476,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal } if (!authenticatedUser.isEnabled()) { - fireErrorEvent(Errors.USER_DISABLED); - return redirectToErrorPage(Messages.ACCOUNT_DISABLED); + return redirectToAccountErrorPage(clientSession, Messages.ACCOUNT_DISABLED); } if (!authenticatedUser.hasRole(this.realmModel.getClientByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).getRole(MANAGE_ACCOUNT))) { - fireErrorEvent(Errors.NOT_ALLOWED); return redirectToErrorPage(Messages.INSUFFICIENT_PERMISSION); } @@ -581,6 +580,20 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal return ErrorPage.error(this.session, message, parameters); } + private Response redirectToAccountErrorPage(ClientSessionModel clientSession, String message, Object ... parameters) { + fireErrorEvent(message); + + FormMessage errorMessage = new FormMessage(message, parameters); + try { + String serializedError = JsonSerialization.writeValueAsString(errorMessage); + clientSession.setNote(AccountService.ACCOUNT_MGMT_FORWARDED_ERROR_NOTE, serializedError); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + + return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build(); + } + private Response redirectToLoginPage(Throwable t, ClientSessionCode clientCode) { String message = t.getMessage(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java index 904caf6282..1d2e5b68d4 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java @@ -389,6 +389,34 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent this.updateProfilePage.assertCurrent(); } + + // KEYCLOAK-1822 + @Test + public void testAccountManagementLinkedIdentityAlreadyExists() { + // Login as "test-user" through broker + IdentityProviderModel identityProvider = getIdentityProviderModel(); + assertSuccessfulAuthentication(identityProvider, "test-user", "test-user@localhost", false); + + // Login as pedroigor to account management + accountFederatedIdentityPage.realm("realm-with-broker"); + accountFederatedIdentityPage.open(); + assertTrue(driver.getTitle().equals("Log in to realm-with-broker")); + loginPage.login("pedroigor", "password"); + assertTrue(accountFederatedIdentityPage.isCurrent()); + + // Try to link my "pedroigor" identity with "test-user" from brokered Keycloak. + accountFederatedIdentityPage.clickAddProvider(identityProvider.getAlias()); + + assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/")); + this.loginPage.login("test-user", "password"); + doAfterProviderAuthentication(); + + // Error is displayed in account management because federated identity"test-user" already linked to local account "test-user" + assertTrue(accountFederatedIdentityPage.isCurrent()); + assertEquals("Federated identity returned by " + getProviderId() + " is already linked to another user.", accountFederatedIdentityPage.getError()); + } + + @Test(expected = NoSuchElementException.class) public void testIdentityProviderNotAllowed() { this.driver.navigate().to("http://localhost:8081/test-app/"); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java index 4c0279848f..54a5cbb4ba 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java @@ -5,12 +5,17 @@ import javax.ws.rs.core.UriBuilder; import org.keycloak.services.Urls; import org.keycloak.testsuite.Constants; import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; /** * @author Marek Posolda */ public class AccountFederatedIdentityPage extends AbstractAccountPage { + @FindBy(className = "alert-error") + private WebElement errorMessage; + public AccountFederatedIdentityPage() {}; private String realmName = "test"; @@ -39,4 +44,8 @@ public class AccountFederatedIdentityPage extends AbstractAccountPage { public void clickRemoveProvider(String providerId) { driver.findElement(By.id("remove-" + providerId)).click(); } + + public String getError() { + return errorMessage.getText(); + } } From 1d5a01577efad101309105ac93b9bc7f5246d9f1 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 13 Nov 2015 17:48:48 +0100 Subject: [PATCH 08/16] KEYCLOAK-1750 First broker login - migration --- .../utils/DefaultAuthenticationFlows.java | 25 +++++++++++++------ .../models/utils/KeycloakModelUtils.java | 24 ++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java index 98e960cc40..40a8999910 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java +++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java @@ -1,6 +1,8 @@ package org.keycloak.models.utils; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import org.keycloak.models.AuthenticationExecutionModel; @@ -36,7 +38,7 @@ public class DefaultAuthenticationFlows { if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm); if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm); if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm); - if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm); + if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, false); } public static void migrateFlows(RealmModel realm) { if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm, true); @@ -44,7 +46,7 @@ public class DefaultAuthenticationFlows { if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm); if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm); if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm); - if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm); + if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, true); } public static void registrationFlow(RealmModel realm) { @@ -322,7 +324,7 @@ public class DefaultAuthenticationFlows { realm.addAuthenticatorExecution(execution); } - public static void firstBrokerLoginFlow(RealmModel realm) { + public static void firstBrokerLoginFlow(RealmModel realm, boolean migrate) { AuthenticationFlowModel firstBrokerLogin = new AuthenticationFlowModel(); firstBrokerLogin.setAlias(FIRST_BROKER_LOGIN_FLOW); firstBrokerLogin.setDescription("Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account"); @@ -423,10 +425,19 @@ public class DefaultAuthenticationFlows { execution = new AuthenticationExecutionModel(); execution.setParentFlow(verifyByReauthenticationAccountFlow.getId()); execution.setRequirement(AuthenticationExecutionModel.Requirement.OPTIONAL); - // TODO: read the requirement from browser authenticator -// if (migrate && hasCredentialType(realm, RequiredCredentialModel.TOTP.getType())) { -// execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); -// } + + if (migrate) { + // Try to read OTP requirement from browser flow + AuthenticationFlowModel browserFlow = realm.getBrowserFlow(); + List browserExecutions = new LinkedList<>(); + KeycloakModelUtils.deepFindAuthenticationExecutions(realm, browserFlow, browserExecutions); + for (AuthenticationExecutionModel browserExecution : browserExecutions) { + if (browserExecution.getAuthenticator().equals("auth-otp-form")) { + execution.setRequirement(browserExecution.getRequirement()); + } + } + } + execution.setAuthenticator("auth-otp-form"); execution.setPriority(20); execution.setAuthenticatorFlow(false); diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index c2fd73e7d9..ad5997a9a0 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -1,6 +1,8 @@ package org.keycloak.models.utils; import org.bouncycastle.openssl.PEMWriter; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.GroupModel; @@ -16,6 +18,7 @@ import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.AuthenticationExecutionRepresentation; import org.keycloak.representations.idm.CertificateRepresentation; import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.PemUtils; @@ -31,6 +34,7 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -386,4 +390,24 @@ public final class KeycloakModelUtils { realm.addDefaultRole(Constants.OFFLINE_ACCESS_ROLE); } } + + + /** + * Recursively find all AuthenticationExecutionModel from specified flow or all it's subflows + * + * @param realm + * @param flow + * @param result input should be empty list. At the end will be all executions added to this list + */ + public static void deepFindAuthenticationExecutions(RealmModel realm, AuthenticationFlowModel flow, List result) { + List executions = realm.getAuthenticationExecutions(flow.getId()); + for (AuthenticationExecutionModel execution : executions) { + if (execution.isAuthenticatorFlow()) { + AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId()); + deepFindAuthenticationExecutions(realm, subFlow, result); + } else { + result.add(execution); + } + } + } } From ea7709a104c16f37007d83ee567393218f97a4f4 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 13 Nov 2015 18:23:53 +0100 Subject: [PATCH 09/16] KEYCLOAK-2026 Fix realm.getMasterAdminClient() in JPA and Mongo models --- .../org/keycloak/models/jpa/RealmAdapter.java | 10 +++++++-- .../mongo/keycloak/adapters/RealmAdapter.java | 8 ++++++- .../mongo/keycloak/adapters/UserAdapter.java | 2 +- .../keycloak/testsuite/model/AdapterTest.java | 21 +++++++++++++++++-- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index 4a26cd83c3..7b8e3a6cca 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -1,5 +1,6 @@ package org.keycloak.models.jpa; +import org.keycloak.Config; import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.common.enums.SslRequired; import org.keycloak.models.AuthenticationExecutionModel; @@ -1195,8 +1196,13 @@ public class RealmAdapter implements RealmModel { @Override public ClientModel getMasterAdminClient() { - ClientEntity client = realm.getMasterAdminClient(); - return client!=null ? new ClientAdapter(this, em, session, realm.getMasterAdminClient()) : null; + ClientEntity masterAdminClient = realm.getMasterAdminClient(); + if (masterAdminClient == null) { + return null; + } + + RealmAdapter masterRealm = new RealmAdapter(session, em, masterAdminClient.getRealm()); + return new ClientAdapter(masterRealm, em, session, masterAdminClient); } @Override diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index 59bc5536f5..30266af27a 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -1223,7 +1223,13 @@ public class RealmAdapter extends AbstractMongoAdapter impleme @Override public ClientModel getMasterAdminClient() { MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getMasterAdminClient(), invocationContext); - return appData != null ? new ClientAdapter(session, this, appData, invocationContext) : null; + if (appData == null) { + return null; + } + + MongoRealmEntity masterRealm = getMongoStore().loadEntity(MongoRealmEntity.class, appData.getRealmId(), invocationContext); + RealmModel masterRealmModel = new RealmAdapter(session, masterRealm, invocationContext); + return new ClientAdapter(session, masterRealmModel, appData, invocationContext); } @Override diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java index 87729d4a6b..83979c3dff 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java @@ -453,7 +453,7 @@ public class UserAdapter extends AbstractMongoAdapter implement @Override public Set getGroups() { - if (user.getGroupIds() == null && user.getGroupIds().size() == 0) return Collections.EMPTY_SET; + if (user.getGroupIds() == null || user.getGroupIds().size() == 0) return Collections.EMPTY_SET; Set groups = new HashSet<>(); for (String id : user.getGroupIds()) { groups.add(realm.getGroupById(id)); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java index 4e771dfefd..8c84581ae7 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java @@ -4,6 +4,7 @@ import org.junit.Assert; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; +import org.keycloak.Config; import org.keycloak.models.ClientModel; import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.ModelDuplicateException; @@ -146,11 +147,11 @@ public class AdapterTest extends AbstractModelTest { Assert.assertTrue(userProvider.validCredentials(realmModel, user, UserCredentialModel.password("geheim"))); List creds = user.getCredentialsDirectly(); Assert.assertEquals(creds.get(0).getHashIterations(), 1); - realmModel.setPasswordPolicy( new PasswordPolicy("hashIterations(200)")); + realmModel.setPasswordPolicy(new PasswordPolicy("hashIterations(200)")); Assert.assertTrue(userProvider.validCredentials(realmModel, user, UserCredentialModel.password("geheim"))); creds = user.getCredentialsDirectly(); Assert.assertEquals(creds.get(0).getHashIterations(), 200); - realmModel.setPasswordPolicy( new PasswordPolicy("hashIterations(1)")); + realmModel.setPasswordPolicy(new PasswordPolicy("hashIterations(1)")); } @Test @@ -797,6 +798,22 @@ public class AdapterTest extends AbstractModelTest { } + // KEYCLOAK-2026 + @Test + public void testMasterAdminClient() { + realmModel = realmManager.createRealm("foo-realm"); + ClientModel masterAdminClient = realmModel.getMasterAdminClient(); + Assert.assertEquals(Config.getAdminRealm(), masterAdminClient.getRealm().getId()); + + commit(); + + realmModel = realmManager.getRealmByName("foo-realm"); + masterAdminClient = realmModel.getMasterAdminClient(); + Assert.assertEquals(Config.getAdminRealm(), masterAdminClient.getRealm().getId()); + + realmManager.removeRealm(realmModel); + } + private KeyPair generateKeypair() throws NoSuchAlgorithmException { return KeyPairGenerator.getInstance("RSA").generateKeyPair(); } From bad0a951231bad14e1d283a22e05820b5999e46d Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Wed, 11 Nov 2015 14:31:39 +0100 Subject: [PATCH 10/16] KEYCLOAK-1749 Client registration service Changed endpoints of client registration to just clients Started installation Added adapter config retrival to client reg --- .../keycloak/client/registration/Auth.java | 58 ++++ .../registration/ClientRegistration.java | 252 +++-------------- .../client/registration/HttpUtil.java | 159 +++++++++++ .../META-INF/jpa-changelog-1.7.0.xml | 3 + .../idm/ClientRepresentation.java | 10 + .../java/org/keycloak/models/ClientModel.java | 3 + .../org/keycloak/models/KeycloakContext.java | 3 + .../models/entities/ClientEntity.java | 9 + .../models/utils/ModelToRepresentation.java | 4 +- .../models/utils/RepresentationToModel.java | 3 + .../cache/infinispan/ClientAdapter.java | 9 + .../models/cache/entities/CachedClient.java | 6 + .../keycloak/models/jpa/ClientAdapter.java | 10 + .../models/jpa/entities/ClientEntity.java | 10 + .../keycloak/adapters/ClientAdapter.java | 11 + .../EntityDescriptorDescriptionConverter.java | 4 +- ...yDescriptorClientRegistrationProvider.java | 79 ++++++ ...ptorClientRegistrationProviderFactory.java | 38 +++ ...stration.ClientRegistrationProviderFactory | 1 + .../oidc/OIDCClientDescriptionConverter.java | 16 +- .../oidc/endpoints/LogoutEndpoint.java | 2 +- .../oidc/endpoints/TokenEndpoint.java | 2 +- .../OIDCClientRepresentation.java | 132 +++++++++ .../oidc/utils/AuthorizeClientUtil.java | 32 ++- .../services/DefaultKeycloakContext.java | 8 + ...nstallationClientRegistrationProvider.java | 92 ++++++ ...tionClientRegistrationProviderFactory.java | 34 +++ .../clientregistration/ClientRegAuth.java | 128 +++++++++ .../ClientRegistrationProvider.java | 2 +- .../ClientRegistrationService.java | 21 +- .../DefaultClientRegistrationProvider.java | 100 ++----- .../OIDCClientRegistrationProvider.java | 34 --- .../clientregistration/TokenGenerator.java | 27 ++ .../oidc/DescriptionConverter.java | 38 +++ .../oidc/OIDCClientRegistrationProvider.java | 100 +++++++ ...OIDCClientRegistrationProviderFactory.java | 4 +- .../OIDCClientResponseRepresentation.java | 77 ++++++ .../resources/ClientsManagementService.java | 2 +- .../services/resources/RealmsResource.java | 4 +- ...stration.ClientRegistrationProviderFactory | 4 +- .../AbstractClientRegistrationTest.java | 96 +++++++ .../client/AdapterInstallationConfigTest.java | 103 +++++++ .../client/ClientRegistrationTest.java | 261 ++++++------------ .../client/RegistrationAccessTokenTest.java | 94 +++++++ 44 files changed, 1542 insertions(+), 543 deletions(-) create mode 100644 client-api/src/main/java/org/keycloak/client/registration/Auth.java create mode 100644 client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java create mode 100644 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java create mode 100644 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java create mode 100644 saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory create mode 100644 services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java create mode 100644 services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProviderFactory.java create mode 100644 services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java delete mode 100644 services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java create mode 100644 services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java create mode 100644 services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java create mode 100644 services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java rename services/src/main/java/org/keycloak/services/clientregistration/{ => oidc}/OIDCClientRegistrationProviderFactory.java (77%) create mode 100644 services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java diff --git a/client-api/src/main/java/org/keycloak/client/registration/Auth.java b/client-api/src/main/java/org/keycloak/client/registration/Auth.java new file mode 100644 index 0000000000..5b0e85fa8d --- /dev/null +++ b/client-api/src/main/java/org/keycloak/client/registration/Auth.java @@ -0,0 +1,58 @@ +package org.keycloak.client.registration; + +import org.apache.http.HttpHeaders; +import org.apache.http.HttpRequest; +import org.keycloak.common.util.Base64; +import org.keycloak.representations.idm.ClientRepresentation; + +/** + * @author Stian Thorgersen + */ +public abstract class Auth { + + public abstract void addAuth(HttpRequest request); + + public static Auth token(String token) { + return new BearerTokenAuth(token); + } + + public static Auth token(ClientRepresentation client) { + return new BearerTokenAuth(client.getRegistrationAccessToken()); + } + + public static Auth client(String clientId, String clientSecret) { + return new BasicAuth(clientId, clientSecret); + } + + private static class BearerTokenAuth extends Auth { + + private String token; + + public BearerTokenAuth(String token) { + this.token = token; + } + + @Override + public void addAuth(HttpRequest request) { + request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token); + } + } + + private static class BasicAuth extends Auth { + + private String username; + private String password; + + public BasicAuth(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public void addAuth(HttpRequest request) { + String val = Base64.encodeBytes((username + ":" + password).getBytes()); + request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + val); + } + } + +} diff --git a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java index 82a3b3759b..e59de7634c 100644 --- a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java +++ b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java @@ -1,18 +1,9 @@ package org.keycloak.client.registration; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.common.util.Base64; import org.keycloak.util.JsonSerialization; import java.io.IOException; @@ -23,160 +14,58 @@ import java.io.InputStream; */ public class ClientRegistration { - private String clientRegistrationUrl; - private HttpClient httpClient; - private Auth auth; + private final String DEFAULT = "default"; + private final String INSTALLATION = "install"; - public static ClientRegistrationBuilder create() { - return new ClientRegistrationBuilder(); + private HttpUtil httpUtil; + + public ClientRegistration(String authServerUrl, String realm) { + httpUtil = new HttpUtil(HttpClients.createDefault(), HttpUtil.getUrl(authServerUrl, "realms", realm, "clients")); } - private ClientRegistration() { + public ClientRegistration(String authServerUrl, String realm, HttpClient httpClient) { + httpUtil = new HttpUtil(httpClient, HttpUtil.getUrl(authServerUrl, "realms", realm, "clients")); + } + + public void close() throws ClientRegistrationException { + if (httpUtil != null) { + httpUtil.close(); + } + httpUtil = null; + } + + public ClientRegistration auth(Auth auth) { + httpUtil.setAuth(auth); + return this; } public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException { String content = serialize(client); - InputStream resultStream = doPost(content); + InputStream resultStream = httpUtil.doPost(content, DEFAULT); return deserialize(resultStream, ClientRepresentation.class); } - public ClientRepresentation get() throws ClientRegistrationException { - if (auth instanceof ClientIdSecretAuth) { - String clientId = ((ClientIdSecretAuth) auth).clientId; - return get(clientId); - } else { - throw new ClientRegistrationException("Requires client authentication"); - } + public ClientRepresentation get(String clientId) throws ClientRegistrationException { + InputStream resultStream = httpUtil.doGet(DEFAULT, clientId); + return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null; } - public ClientRepresentation get(String clientId) throws ClientRegistrationException { - InputStream resultStream = doGet(clientId); - return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null; + public AdapterConfig getAdapterConfig(String clientId) throws ClientRegistrationException { + InputStream resultStream = httpUtil.doGet(INSTALLATION, clientId); + return resultStream != null ? deserialize(resultStream, AdapterConfig.class) : null; } public void update(ClientRepresentation client) throws ClientRegistrationException { String content = serialize(client); - doPut(content, client.getClientId()); + httpUtil.doPut(content, DEFAULT, client.getClientId()); } - public void delete() throws ClientRegistrationException { - if (auth instanceof ClientIdSecretAuth) { - String clientId = ((ClientIdSecretAuth) auth).clientId; - delete(clientId); - } else { - throw new ClientRegistrationException("Requires client authentication"); - } + public void delete(ClientRepresentation client) throws ClientRegistrationException { + delete(client.getClientId()); } public void delete(String clientId) throws ClientRegistrationException { - doDelete(clientId); - } - - public void close() throws ClientRegistrationException { - if (httpClient instanceof CloseableHttpClient) { - try { - ((CloseableHttpClient) httpClient).close(); - } catch (IOException e) { - throw new ClientRegistrationException("Failed to close http client", e); - } - } - } - - private InputStream doPost(String content) throws ClientRegistrationException { - try { - HttpPost request = new HttpPost(clientRegistrationUrl); - - request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); - request.setHeader(HttpHeaders.ACCEPT, "application/json"); - request.setEntity(new StringEntity(content)); - - auth.addAuth(request); - - HttpResponse response = httpClient.execute(request); - InputStream responseStream = null; - if (response.getEntity() != null) { - responseStream = response.getEntity().getContent(); - } - - if (response.getStatusLine().getStatusCode() == 201) { - return responseStream; - } else { - responseStream.close(); - throw new HttpErrorException(response.getStatusLine()); - } - } catch (IOException e) { - throw new ClientRegistrationException("Failed to send request", e); - } - } - - private InputStream doGet(String endpoint) throws ClientRegistrationException { - try { - HttpGet request = new HttpGet(clientRegistrationUrl + "/" + endpoint); - - request.setHeader(HttpHeaders.ACCEPT, "application/json"); - - auth.addAuth(request); - - HttpResponse response = httpClient.execute(request); - InputStream responseStream = null; - if (response.getEntity() != null) { - responseStream = response.getEntity().getContent(); - } - - if (response.getStatusLine().getStatusCode() == 200) { - return responseStream; - } else if (response.getStatusLine().getStatusCode() == 404) { - responseStream.close(); - return null; - } else { - responseStream.close(); - throw new HttpErrorException(response.getStatusLine()); - } - } catch (IOException e) { - throw new ClientRegistrationException("Failed to send request", e); - } - } - - private void doPut(String content, String endpoint) throws ClientRegistrationException { - try { - HttpPut request = new HttpPut(clientRegistrationUrl + "/" + endpoint); - - request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); - request.setHeader(HttpHeaders.ACCEPT, "application/json"); - request.setEntity(new StringEntity(content)); - - auth.addAuth(request); - - HttpResponse response = httpClient.execute(request); - if (response.getEntity() != null) { - response.getEntity().getContent().close(); - } - - if (response.getStatusLine().getStatusCode() != 200) { - throw new HttpErrorException(response.getStatusLine()); - } - } catch (IOException e) { - throw new ClientRegistrationException("Failed to send request", e); - } - } - - private void doDelete(String endpoint) throws ClientRegistrationException { - try { - HttpDelete request = new HttpDelete(clientRegistrationUrl + "/" + endpoint); - - auth.addAuth(request); - - HttpResponse response = httpClient.execute(request); - if (response.getEntity() != null) { - response.getEntity().getContent().close(); - } - - if (response.getStatusLine().getStatusCode() != 200) { - throw new HttpErrorException(response.getStatusLine()); - } - } catch (IOException e) { - throw new ClientRegistrationException("Failed to send request", e); - } + httpUtil.doDelete(DEFAULT, clientId); } private String serialize(ClientRepresentation client) throws ClientRegistrationException { @@ -195,81 +84,4 @@ public class ClientRegistration { } } - public static class ClientRegistrationBuilder { - - private String realm; - - private String authServerUrl; - - private Auth auth; - - private HttpClient httpClient; - - public ClientRegistrationBuilder realm(String realm) { - this.realm = realm; - return this; - } - public ClientRegistrationBuilder authServerUrl(String authServerUrl) { - this.authServerUrl = authServerUrl; - return this; - } - - public ClientRegistrationBuilder auth(String token) { - this.auth = new TokenAuth(token); - return this; - } - - public ClientRegistrationBuilder auth(String clientId, String clientSecret) { - this.auth = new ClientIdSecretAuth(clientId, clientSecret); - return this; - } - - public ClientRegistrationBuilder httpClient(HttpClient httpClient) { - this.httpClient = httpClient; - return this; - } - - public ClientRegistration build() { - ClientRegistration clientRegistration = new ClientRegistration(); - clientRegistration.clientRegistrationUrl = authServerUrl + "/realms/" + realm + "/client-registration/default"; - - clientRegistration.httpClient = httpClient != null ? httpClient : HttpClients.createDefault(); - clientRegistration.auth = auth; - - return clientRegistration; - } - - } - - public interface Auth { - void addAuth(HttpRequest httpRequest); - } - - public static class AuthorizationHeaderAuth implements Auth { - private String credentials; - - public AuthorizationHeaderAuth(String credentials) { - this.credentials = credentials; - } - - public void addAuth(HttpRequest httpRequest) { - httpRequest.setHeader(HttpHeaders.AUTHORIZATION, credentials); - } - } - - public static class TokenAuth extends AuthorizationHeaderAuth { - public TokenAuth(String token) { - super("Bearer " + token); - } - } - - public static class ClientIdSecretAuth extends AuthorizationHeaderAuth { - private String clientId; - - public ClientIdSecretAuth(String clientId, String clientSecret) { - super("Basic " + Base64.encodeBytes((clientId + ":" + clientSecret).getBytes())); - this.clientId = clientId; - } - } - } diff --git a/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java new file mode 100644 index 0000000000..699d378020 --- /dev/null +++ b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java @@ -0,0 +1,159 @@ +package org.keycloak.client.registration; + +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.*; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.keycloak.client.registration.Auth; +import org.keycloak.client.registration.ClientRegistrationException; +import org.keycloak.client.registration.HttpErrorException; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author Stian Thorgersen + */ +class HttpUtil { + + private HttpClient httpClient; + + private String baseUri; + + private Auth auth; + + HttpUtil(HttpClient httpClient, String baseUri) { + this.httpClient = httpClient; + this.baseUri = baseUri; + } + + void setAuth(Auth auth) { + this.auth = auth; + } + + InputStream doPost(String content, String... path) throws ClientRegistrationException { + try { + HttpPost request = new HttpPost(getUrl(baseUri, path)); + + request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + request.setHeader(HttpHeaders.ACCEPT, "application/json"); + request.setEntity(new StringEntity(content)); + + addAuth(request); + + HttpResponse response = httpClient.execute(request); + InputStream responseStream = null; + if (response.getEntity() != null) { + responseStream = response.getEntity().getContent(); + } + + if (response.getStatusLine().getStatusCode() == 201) { + return responseStream; + } else { + responseStream.close(); + throw new HttpErrorException(response.getStatusLine()); + } + } catch (IOException e) { + throw new ClientRegistrationException("Failed to send request", e); + } + } + + InputStream doGet(String... path) throws ClientRegistrationException { + try { + HttpGet request = new HttpGet(getUrl(baseUri, path)); + + request.setHeader(HttpHeaders.ACCEPT, "application/json"); + + addAuth(request); + + HttpResponse response = httpClient.execute(request); + InputStream responseStream = null; + if (response.getEntity() != null) { + responseStream = response.getEntity().getContent(); + } + + if (response.getStatusLine().getStatusCode() == 200) { + return responseStream; + } else if (response.getStatusLine().getStatusCode() == 404) { + responseStream.close(); + return null; + } else { + responseStream.close(); + throw new HttpErrorException(response.getStatusLine()); + } + } catch (IOException e) { + throw new ClientRegistrationException("Failed to send request", e); + } + } + + void doPut(String content, String... path) throws ClientRegistrationException { + try { + HttpPut request = new HttpPut(getUrl(baseUri, path)); + + request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + request.setHeader(HttpHeaders.ACCEPT, "application/json"); + request.setEntity(new StringEntity(content)); + + addAuth(request); + + HttpResponse response = httpClient.execute(request); + if (response.getEntity() != null) { + response.getEntity().getContent().close(); + } + + if (response.getStatusLine().getStatusCode() != 200) { + throw new HttpErrorException(response.getStatusLine()); + } + } catch (IOException e) { + throw new ClientRegistrationException("Failed to send request", e); + } + } + + void doDelete(String... path) throws ClientRegistrationException { + try { + HttpDelete request = new HttpDelete(getUrl(baseUri, path)); + + addAuth(request); + + HttpResponse response = httpClient.execute(request); + if (response.getEntity() != null) { + response.getEntity().getContent().close(); + } + + if (response.getStatusLine().getStatusCode() != 200) { + throw new HttpErrorException(response.getStatusLine()); + } + } catch (IOException e) { + throw new ClientRegistrationException("Failed to send request", e); + } + } + + void close() throws ClientRegistrationException { + if (httpClient instanceof CloseableHttpClient) { + try { + ((CloseableHttpClient) httpClient).close(); + } catch (IOException e) { + throw new ClientRegistrationException("Failed to close http client", e); + } + } + } + + static String getUrl(String baseUri, String... path) { + StringBuilder s = new StringBuilder(); + s.append(baseUri); + for (String p : path) { + s.append('/'); + s.append(p); + } + return s.toString(); + } + + private void addAuth(HttpRequestBase request) { + if (auth != null) { + auth.addAuth(request); + } + } + +} diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml index 1120252613..aed99fcbe1 100755 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml @@ -58,6 +58,9 @@ + + + \ No newline at end of file diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java index 099950512b..514c0fb953 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java @@ -19,6 +19,7 @@ public class ClientRepresentation { protected Boolean enabled; protected String clientAuthenticatorType; protected String secret; + protected String registrationAccessToken; protected String[] defaultRoles; protected List redirectUris; protected List webOrigins; @@ -124,6 +125,14 @@ public class ClientRepresentation { this.secret = secret; } + public String getRegistrationAccessToken() { + return registrationAccessToken; + } + + public void setRegistrationAccessToken(String registrationAccessToken) { + this.registrationAccessToken = registrationAccessToken; + } + public List getRedirectUris() { return redirectUris; } @@ -251,4 +260,5 @@ public class ClientRepresentation { public void setProtocolMappers(List protocolMappers) { this.protocolMappers = protocolMappers; } + } diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java index 8753f45bd0..c421aeab1e 100755 --- a/model/api/src/main/java/org/keycloak/models/ClientModel.java +++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java @@ -90,6 +90,9 @@ public interface ClientModel extends RoleContainerModel { String getSecret(); public void setSecret(String secret); + String getRegistrationSecret(); + void setRegistrationSecret(String registrationSecret); + boolean isFullScopeAllowed(); void setFullScopeAllowed(boolean value); diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java index cf8bc6dbf5..f430e5f64f 100755 --- a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java +++ b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java @@ -5,6 +5,7 @@ import org.keycloak.models.utils.RealmImporter; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.UriInfo; +import java.net.URI; import java.util.Locale; /** @@ -12,6 +13,8 @@ import java.util.Locale; */ public interface KeycloakContext { + URI getAuthServerUrl(); + String getContextPath(); UriInfo getUri(); diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java index aa4f725b09..f15614fe4b 100755 --- a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java +++ b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java @@ -17,6 +17,7 @@ public class ClientEntity extends AbstractIdentifiableEntity { private boolean enabled; private String clientAuthenticatorType; private String secret; + private String registrationSecret; private String protocol; private int notBefore; private boolean publicClient; @@ -90,6 +91,14 @@ public class ClientEntity extends AbstractIdentifiableEntity { this.secret = secret; } + public String getRegistrationSecret() { + return registrationSecret; + } + + public void setRegistrationSecret(String registrationSecret) { + this.registrationSecret = registrationSecret; + } + public int getNotBefore() { return notBefore; } diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 59c640c16b..0df3516980 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -11,8 +11,6 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.ModelException; import org.keycloak.models.OTPPolicy; -import org.keycloak.models.session.PersistentClientSessionModel; -import org.keycloak.models.session.PersistentUserSessionModel; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredActionProviderModel; @@ -47,7 +45,6 @@ import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.common.util.Time; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -390,6 +387,7 @@ public class ModelToRepresentation { rep.setNotBefore(clientModel.getNotBefore()); rep.setNodeReRegistrationTimeout(clientModel.getNodeReRegistrationTimeout()); rep.setClientAuthenticatorType(clientModel.getClientAuthenticatorType()); + rep.setRegistrationAccessToken(clientModel.getRegistrationSecret()); Set redirectUris = clientModel.getRedirectUris(); if (redirectUris != null) { diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index a31d35592b..a46ee57503 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -737,6 +737,8 @@ public class RepresentationToModel { KeycloakModelUtils.generateSecret(client); } + client.setRegistrationSecret(resourceRep.getRegistrationAccessToken()); + if (resourceRep.getAttributes() != null) { for (Map.Entry entry : resourceRep.getAttributes().entrySet()) { client.setAttribute(entry.getKey(), entry.getValue()); @@ -813,6 +815,7 @@ public class RepresentationToModel { if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired()); if (rep.getNodeReRegistrationTimeout() != null) resource.setNodeReRegistrationTimeout(rep.getNodeReRegistrationTimeout()); if (rep.getClientAuthenticatorType() != null) resource.setClientAuthenticatorType(rep.getClientAuthenticatorType()); + if (rep.getRegistrationAccessToken() != null) resource.setRegistrationSecret(rep.getRegistrationAccessToken()); resource.updateClient(); if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol()); diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java index 477c5d6ab2..7a873735bc 100755 --- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java +++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java @@ -120,6 +120,15 @@ public class ClientAdapter implements ClientModel { getDelegateForUpdate(); updated.setSecret(secret); } + public String getRegistrationSecret() { + if (updated != null) return updated.getRegistrationSecret(); + return cached.getRegistrationSecret(); + } + + public void setRegistrationSecret(String registrationsecret) { + getDelegateForUpdate(); + updated.setRegistrationSecret(registrationsecret); + } public boolean isPublicClient() { if (updated != null) return updated.isPublicClient(); diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java index 2d582222a8..1c04b9df10 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java @@ -31,6 +31,7 @@ public class CachedClient implements Serializable { private boolean enabled; private String clientAuthenticatorType; private String secret; + private String registrationSecret; private String protocol; private Map attributes = new HashMap(); private boolean publicClient; @@ -57,6 +58,7 @@ public class CachedClient implements Serializable { id = model.getId(); clientAuthenticatorType = model.getClientAuthenticatorType(); secret = model.getSecret(); + registrationSecret = model.getRegistrationSecret(); clientId = model.getClientId(); name = model.getName(); description = model.getDescription(); @@ -129,6 +131,10 @@ public class CachedClient implements Serializable { return secret; } + public String getRegistrationSecret() { + return registrationSecret; + } + public boolean isPublicClient() { return publicClient; } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java index 3baddd19a7..5ea0b11619 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java @@ -177,6 +177,16 @@ public class ClientAdapter implements ClientModel { entity.setSecret(secret); } + @Override + public String getRegistrationSecret() { + return entity.getRegistrationSecret(); + } + + @Override + public void setRegistrationSecret(String registrationSecret) { + entity.setRegistrationSecret(registrationSecret); + } + @Override public boolean validateSecret(String secret) { return secret.equals(entity.getSecret()); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java index f26f65126e..881b12967f 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java @@ -42,6 +42,8 @@ public class ClientEntity { private boolean enabled; @Column(name="SECRET") private String secret; + @Column(name="REGISTRATION_SECRET") + private String registrationSecret; @Column(name="CLIENT_AUTHENTICATOR_TYPE") private String clientAuthenticatorType; @Column(name="NOT_BEFORE") @@ -201,6 +203,14 @@ public class ClientEntity { this.secret = secret; } + public String getRegistrationSecret() { + return registrationSecret; + } + + public void setRegistrationSecret(String registrationSecret) { + this.registrationSecret = registrationSecret; + } + public int getNotBefore() { return notBefore; } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java index e99f142297..cbacd096d2 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java @@ -177,6 +177,17 @@ public class ClientAdapter extends AbstractMongoAdapter imple updateMongoEntity(); } + @Override + public String getRegistrationSecret() { + return getMongoEntity().getRegistrationSecret(); + } + + @Override + public void setRegistrationSecret(String registrationSecretsecret) { + getMongoEntity().setRegistrationSecret(registrationSecretsecret); + updateMongoEntity(); + } + @Override public boolean isPublicClient() { return getMongoEntity().isPublicClient(); diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java index da9961301a..42ff3ee0c0 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java @@ -33,6 +33,8 @@ import java.util.Map; */ public class EntityDescriptorDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory { + public static final String ID = "saml2-entity-descriptor"; + @Override public boolean isSupported(String description) { description = description.trim(); @@ -161,7 +163,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo @Override public String getId() { - return "saml2-entity-descriptor"; + return ID; } } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java new file mode 100644 index 0000000000..298623d582 --- /dev/null +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java @@ -0,0 +1,79 @@ +package org.keycloak.protocol.saml.clientregistration; + +import org.jboss.logging.Logger; +import org.keycloak.events.EventBuilder; +import org.keycloak.events.EventType; +import org.keycloak.exportimport.ClientDescriptionConverter; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.models.utils.RepresentationToModel; +import org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.services.ErrorResponse; +import org.keycloak.services.clientregistration.ClientRegAuth; +import org.keycloak.services.clientregistration.ClientRegistrationProvider; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.net.URI; + +/** + * @author Stian Thorgersen + */ +public class EntityDescriptorClientRegistrationProvider implements ClientRegistrationProvider { + + private static final Logger logger = Logger.getLogger(EntityDescriptorClientRegistrationProvider.class); + + private KeycloakSession session; + private EventBuilder event; + private ClientRegAuth auth; + + public EntityDescriptorClientRegistrationProvider(KeycloakSession session) { + this.session = session; + } + +// @POST +// @Consumes(MediaType.APPLICATION_XML) +// @Produces(MediaType.APPLICATION_JSON) +// public Response create(String descriptor) { +// event.event(EventType.CLIENT_REGISTER); +// +// auth.requireCreate(); +// +// ClientRepresentation client = session.getProvider(ClientDescriptionConverter.class, EntityDescriptorDescriptionConverter.ID).convertToInternal(descriptor); +// +// try { +// ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true); +// client = ModelToRepresentation.toRepresentation(clientModel); +// URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build(); +// +// logger.infov("Created client {0}", client.getClientId()); +// +// event.client(client.getClientId()).success(); +// +// return Response.created(uri).entity(client).build(); +// } catch (ModelDuplicateException e) { +// return ErrorResponse.exists("Client " + client.getClientId() + " already exists"); +// } +// } + + @Override + public void close() { + } + + @Override + public void setAuth(ClientRegAuth auth) { + this.auth = auth; + } + + @Override + public void setEvent(EventBuilder event) { + this.event = event; + } + +} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java new file mode 100644 index 0000000000..393ff36fc2 --- /dev/null +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java @@ -0,0 +1,38 @@ +package org.keycloak.protocol.saml.clientregistration; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.clientregistration.ClientRegistrationProvider; +import org.keycloak.services.clientregistration.ClientRegistrationProviderFactory; + +/** + * @author Stian Thorgersen + */ +public class EntityDescriptorClientRegistrationProviderFactory implements ClientRegistrationProviderFactory { + + public static final String ID = "saml2-entity-descriptor"; + + @Override + public ClientRegistrationProvider create(KeycloakSession session) { + return new EntityDescriptorClientRegistrationProvider(session); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return ID; + } + +} diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory new file mode 100644 index 0000000000..e4f81175e6 --- /dev/null +++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory @@ -0,0 +1 @@ +org.keycloak.protocol.saml.clientregistration.EntityDescriptorClientRegistrationProviderFactory \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java index 56b47bb585..955dfe4769 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java @@ -8,6 +8,7 @@ import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.services.clientregistration.oidc.DescriptionConverter; import org.keycloak.util.JsonSerialization; import java.io.IOException; @@ -17,6 +18,8 @@ import java.io.IOException; */ public class OIDCClientDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory { + public static final String ID = "openid-connect"; + @Override public boolean isSupported(String description) { description = description.trim(); @@ -26,15 +29,8 @@ public class OIDCClientDescriptionConverter implements ClientDescriptionConverte @Override public ClientRepresentation convertToInternal(String description) { try { - OIDCClientRepresentation oidcRep = JsonSerialization.readValue(description, OIDCClientRepresentation.class); - - ClientRepresentation client = new ClientRepresentation(); - client.setClientId(KeycloakModelUtils.generateId()); - client.setName(oidcRep.getClientName()); - client.setRedirectUris(oidcRep.getRedirectUris()); - client.setBaseUrl(oidcRep.getClientUri()); - - return client; + OIDCClientRepresentation clientOIDC = JsonSerialization.readValue(description, OIDCClientRepresentation.class); + return DescriptionConverter.toInternal(clientOIDC); } catch (IOException e) { throw new RuntimeException(e); } @@ -59,7 +55,7 @@ public class OIDCClientDescriptionConverter implements ClientDescriptionConverte @Override public String getId() { - return "openid-connect"; + return ID; } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java index 9b862eff8f..2250dd076f 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java @@ -190,7 +190,7 @@ public class LogoutEndpoint { } private ClientModel authorizeClient() { - ClientModel client = AuthorizeClientUtil.authorizeClient(session, event, realm).getClient(); + ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient(); if (client.isBearerOnly()) { throw new ErrorResponseException("invalid_client", "Bearer-only not allowed", Response.Status.BAD_REQUEST); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java index 1fb3e4a727..34161d8b5c 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java @@ -145,7 +145,7 @@ public class TokenEndpoint { } private void checkClient() { - AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm); + AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event); client = clientAuth.getClient(); clientAuthAttributes = clientAuth.getClientAuthAttributes(); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java index 7de415c984..4a83ebddce 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java @@ -12,12 +12,48 @@ public class OIDCClientRepresentation { @JsonProperty("redirect_uris") private List redirectUris; + @JsonProperty("token_endpoint_auth_method") + private String tokenEndpointAuthMethod; + + @JsonProperty("grant_types") + private String grantTypes; + + @JsonProperty("response_types") + private String responseTypes; + @JsonProperty("client_name") private String clientName; @JsonProperty("client_uri") private String clientUri; + @JsonProperty("logo_uri") + private String logoUri; + + @JsonProperty("scope") + private String scope; + + @JsonProperty("contacts") + private String contacts; + + @JsonProperty("tos_uri") + private String tos_uri; + + @JsonProperty("policy_uri") + private String policy_uri; + + @JsonProperty("jwks_uri") + private String jwks_uri; + + @JsonProperty("jwks") + private String jwks; + + @JsonProperty("software_id") + private String softwareId; + + @JsonProperty("software_version") + private String softwareVersion; + public List getRedirectUris() { return redirectUris; } @@ -26,6 +62,30 @@ public class OIDCClientRepresentation { this.redirectUris = redirectUris; } + public String getTokenEndpointAuthMethod() { + return tokenEndpointAuthMethod; + } + + public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { + this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; + } + + public String getGrantTypes() { + return grantTypes; + } + + public void setGrantTypes(String grantTypes) { + this.grantTypes = grantTypes; + } + + public String getResponseTypes() { + return responseTypes; + } + + public void setResponseTypes(String responseTypes) { + this.responseTypes = responseTypes; + } + public String getClientName() { return clientName; } @@ -42,4 +102,76 @@ public class OIDCClientRepresentation { this.clientUri = clientUri; } + public String getLogoUri() { + return logoUri; + } + + public void setLogoUri(String logoUri) { + this.logoUri = logoUri; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getContacts() { + return contacts; + } + + public void setContacts(String contacts) { + this.contacts = contacts; + } + + public String getTos_uri() { + return tos_uri; + } + + public void setTos_uri(String tos_uri) { + this.tos_uri = tos_uri; + } + + public String getPolicy_uri() { + return policy_uri; + } + + public void setPolicy_uri(String policy_uri) { + this.policy_uri = policy_uri; + } + + public String getJwks_uri() { + return jwks_uri; + } + + public void setJwks_uri(String jwks_uri) { + this.jwks_uri = jwks_uri; + } + + public String getJwks() { + return jwks; + } + + public void setJwks(String jwks) { + this.jwks = jwks; + } + + public String getSoftwareId() { + return softwareId; + } + + public void setSoftwareId(String softwareId) { + this.softwareId = softwareId; + } + + public String getSoftwareVersion() { + return softwareVersion; + } + + public void setSoftwareVersion(String softwareVersion) { + this.softwareVersion = softwareVersion; + } + } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java index c017ba3019..a2d0d6c778 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java @@ -19,18 +19,8 @@ import javax.ws.rs.core.Response; */ public class AuthorizeClientUtil { - public static ClientAuthResult authorizeClient(KeycloakSession session, EventBuilder event, RealmModel realm) { - AuthenticationFlowModel clientAuthFlow = realm.getClientAuthenticationFlow(); - String flowId = clientAuthFlow.getId(); - - AuthenticationProcessor processor = new AuthenticationProcessor(); - processor.setFlowId(flowId) - .setConnection(session.getContext().getConnection()) - .setEventBuilder(event) - .setRealm(realm) - .setSession(session) - .setUriInfo(session.getContext().getUri()) - .setRequest(session.getContext().getContextObject(HttpRequest.class)); + public static ClientAuthResult authorizeClient(KeycloakSession session, EventBuilder event) { + AuthenticationProcessor processor = getAuthenticationProcessor(session, event); Response response = processor.authenticateClient(); if (response != null) { @@ -45,6 +35,24 @@ public class AuthorizeClientUtil { return new ClientAuthResult(client, processor.getClientAuthAttributes()); } + public static AuthenticationProcessor getAuthenticationProcessor(KeycloakSession session, EventBuilder event) { + RealmModel realm = session.getContext().getRealm(); + + AuthenticationFlowModel clientAuthFlow = realm.getClientAuthenticationFlow(); + String flowId = clientAuthFlow.getId(); + + AuthenticationProcessor processor = new AuthenticationProcessor(); + processor.setFlowId(flowId) + .setConnection(session.getContext().getConnection()) + .setEventBuilder(event) + .setRealm(realm) + .setSession(session) + .setUriInfo(session.getContext().getUri()) + .setRequest(session.getContext().getContextObject(HttpRequest.class)); + + return processor; + } + public static class ClientAuthResult { private final ClientModel client; diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java index a78f99e43e..26aba4b49d 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java @@ -10,6 +10,7 @@ import org.keycloak.services.util.LocaleHelper; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.UriInfo; +import java.net.URI; import java.util.Locale; /** @@ -29,6 +30,13 @@ public class DefaultKeycloakContext implements KeycloakContext { this.session = session; } + @Override + public URI getAuthServerUrl() { + UriInfo uri = getUri(); + KeycloakApplication keycloakApplication = getContextObject(KeycloakApplication.class); + return keycloakApplication.getBaseUri(uri); + } + @Override public String getContextPath() { KeycloakApplication app = getContextObject(KeycloakApplication.class); diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java new file mode 100644 index 0000000000..feffc5f495 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java @@ -0,0 +1,92 @@ +package org.keycloak.services.clientregistration; + +import org.jboss.resteasy.spi.UnauthorizedException; +import org.keycloak.authentication.AuthenticationProcessor; +import org.keycloak.events.Errors; +import org.keycloak.events.EventBuilder; +import org.keycloak.events.EventType; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil; +import org.keycloak.services.ForbiddenException; +import org.keycloak.services.managers.ClientManager; +import org.keycloak.services.managers.RealmManager; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * @author Stian Thorgersen + */ +public class AdapterInstallationClientRegistrationProvider implements ClientRegistrationProvider { + + private KeycloakSession session; + private EventBuilder event; + private ClientRegAuth auth; + + public AdapterInstallationClientRegistrationProvider(KeycloakSession session) { + this.session = session; + } + + @GET + @Path("{clientId}") + @Produces(MediaType.APPLICATION_JSON) + public Response get(@PathParam("clientId") String clientId) { + event.event(EventType.CLIENT_INFO); + + ClientModel client = session.getContext().getRealm().getClientByClientId(clientId); + + if (auth.isAuthenticated()) { + auth.requireView(client); + } else { + authenticateClient(client); + } + + ClientManager clientManager = new ClientManager(new RealmManager(session)); + Object rep = clientManager.toInstallationRepresentation(session.getContext().getRealm(), client, session.getContext().getAuthServerUrl()); + + event.client(client.getClientId()).success(); + return Response.ok(rep).build(); + } + + @Override + public void setAuth(ClientRegAuth auth) { + this.auth = auth; + } + + @Override + public void setEvent(EventBuilder event) { + this.event = event; + } + + @Override + public void close() { + } + + private void authenticateClient(ClientModel client) { + if (client.isPublicClient()) { + return; + } + + AuthenticationProcessor processor = AuthorizeClientUtil.getAuthenticationProcessor(session, event); + + Response response = processor.authenticateClient(); + if (response != null) { + event.client(client.getClientId()).error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + + ClientModel authClient = processor.getClient(); + if (client == null) { + event.client(client.getClientId()).error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + + if (!authClient.getClientId().equals(client.getClientId())) { + event.client(client.getClientId()).error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProviderFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProviderFactory.java new file mode 100644 index 0000000000..4b9700cd0c --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProviderFactory.java @@ -0,0 +1,34 @@ +package org.keycloak.services.clientregistration; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + +/** + * @author Stian Thorgersen + */ +public class AdapterInstallationClientRegistrationProviderFactory implements ClientRegistrationProviderFactory { + + @Override + public ClientRegistrationProvider create(KeycloakSession session) { + return new AdapterInstallationClientRegistrationProvider(session); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return "install"; + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java new file mode 100644 index 0000000000..496434993c --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java @@ -0,0 +1,128 @@ +package org.keycloak.services.clientregistration; + +import org.jboss.resteasy.spi.UnauthorizedException; +import org.keycloak.events.Errors; +import org.keycloak.events.EventBuilder; +import org.keycloak.models.*; +import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil; +import org.keycloak.representations.AccessToken; +import org.keycloak.services.ForbiddenException; +import org.keycloak.services.managers.AppAuthManager; +import org.keycloak.services.managers.AuthenticationManager; + +import javax.ws.rs.core.HttpHeaders; + +/** + * @author Stian Thorgersen + */ +public class ClientRegAuth { + + private KeycloakSession session; + private EventBuilder event; + + private String token; + private AccessToken.Access bearerRealmAccess; + + private boolean authenticated = false; + + public ClientRegAuth(KeycloakSession session, EventBuilder event) { + this.session = session; + this.event = event; + + init(); + } + + private void init() { + RealmModel realm = session.getContext().getRealm(); + + String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION); + if (authorizationHeader == null) { + return; + } + + String[] split = authorizationHeader.split(" "); + if (!split[0].equalsIgnoreCase("bearer")) { + return; + } + + if (split[1].indexOf('.') == -1) { + token = split[1]; + authenticated = true; + } else { + AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm); + bearerRealmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID); + authenticated = true; + } + } + + public boolean isAuthenticated() { + return authenticated; + } + + public void requireCreate() { + if (!authenticated) { + event.error(Errors.NOT_ALLOWED); + throw new UnauthorizedException(); + } + + if (bearerRealmAccess != null) { + if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) { + return; + } + } + + event.error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + + public void requireView(ClientModel client) { + if (!authenticated) { + event.error(Errors.NOT_ALLOWED); + throw new UnauthorizedException(); + } + + if (client == null) { + event.error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + + if (bearerRealmAccess != null) { + if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.VIEW_CLIENTS)) { + return; + } + } else if (token != null) { + if (client.getRegistrationSecret() != null && client.getRegistrationSecret().equals(token)) { + return; + } + } + + event.error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + + public void requireUpdate(ClientModel client) { + if (!authenticated) { + event.error(Errors.NOT_ALLOWED); + throw new UnauthorizedException(); + } + + if (client == null) { + event.error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + + if (bearerRealmAccess != null) { + if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.VIEW_CLIENTS)) { + return; + } + } else if (token != null) { + if (client.getRegistrationSecret() != null && client.getRegistrationSecret().equals(token)) { + return; + } + } + + event.error(Errors.NOT_ALLOWED); + throw new ForbiddenException(); + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java index d1d664868a..0c6bc4e1b7 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java @@ -9,7 +9,7 @@ import org.keycloak.provider.Provider; */ public interface ClientRegistrationProvider extends Provider { - void setRealm(RealmModel realm); + void setAuth(ClientRegAuth auth); void setEvent(EventBuilder event); diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java index 8b215face8..ea508def4f 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java @@ -2,36 +2,43 @@ package org.keycloak.services.clientregistration; import org.keycloak.events.EventBuilder; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.services.managers.AppAuthManager; +import org.keycloak.services.ErrorResponseException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; /** * @author Stian Thorgersen */ public class ClientRegistrationService { - private RealmModel realm; - private EventBuilder event; @Context private KeycloakSession session; - public ClientRegistrationService(RealmModel realm, EventBuilder event) { - this.realm = realm; + public ClientRegistrationService(EventBuilder event) { this.event = event; } @Path("{provider}") public Object getProvider(@PathParam("provider") String providerId) { + checkSsl(); + ClientRegistrationProvider provider = session.getProvider(ClientRegistrationProvider.class, providerId); - provider.setRealm(realm); provider.setEvent(event); + provider.setAuth(new ClientRegAuth(session, event)); return provider; } + private void checkSsl() { + if (!session.getContext().getUri().getBaseUri().getScheme().equals("https")) { + if (session.getContext().getRealm().getSslRequired().isRequired(session.getContext().getConnection())) { + throw new ErrorResponseException("invalid_request", "HTTPS required", Response.Status.FORBIDDEN); + } + } + } + } diff --git a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java index 04cb46a69c..603ed089da 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java @@ -1,22 +1,16 @@ package org.keycloak.services.clientregistration; -import org.jboss.logging.Logger; -import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; -import org.keycloak.models.*; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; -import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil; -import org.keycloak.representations.AccessToken; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ForbiddenException; -import org.keycloak.services.managers.AppAuthManager; -import org.keycloak.services.managers.AuthenticationManager; import javax.ws.rs.*; -import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; @@ -26,31 +20,29 @@ import java.net.URI; */ public class DefaultClientRegistrationProvider implements ClientRegistrationProvider { - private static final Logger logger = Logger.getLogger(DefaultClientRegistrationProvider.class); - private KeycloakSession session; private EventBuilder event; - private RealmModel realm; + private ClientRegAuth auth; public DefaultClientRegistrationProvider(KeycloakSession session) { this.session = session; } - @POST @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) public Response create(ClientRepresentation client) { event.event(EventType.CLIENT_REGISTER); - authenticate(true, null); + auth.requireCreate(); try { - ClientModel clientModel = RepresentationToModel.createClient(session, realm, client, true); + ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true); + clientModel.setRegistrationSecret(TokenGenerator.createRegistrationAccessToken()); + client = ModelToRepresentation.toRepresentation(clientModel); URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build(); - logger.infov("Created client {0}", client.getClientId()); - event.client(client.getClientId()).success(); return Response.created(uri).entity(client).build(); } catch (ModelDuplicateException e) { @@ -64,10 +56,10 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv public Response get(@PathParam("clientId") String clientId) { event.event(EventType.CLIENT_INFO); - ClientModel client = authenticate(false, clientId); - if (client == null) { - return Response.status(Response.Status.NOT_FOUND).build(); - } + ClientModel client = session.getContext().getRealm().getClientByClientId(clientId); + auth.requireView(client); + + event.client(client.getClientId()).success(); return Response.ok(ModelToRepresentation.toRepresentation(client)).build(); } @@ -77,12 +69,12 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv public Response update(@PathParam("clientId") String clientId, ClientRepresentation rep) { event.event(EventType.CLIENT_UPDATE).client(clientId); - ClientModel client = authenticate(false, clientId); + ClientModel client = session.getContext().getRealm().getClientByClientId(clientId); + auth.requireUpdate(client); + RepresentationToModel.updateClient(rep, client); - logger.infov("Updated client {0}", rep.getClientId()); - - event.success(); + event.client(client.getClientId()).success(); return Response.status(Response.Status.OK).build(); } @@ -91,9 +83,11 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv public Response delete(@PathParam("clientId") String clientId) { event.event(EventType.CLIENT_DELETE).client(clientId); - ClientModel client = authenticate(false, clientId); - if (realm.removeClient(client.getId())) { - event.success(); + ClientModel client = session.getContext().getRealm().getClientByClientId(clientId); + auth.requireUpdate(client); + + if (session.getContext().getRealm().removeClient(client.getId())) { + event.client(client.getClientId()).success(); return Response.ok().build(); } else { return Response.status(Response.Status.NOT_FOUND).build(); @@ -101,50 +95,8 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv } @Override - public void close() { - - } - - - private ClientModel authenticate(boolean create, String clientId) { - String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION); - - boolean bearer = authorizationHeader != null && authorizationHeader.split(" ")[0].equalsIgnoreCase("Bearer"); - - if (bearer) { - AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm); - AccessToken.Access realmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID); - if (realmAccess != null) { - if (realmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS)) { - return create ? null : realm.getClientByClientId(clientId); - } - - if (create && realmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) { - return create ? null : realm.getClientByClientId(clientId); - } - } - } else if (!create) { - ClientModel client; - - try { - AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm); - client = clientAuth.getClient(); - - if (client != null && !client.isPublicClient() && client.getClientId().equals(clientId)) { - return client; - } - } catch (Throwable t) { - } - } - - event.error(Errors.NOT_ALLOWED); - - throw new ForbiddenException(); - } - - @Override - public void setRealm(RealmModel realm) { -this.realm = realm; + public void setAuth(ClientRegAuth auth) { + this.auth = auth; } @Override @@ -152,4 +104,8 @@ this.realm = realm; this.event = event; } + @Override + public void close() { + } + } diff --git a/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java deleted file mode 100644 index baf9df1a7e..0000000000 --- a/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.keycloak.services.clientregistration; - -import org.keycloak.events.EventBuilder; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; - -/** - * @author Stian Thorgersen - */ -public class OIDCClientRegistrationProvider implements ClientRegistrationProvider { - - private KeycloakSession session; - private RealmModel realm; - private EventBuilder event; - - public OIDCClientRegistrationProvider(KeycloakSession session) { - this.session = session; - } - - @Override - public void close() { - } - - @Override - public void setRealm(RealmModel realm) { - this.realm = realm; - } - - @Override - public void setEvent(EventBuilder event) { - this.event = event; - } - -} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java b/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java new file mode 100644 index 0000000000..3c49d0f0ea --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java @@ -0,0 +1,27 @@ +package org.keycloak.services.clientregistration; + +import org.keycloak.common.util.Base64Url; + +import java.security.SecureRandom; + +/** + * @author Stian Thorgersen + */ +public class TokenGenerator { + + private static final int REGISTRATION_ACCESS_TOKEN_BYTES = 32; + + private TokenGenerator() { + } + + public String createInitialAccessToken() { + return null; + } + + public static String createRegistrationAccessToken() { + byte[] buf = new byte[REGISTRATION_ACCESS_TOKEN_BYTES]; + new SecureRandom().nextBytes(buf); + return Base64Url.encode(buf); + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java new file mode 100644 index 0000000000..1e0784c288 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java @@ -0,0 +1,38 @@ +package org.keycloak.services.clientregistration.oidc; + +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation; +import org.keycloak.representations.idm.ClientRepresentation; + +/** + * @author Stian Thorgersen + */ +public class DescriptionConverter { + + public static ClientRepresentation toInternal(OIDCClientRepresentation clientOIDC) { + ClientRepresentation client = new ClientRepresentation(); + client.setClientId(KeycloakModelUtils.generateId()); + client.setName(clientOIDC.getClientName()); + client.setRedirectUris(clientOIDC.getRedirectUris()); + client.setBaseUrl(clientOIDC.getClientUri()); + return client; + } + + public static OIDCClientResponseRepresentation toExternalResponse(ClientRepresentation client) { + OIDCClientResponseRepresentation response = new OIDCClientResponseRepresentation(); + response.setClientId(client.getClientId()); + + response.setClientName(client.getName()); + response.setClientUri(client.getBaseUrl()); + + response.setClientSecret(client.getSecret()); + response.setClientSecretExpiresAt(0); + + response.setRedirectUris(client.getRedirectUris()); + + response.setRegistrationAccessToken(client.getRegistrationAccessToken()); + + return response; + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java new file mode 100644 index 0000000000..4d131cc913 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java @@ -0,0 +1,100 @@ +package org.keycloak.services.clientregistration.oidc; + +import org.jboss.logging.Logger; +import org.keycloak.common.util.Time; +import org.keycloak.events.EventBuilder; +import org.keycloak.events.EventType; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.models.utils.RepresentationToModel; +import org.keycloak.protocol.oidc.OIDCClientDescriptionConverter; +import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.services.ErrorResponse; +import org.keycloak.services.clientregistration.ClientRegAuth; +import org.keycloak.services.clientregistration.ClientRegistrationProvider; +import org.keycloak.services.clientregistration.TokenGenerator; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.net.URI; + +/** + * @author Stian Thorgersen + */ +public class OIDCClientRegistrationProvider implements ClientRegistrationProvider { + + private static final Logger logger = Logger.getLogger(OIDCClientRegistrationProvider.class); + + private KeycloakSession session; + private EventBuilder event; + private ClientRegAuth auth; + + public OIDCClientRegistrationProvider(KeycloakSession session) { + this.session = session; + } + +// @POST +// @Consumes(MediaType.APPLICATION_JSON) +// @Produces(MediaType.APPLICATION_JSON) +// public Response create(OIDCClientRepresentation clientOIDC) { +// event.event(EventType.CLIENT_REGISTER); +// +// auth.requireCreate(); +// +// ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC); +// +// try { +// ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true); +// +// client = ModelToRepresentation.toRepresentation(clientModel); +// +// String registrationAccessToken = TokenGenerator.createRegistrationAccessToken(); +// +// clientModel.setRegistrationSecret(registrationAccessToken); +// +// URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build(); +// +// logger.infov("Created client {0}", client.getClientId()); +// +// event.client(client.getClientId()).success(); +// +// OIDCClientResponseRepresentation response = DescriptionConverter.toExternalResponse(client); +// +// response.setClientName(client.getName()); +// response.setClientUri(client.getBaseUrl()); +// +// response.setClientSecret(client.getSecret()); +// response.setClientSecretExpiresAt(0); +// +// response.setRedirectUris(client.getRedirectUris()); +// +// response.setRegistrationAccessToken(registrationAccessToken); +// response.setRegistrationClientUri(uri.toString()); +// +// return Response.created(uri).entity(response).build(); +// } catch (ModelDuplicateException e) { +// return ErrorResponse.exists("Client " + client.getClientId() + " already exists"); +// } +// } + + @Override + public void close() { + } + + @Override + public void setAuth(ClientRegAuth auth) { + this.auth = auth; + } + + @Override + public void setEvent(EventBuilder event) { + this.event = event; + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProviderFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java similarity index 77% rename from services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProviderFactory.java rename to services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java index a3ba0005d4..6f112f8816 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProviderFactory.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java @@ -1,8 +1,10 @@ -package org.keycloak.services.clientregistration; +package org.keycloak.services.clientregistration.oidc; import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.clientregistration.ClientRegistrationProvider; +import org.keycloak.services.clientregistration.ClientRegistrationProviderFactory; /** * @author Stian Thorgersen diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java new file mode 100644 index 0000000000..cd4eea4949 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java @@ -0,0 +1,77 @@ +package org.keycloak.services.clientregistration.oidc; + +import org.codehaus.jackson.annotate.JsonProperty; +import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation; + +/** + * @author Stian Thorgersen + */ +public class OIDCClientResponseRepresentation extends OIDCClientRepresentation { + + @JsonProperty("client_id") + private String clientId; + + @JsonProperty("client_secret") + private String clientSecret; + + @JsonProperty("client_id_issued_at") + private int clientIdIssuedAt; + + @JsonProperty("client_secret_expires_at") + private int clientSecretExpiresAt; + + @JsonProperty("registration_client_uri") + private String registrationClientUri; + + @JsonProperty("registration_access_token") + private String registrationAccessToken; + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public int getClientIdIssuedAt() { + return clientIdIssuedAt; + } + + public void setClientIdIssuedAt(int clientIdIssuedAt) { + this.clientIdIssuedAt = clientIdIssuedAt; + } + + public int getClientSecretExpiresAt() { + return clientSecretExpiresAt; + } + + public void setClientSecretExpiresAt(int clientSecretExpiresAt) { + this.clientSecretExpiresAt = clientSecretExpiresAt; + } + + public String getRegistrationClientUri() { + return registrationClientUri; + } + + public void setRegistrationClientUri(String registrationClientUri) { + this.registrationClientUri = registrationClientUri; + } + + public String getRegistrationAccessToken() { + return registrationAccessToken; + } + + public void setRegistrationAccessToken(String registrationAccessToken) { + this.registrationAccessToken = registrationAccessToken; + } + +} diff --git a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java index ddb301df7c..a93371282c 100755 --- a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java +++ b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java @@ -153,7 +153,7 @@ public class ClientsManagementService { } protected ClientModel authorizeClient() { - ClientModel client = AuthorizeClientUtil.authorizeClient(session, event, realm).getClient(); + ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient(); if (client.isPublicClient()) { Map error = new HashMap(); diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index f18f000fd2..4b5e4f201b 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -113,11 +113,11 @@ public class RealmsResource { return service; } - @Path("{realm}/client-registration") + @Path("{realm}/clients") public ClientRegistrationService getClientsService(final @PathParam("realm") String name) { RealmModel realm = init(name); EventBuilder event = new EventBuilder(realm, session, clientConnection); - ClientRegistrationService service = new ClientRegistrationService(realm, event); + ClientRegistrationService service = new ClientRegistrationService(event); ResteasyProviderFactory.getInstance().injectProperties(service); return service; } diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory index 3e8773a31a..d9b8c4159d 100644 --- a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory @@ -1 +1,3 @@ -org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory \ No newline at end of file +org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory +org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory +org.keycloak.services.clientregistration.AdapterInstallationClientRegistrationProviderFactory \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java new file mode 100644 index 0000000000..8b8dfad45c --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java @@ -0,0 +1,96 @@ +package org.keycloak.testsuite.client; + +import org.junit.After; +import org.junit.Before; +import org.keycloak.client.registration.ClientRegistration; +import org.keycloak.client.registration.ClientRegistrationException; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.Constants; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.AbstractKeycloakTest; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Stian Thorgersen + */ +public abstract class AbstractClientRegistrationTest extends AbstractKeycloakTest { + + static final String REALM_NAME = "test"; + + ClientRegistration reg; + + @Before + public void before() throws Exception { + reg = new ClientRegistration(testContext.getAuthServerContextRoot() + "/auth", "test"); + } + + @After + public void after() throws Exception { + reg.close(); + } + + @Override + public void addTestRealms(List testRealms) { + RealmRepresentation rep = new RealmRepresentation(); + rep.setEnabled(true); + rep.setRealm(REALM_NAME); + rep.setUsers(new LinkedList()); + + LinkedList credentials = new LinkedList<>(); + CredentialRepresentation password = new CredentialRepresentation(); + password.setType(CredentialRepresentation.PASSWORD); + password.setValue("password"); + credentials.add(password); + + UserRepresentation user = new UserRepresentation(); + user.setEnabled(true); + user.setUsername("manage-clients"); + user.setCredentials(credentials); + user.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.MANAGE_CLIENTS))); + + rep.getUsers().add(user); + + UserRepresentation user2 = new UserRepresentation(); + user2.setEnabled(true); + user2.setUsername("create-clients"); + user2.setCredentials(credentials); + user2.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.CREATE_CLIENT))); + + rep.getUsers().add(user2); + + UserRepresentation user3 = new UserRepresentation(); + user3.setEnabled(true); + user3.setUsername("no-access"); + user3.setCredentials(credentials); + + rep.getUsers().add(user3); + + testRealms.add(rep); + } + + public ClientRepresentation createClient(ClientRepresentation client) { + Response response = adminClient.realm(REALM_NAME).clients().create(client); + String id = response.getLocation().toString(); + id = id.substring(id.lastIndexOf('/') + 1); + client.setId(id); + response.close(); + return client; + } + + public ClientRepresentation getClient(String clientId) { + try { + return adminClient.realm(REALM_NAME).clients().get(clientId).toRepresentation(); + } catch (NotFoundException e) { + return null; + } + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java new file mode 100644 index 0000000000..b8bdb412a3 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java @@ -0,0 +1,103 @@ +package org.keycloak.testsuite.client; + +import org.junit.Before; +import org.junit.Test; +import org.keycloak.client.registration.Auth; +import org.keycloak.client.registration.ClientRegistrationException; +import org.keycloak.client.registration.HttpErrorException; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.keycloak.representations.idm.ClientRepresentation; + +import javax.ws.rs.core.Response; + +import static org.junit.Assert.*; + +/** + * @author Stian Thorgersen + */ +public class AdapterInstallationConfigTest extends AbstractClientRegistrationTest { + + private ClientRepresentation client; + private ClientRepresentation client2; + private ClientRepresentation clientPublic; + + @Before + public void before() throws Exception { + super.before(); + + client = new ClientRepresentation(); + client.setEnabled(true); + client.setClientId("RegistrationAccessTokenTest"); + client.setSecret("RegistrationAccessTokenTestClientSecret"); + client.setPublicClient(false); + client.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken"); + client.setRootUrl("http://root"); + client = createClient(client); + + client2 = new ClientRepresentation(); + client2.setEnabled(true); + client2.setClientId("RegistrationAccessTokenTest2"); + client2.setSecret("RegistrationAccessTokenTestClientSecret"); + client2.setPublicClient(false); + client2.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken"); + client2.setRootUrl("http://root"); + client2 = createClient(client2); + + clientPublic = new ClientRepresentation(); + clientPublic.setEnabled(true); + clientPublic.setClientId("RegistrationAccessTokenTestPublic"); + clientPublic.setPublicClient(true); + clientPublic.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessTokenPublic"); + clientPublic.setRootUrl("http://root"); + clientPublic = createClient(clientPublic); + } + + @Test + public void getConfigWithRegistrationAccessToken() throws ClientRegistrationException { + reg.auth(Auth.token(client.getRegistrationAccessToken())); + + AdapterConfig config = reg.getAdapterConfig(client.getClientId()); + assertNotNull(config); + } + + @Test + public void getConfig() throws ClientRegistrationException { + reg.auth(Auth.client(client.getClientId(), client.getSecret())); + + AdapterConfig config = reg.getAdapterConfig(client.getClientId()); + assertNotNull(config); + } + + @Test + public void getConfigMissingSecret() throws ClientRegistrationException { + reg.auth(null); + + try { + reg.getAdapterConfig(client.getClientId()); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } + } + + @Test + public void getConfigWrongClient() throws ClientRegistrationException { + reg.auth(Auth.client(client.getClientId(), client.getSecret())); + + try { + reg.getAdapterConfig(client2.getClientId()); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } + } + + @Test + public void getConfigPublicClient() throws ClientRegistrationException { + reg.auth(null); + + AdapterConfig config = reg.getAdapterConfig(clientPublic.getClientId()); + assertNotNull(config); + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java index 1d4b01129b..8b84b10af9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java @@ -1,302 +1,211 @@ package org.keycloak.testsuite.client; -import org.junit.After; -import org.junit.Before; import org.junit.Test; -import org.keycloak.client.registration.ClientRegistration; +import org.keycloak.client.registration.Auth; import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.HttpErrorException; -import org.keycloak.models.AdminRoles; -import org.keycloak.models.Constants; -import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.testsuite.AbstractKeycloakTest; +import javax.ws.rs.NotFoundException; import java.util.Collections; -import java.util.LinkedList; -import java.util.List; import static org.junit.Assert.*; /** * @author Stian Thorgersen */ -public class ClientRegistrationTest extends AbstractKeycloakTest { +public class ClientRegistrationTest extends AbstractClientRegistrationTest { - private static final String REALM_NAME = "test"; private static final String CLIENT_ID = "test-client"; private static final String CLIENT_SECRET = "test-client-secret"; - private ClientRegistration clientRegistrationAsAdmin; - private ClientRegistration clientRegistrationAsClient; - - @Before - public void before() throws ClientRegistrationException { - clientRegistrationAsAdmin = clientBuilder().auth(getToken("manage-clients", "password")).build(); - clientRegistrationAsClient = clientBuilder().auth(CLIENT_ID, CLIENT_SECRET).build(); - } - - @After - public void after() throws ClientRegistrationException { - clientRegistrationAsAdmin.close(); - clientRegistrationAsClient.close(); - } - - @Override - public void addTestRealms(List testRealms) { - RealmRepresentation rep = new RealmRepresentation(); - rep.setEnabled(true); - rep.setRealm(REALM_NAME); - rep.setUsers(new LinkedList()); - - LinkedList credentials = new LinkedList<>(); - CredentialRepresentation password = new CredentialRepresentation(); - password.setType(CredentialRepresentation.PASSWORD); - password.setValue("password"); - credentials.add(password); - - UserRepresentation user = new UserRepresentation(); - user.setEnabled(true); - user.setUsername("manage-clients"); - user.setCredentials(credentials); - user.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.MANAGE_CLIENTS))); - - rep.getUsers().add(user); - - UserRepresentation user2 = new UserRepresentation(); - user2.setEnabled(true); - user2.setUsername("create-clients"); - user2.setCredentials(credentials); - user2.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.CREATE_CLIENT))); - - rep.getUsers().add(user2); - - UserRepresentation user3 = new UserRepresentation(); - user3.setEnabled(true); - user3.setUsername("no-access"); - user3.setCredentials(credentials); - - rep.getUsers().add(user3); - - testRealms.add(rep); - } - - private void registerClient(ClientRegistration clientRegistration) throws ClientRegistrationException { + private ClientRepresentation registerClient() throws ClientRegistrationException { ClientRepresentation client = new ClientRepresentation(); client.setClientId(CLIENT_ID); client.setSecret(CLIENT_SECRET); - ClientRepresentation createdClient = clientRegistration.create(client); + ClientRepresentation createdClient = reg.create(client); assertEquals(CLIENT_ID, createdClient.getClientId()); client = adminClient.realm(REALM_NAME).clients().get(createdClient.getId()).toRepresentation(); assertEquals(CLIENT_ID, client.getClientId()); - AccessTokenResponse token2 = oauthClient.getToken(REALM_NAME, CLIENT_ID, CLIENT_SECRET, "manage-clients", "password"); - assertNotNull(token2.getToken()); + return client; } @Test public void registerClientAsAdmin() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); + authManageClients(); + registerClient(); } @Test public void registerClientAsAdminWithCreateOnly() throws ClientRegistrationException { - ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build(); - try { - registerClient(clientRegistration); - } finally { - clientRegistration.close(); - } + authCreateClients(); + registerClient(); } @Test public void registerClientAsAdminWithNoAccess() throws ClientRegistrationException { - ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build(); + authNoAccess(); try { - registerClient(clientRegistration); + registerClient(); fail("Expected 403"); } catch (ClientRegistrationException e) { assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } finally { - clientRegistration.close(); } } + @Test + public void getClientAsAdmin() throws ClientRegistrationException { + registerClientAsAdmin(); + ClientRepresentation rep = reg.get(CLIENT_ID); + assertNotNull(rep); + } + @Test public void getClientAsAdminWithCreateOnly() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); - ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build(); + registerClientAsAdmin(); + authCreateClients(); try { - clientRegistration.get(CLIENT_ID); + reg.get(CLIENT_ID); fail("Expected 403"); } catch (ClientRegistrationException e) { assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } finally { - clientRegistration.close(); - } - } - - @Test - public void wrongClient() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); - - ClientRepresentation client = new ClientRepresentation(); - client.setClientId("test-client-2"); - client.setSecret("test-client-2-secret"); - - clientRegistrationAsAdmin.create(client); - - ClientRegistration clientRegistration = clientBuilder().auth("test-client-2", "test-client-2-secret").build(); - - client = clientRegistration.get("test-client-2"); - assertNotNull(client); - assertEquals("test-client-2", client.getClientId()); - - try { - try { - clientRegistration.get(CLIENT_ID); - fail("Expected 403"); - } catch (ClientRegistrationException e) { - assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } - - client = clientRegistrationAsAdmin.get(CLIENT_ID); - try { - clientRegistration.update(client); - fail("Expected 403"); - } catch (ClientRegistrationException e) { - assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } - - try { - clientRegistration.delete(CLIENT_ID); - fail("Expected 403"); - } catch (ClientRegistrationException e) { - assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } - } - finally { - clientRegistration.close(); } } @Test public void getClientAsAdminWithNoAccess() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); - ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build(); + registerClientAsAdmin(); + authNoAccess(); try { - clientRegistration.get(CLIENT_ID); + reg.get(CLIENT_ID); fail("Expected 403"); } catch (ClientRegistrationException e) { assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } finally { - clientRegistration.close(); } } - private void updateClient(ClientRegistration clientRegistration) throws ClientRegistrationException { - ClientRepresentation client = clientRegistration.get(CLIENT_ID); + @Test + public void getClientNotFound() throws ClientRegistrationException { + authManageClients(); + try { + reg.get(CLIENT_ID); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } + } + + private void updateClient() throws ClientRegistrationException { + ClientRepresentation client = reg.get(CLIENT_ID); client.setRedirectUris(Collections.singletonList("http://localhost:8080/app")); - clientRegistration.update(client); + reg.update(client); - ClientRepresentation updatedClient = clientRegistration.get(CLIENT_ID); + ClientRepresentation updatedClient = reg.get(CLIENT_ID); assertEquals(1, updatedClient.getRedirectUris().size()); assertEquals("http://localhost:8080/app", updatedClient.getRedirectUris().get(0)); } + @Test public void updateClientAsAdmin() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); - updateClient(clientRegistrationAsAdmin); + registerClientAsAdmin(); + + authManageClients(); + updateClient(); } @Test public void updateClientAsAdminWithCreateOnly() throws ClientRegistrationException { - ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build(); + authCreateClients(); try { - updateClient(clientRegistration); + updateClient(); fail("Expected 403"); } catch (ClientRegistrationException e) { assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } finally { - clientRegistration.close(); } } @Test public void updateClientAsAdminWithNoAccess() throws ClientRegistrationException { - ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build(); + authNoAccess(); try { - updateClient(clientRegistration); + updateClient(); fail("Expected 403"); } catch (ClientRegistrationException e) { assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } finally { - clientRegistration.close(); } } @Test - public void updateClientAsClient() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); - updateClient(clientRegistrationAsClient); + public void updateClientNotFound() throws ClientRegistrationException { + authManageClients(); + try { + updateClient(); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } } - private void deleteClient(ClientRegistration clientRegistration) throws ClientRegistrationException { - clientRegistration.delete(CLIENT_ID); - - // Can't authenticate as client after client is deleted - ClientRepresentation client = clientRegistrationAsAdmin.get(CLIENT_ID); - assertNull(client); + private void deleteClient(ClientRepresentation client) throws ClientRegistrationException { + reg.delete(CLIENT_ID); + try { + adminClient.realm("test").clients().get(client.getId()).toRepresentation(); + fail("Expected 403"); + } catch (NotFoundException e) { + } } @Test public void deleteClientAsAdmin() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); - deleteClient(clientRegistrationAsAdmin); + authCreateClients(); + ClientRepresentation client = registerClient(); + + authManageClients(); + deleteClient(client); } @Test public void deleteClientAsAdminWithCreateOnly() throws ClientRegistrationException { - ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build(); + authManageClients(); + ClientRepresentation client = registerClient(); try { - deleteClient(clientRegistration); + authCreateClients(); + deleteClient(client); fail("Expected 403"); } catch (ClientRegistrationException e) { assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } finally { - clientRegistration.close(); } } @Test public void deleteClientAsAdminWithNoAccess() throws ClientRegistrationException { - ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build(); + authManageClients(); + ClientRepresentation client = registerClient(); try { - deleteClient(clientRegistration); + authNoAccess(); + deleteClient(client); fail("Expected 403"); } catch (ClientRegistrationException e) { assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); - } finally { - clientRegistration.close(); } } - @Test - public void deleteClientAsClient() throws ClientRegistrationException { - registerClient(clientRegistrationAsAdmin); - deleteClient(clientRegistrationAsClient); + private void authCreateClients() { + reg.auth(Auth.token(getToken("create-clients", "password"))); } - private ClientRegistration.ClientRegistrationBuilder clientBuilder() { - return ClientRegistration.create().realm("test").authServerUrl(testContext.getAuthServerContextRoot() + "/auth"); + private void authManageClients() { + reg.auth(Auth.token(getToken("manage-clients", "password"))); + } + + private void authNoAccess() { + reg.auth(Auth.token(getToken("no-access", "password"))); } private String getToken(String username, String password) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java new file mode 100644 index 0000000000..d76da19bcb --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java @@ -0,0 +1,94 @@ +package org.keycloak.testsuite.client; + +import org.junit.Before; +import org.junit.Test; +import org.keycloak.client.registration.Auth; +import org.keycloak.client.registration.ClientRegistrationException; +import org.keycloak.client.registration.HttpErrorException; +import org.keycloak.representations.idm.ClientRepresentation; + +import javax.ws.rs.core.Response; + +import static org.junit.Assert.*; + +/** + * @author Stian Thorgersen + */ +public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest { + + private ClientRepresentation client; + + @Before + public void before() throws Exception { + super.before(); + + client = new ClientRepresentation(); + client.setEnabled(true); + client.setClientId("RegistrationAccessTokenTest"); + client.setSecret("RegistrationAccessTokenTestClientSecret"); + client.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken"); + client.setRootUrl("http://root"); + client = createClient(client); + + reg.auth(Auth.token(client.getRegistrationAccessToken())); + } + + @Test + public void getClientWithRegistrationToken() throws ClientRegistrationException { + ClientRepresentation rep = reg.get(client.getClientId()); + assertNotNull(rep); + } + + @Test + public void getClientWithBadRegistrationToken() throws ClientRegistrationException { + reg.auth(Auth.token("invalid")); + try { + reg.get(client.getClientId()); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } + } + + @Test + public void updateClientWithRegistrationToken() throws ClientRegistrationException { + client.setRootUrl("http://newroot"); + reg.update(client); + + assertEquals("http://newroot", getClient(client.getId()).getRootUrl()); + } + + @Test + public void updateClientWithBadRegistrationToken() throws ClientRegistrationException { + client.setRootUrl("http://newroot"); + + reg.auth(Auth.token("invalid")); + try { + reg.update(client); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } + + assertEquals("http://root", getClient(client.getId()).getRootUrl()); + } + + @Test + public void deleteClientWithRegistrationToken() throws ClientRegistrationException { + reg.delete(client); + assertNull(getClient(client.getId())); + } + + @Test + public void deleteClientWithBadRegistrationToken() throws ClientRegistrationException { + reg.auth(Auth.token("invalid")); + try { + reg.delete(client); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } + assertNotNull(getClient(client.getId())); + } + +} From 409356f734cddcbc8c57782fbe3e243a5990c712 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 16 Nov 2015 15:05:56 -0200 Subject: [PATCH 11/16] KEYCLOAK-2065: Update the client via Admin REST API causes 500 Internal Server Error --- .../org/keycloak/services/resources/admin/ClientResource.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index 5d76778196..17a16f50c3 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -50,6 +50,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import static java.lang.Boolean.TRUE; + /** * Base resource class for managing one particular client of a realm. @@ -103,7 +105,7 @@ public class ClientResource { auth.requireManage(); try { - if (rep.isServiceAccountsEnabled() && !client.isServiceAccountsEnabled()) { + if (TRUE.equals(rep.isServiceAccountsEnabled()) && !client.isServiceAccountsEnabled()) { new ClientManager(new RealmManager(session)).enableServiceAccount(client);; } From 1bd9b1880382e27991da148d591bbdf52d9fc1b7 Mon Sep 17 00:00:00 2001 From: Markus Backes Date: Tue, 17 Nov 2015 09:32:14 +0100 Subject: [PATCH 12/16] treat principal name case insensitive * Kerberos login with active directory failed with invalid username or password because AD treats principal names in a case insensitive way (https://ssimo.org/blog/id_016.html) --- .../federation/kerberos/KerberosFederationProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java index 09a4da75b6..65831f250c 100755 --- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java +++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java @@ -116,7 +116,7 @@ public class KerberosFederationProvider implements UserFederationProvider { // KerberosUsernamePasswordAuthenticator.isUserAvailable is an overhead, so avoid it for now String kerberosPrincipal = local.getUsername() + "@" + kerberosConfig.getKerberosRealm(); - return kerberosPrincipal.equals(local.getFirstAttribute(KERBEROS_PRINCIPAL)); + return kerberosPrincipal.equalsIgnoreCase(local.getFirstAttribute(KERBEROS_PRINCIPAL)); } @Override From 62c5bc0e9153a79d080cb54ca5f60753b7e7d0cf Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 17 Nov 2015 09:36:42 +0100 Subject: [PATCH 13/16] KEYCLOAK-1749 Rotate registration access token, add registration access token to admin console --- .../registration/ClientRegistration.java | 5 ++-- .../client/registration/HttpUtil.java | 20 ++++++++++--- .../messages/admin-messages_en.properties | 5 +++- .../admin/resources/js/controllers/clients.js | 13 +++++++- .../theme/base/admin/resources/js/services.js | 11 +++++++ .../partials/client-credentials-generic.html | 2 +- .../partials/client-credentials-jwt.html | 2 +- .../partials/client-credentials-secret.html | 2 +- .../partials/client-credentials.html | 5 ++++ .../client-registration-access-token.html | 18 +++++++++++ .../keycloak/admin/resources/css/styles.css | 5 ++++ .../models/utils/KeycloakModelUtils.java | 24 ++++++++++----- .../clientregistration/ClientRegAuth.java | 6 ++++ .../ClientRegistrationService.java | 6 ++++ .../DefaultClientRegistrationProvider.java | 15 ++++++++-- .../clientregistration/TokenGenerator.java | 27 ----------------- .../oidc/OIDCClientRegistrationProvider.java | 1 - .../resources/admin/ClientResource.java | 18 +++++++++++ .../client/AdapterInstallationConfigTest.java | 22 ++++++++++++++ .../client/RegistrationAccessTokenTest.java | 30 ++++++++++++++++++- 20 files changed, 187 insertions(+), 50 deletions(-) create mode 100644 forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html delete mode 100644 services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java diff --git a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java index e59de7634c..f932226440 100644 --- a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java +++ b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java @@ -55,9 +55,10 @@ public class ClientRegistration { return resultStream != null ? deserialize(resultStream, AdapterConfig.class) : null; } - public void update(ClientRepresentation client) throws ClientRegistrationException { + public ClientRepresentation update(ClientRepresentation client) throws ClientRegistrationException { String content = serialize(client); - httpUtil.doPut(content, DEFAULT, client.getClientId()); + InputStream resultStream = httpUtil.doPut(content, DEFAULT, client.getClientId()); + return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null; } public void delete(ClientRepresentation client) throws ClientRegistrationException { diff --git a/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java index 699d378020..6444749782 100644 --- a/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java +++ b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java @@ -80,7 +80,9 @@ class HttpUtil { responseStream.close(); return null; } else { - responseStream.close(); + if (responseStream != null) { + responseStream.close(); + } throw new HttpErrorException(response.getStatusLine()); } } catch (IOException e) { @@ -88,7 +90,7 @@ class HttpUtil { } } - void doPut(String content, String... path) throws ClientRegistrationException { + InputStream doPut(String content, String... path) throws ClientRegistrationException { try { HttpPut request = new HttpPut(getUrl(baseUri, path)); @@ -100,10 +102,20 @@ class HttpUtil { HttpResponse response = httpClient.execute(request); if (response.getEntity() != null) { - response.getEntity().getContent().close(); + response.getEntity().getContent(); } - if (response.getStatusLine().getStatusCode() != 200) { + InputStream responseStream = null; + if (response.getEntity() != null) { + responseStream = response.getEntity().getContent(); + } + + if (response.getStatusLine().getStatusCode() == 200) { + return responseStream; + } else { + if (responseStream != null) { + responseStream.close(); + } throw new HttpErrorException(response.getStatusLine()); } } catch (IOException e) { diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index bcbc98dc2d..a9e74f7020 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -270,7 +270,10 @@ client-certificate-import=Client Certificate Import import-client-certificate=Import Client Certificate jwt-import.key-alias.tooltip=Archive alias for your certificate. secret=Secret -regenerate-secret=Regenerate Secret +regenerate-secret=Regenerate Secretsecret=Secret +registrationAccessToken=Registration access token +registrationAccessToken.regenerate=Regenerate registration access token +registrationAccessToken.tooltip=The registration access token provides access for clients to the client registration service. add-role=Add Role role-name=Role Name composite=Composite diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index c5f631609b..971b0c47a8 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -30,7 +30,7 @@ module.controller('ClientRoleListCtrl', function($scope, $location, realm, clien }); }); -module.controller('ClientCredentialsCtrl', function($scope, $location, realm, client, clientAuthenticatorProviders, clientConfigProperties, Client) { +module.controller('ClientCredentialsCtrl', function($scope, $location, realm, client, clientAuthenticatorProviders, clientConfigProperties, Client, ClientRegistrationAccessToken, Notifications) { $scope.realm = realm; $scope.client = angular.copy(client); $scope.clientAuthenticatorProviders = clientAuthenticatorProviders; @@ -68,6 +68,17 @@ module.controller('ClientCredentialsCtrl', function($scope, $location, realm, cl } }, true); + $scope.regenerateRegistrationAccessToken = function() { + var secret = ClientRegistrationAccessToken.update({ realm : $scope.realm.realm, client : $scope.client.id }, + function(data) { + Notifications.success('The registration access token has been updated.'); + $scope.client['registrationAccessToken'] = data.registrationAccessToken; + }, + function() { + Notifications.error('Failed to update the registration access token'); + } + ); + }; }); module.controller('ClientSecretCtrl', function($scope, $location, ClientSecret, Notifications) { diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js index 065f831877..34fdc9d744 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js @@ -981,6 +981,17 @@ module.factory('ClientSecret', function($resource) { }); }); +module.factory('ClientRegistrationAccessToken', function($resource) { + return $resource(authUrl + '/admin/realms/:realm/clients/:client/registration-access-token', { + realm : '@realm', + client : '@client' + }, { + update : { + method : 'POST' + } + }); +}); + module.factory('ClientOrigins', function($resource) { return $resource(authUrl + '/admin/realms/:realm/clients/:client/allowed-origins', { realm : '@realm', diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html index 631939c9bf..1d59078d1a 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html @@ -1,5 +1,5 @@
-
+
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html index aa032033dc..8c581d7bc2 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html @@ -1,5 +1,5 @@
- +
{{:: 'certificate.tooltip' | translate}} diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html index 2bd53dbedf..744ea80dac 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html @@ -1,5 +1,5 @@
- +
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html index 96f16cad72..b1b1062dfb 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html @@ -28,6 +28,11 @@
+
+ +
+
+
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html new file mode 100644 index 0000000000..55a3546dbd --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html @@ -0,0 +1,18 @@ +
+ +
+ +
+
+
+ +
+
+ +
+
+
+ {{:: 'registrationAccessToken.tooltip' | translate}} +
+ +
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css b/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css index c0e8fb277a..500ed89990 100644 --- a/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css +++ b/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css @@ -22,6 +22,11 @@ table { margin-top: 20px; } +.no-margin-top { + margin-top: 0px !important; +} + + /*********** Loading ***********/ diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index ad5997a9a0..c35c58ecc8 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -1,6 +1,7 @@ package org.keycloak.models.utils; import org.bouncycastle.openssl.PEMWriter; +import org.keycloak.common.util.Base64Url; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientModel; @@ -26,12 +27,7 @@ import org.keycloak.common.util.PemUtils; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.io.StringWriter; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; +import java.security.*; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.LinkedList; @@ -47,6 +43,8 @@ import java.util.UUID; */ public final class KeycloakModelUtils { + private static final int RANDOM_PASSWORD_BYTES = 32; + private KeycloakModelUtils() { } @@ -178,12 +176,22 @@ public final class KeycloakModelUtils { return rep; } - public static UserCredentialModel generateSecret(ClientModel app) { + public static UserCredentialModel generateSecret(ClientModel client) { UserCredentialModel secret = UserCredentialModel.generateSecret(); - app.setSecret(secret.getValue()); + client.setSecret(secret.getValue()); return secret; } + public static void generateRegistrationAccessToken(ClientModel client) { + client.setRegistrationSecret(generatePassword()); + } + + public static String generatePassword() { + byte[] buf = new byte[RANDOM_PASSWORD_BYTES]; + new SecureRandom().nextBytes(buf); + return Base64Url.encode(buf); + } + public static String getDefaultClientAuthenticatorType() { return "client-secret"; } diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java index 496434993c..cf13235cfc 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java @@ -24,6 +24,7 @@ public class ClientRegAuth { private AccessToken.Access bearerRealmAccess; private boolean authenticated = false; + private boolean registrationAccessToken = false; public ClientRegAuth(KeycloakSession session, EventBuilder event) { this.session = session; @@ -48,6 +49,7 @@ public class ClientRegAuth { if (split[1].indexOf('.') == -1) { token = split[1]; authenticated = true; + registrationAccessToken = true; } else { AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm); bearerRealmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID); @@ -59,6 +61,10 @@ public class ClientRegAuth { return authenticated; } + public boolean isRegistrationAccessToken() { + return registrationAccessToken; + } + public void requireCreate() { if (!authenticated) { event.error(Errors.NOT_ALLOWED); diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java index ea508def4f..2aed3f1938 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java @@ -1,5 +1,6 @@ package org.keycloak.services.clientregistration; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.events.EventBuilder; import org.keycloak.models.KeycloakSession; import org.keycloak.services.ErrorResponseException; @@ -28,6 +29,11 @@ public class ClientRegistrationService { checkSsl(); ClientRegistrationProvider provider = session.getProvider(ClientRegistrationProvider.class, providerId); + + if (provider == null) { + throw new NotFoundException("Client registration provider not found"); + } + provider.setEvent(event); provider.setAuth(new ClientRegAuth(session, event)); return provider; diff --git a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java index 603ed089da..0fad9c2f07 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java @@ -5,6 +5,7 @@ import org.keycloak.events.EventType; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ClientRepresentation; @@ -38,7 +39,7 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv try { ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true); - clientModel.setRegistrationSecret(TokenGenerator.createRegistrationAccessToken()); + KeycloakModelUtils.generateRegistrationAccessToken(clientModel); client = ModelToRepresentation.toRepresentation(clientModel); URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build(); @@ -59,6 +60,10 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv ClientModel client = session.getContext().getRealm().getClientByClientId(clientId); auth.requireView(client); + if (auth.isRegistrationAccessToken()) { + KeycloakModelUtils.generateRegistrationAccessToken(client); + } + event.client(client.getClientId()).success(); return Response.ok(ModelToRepresentation.toRepresentation(client)).build(); } @@ -74,8 +79,14 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv RepresentationToModel.updateClient(rep, client); + if (auth.isRegistrationAccessToken()) { + KeycloakModelUtils.generateRegistrationAccessToken(client); + } + + rep = ModelToRepresentation.toRepresentation(client); + event.client(client.getClientId()).success(); - return Response.status(Response.Status.OK).build(); + return Response.ok(rep).build(); } @DELETE diff --git a/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java b/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java deleted file mode 100644 index 3c49d0f0ea..0000000000 --- a/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.keycloak.services.clientregistration; - -import org.keycloak.common.util.Base64Url; - -import java.security.SecureRandom; - -/** - * @author Stian Thorgersen - */ -public class TokenGenerator { - - private static final int REGISTRATION_ACCESS_TOKEN_BYTES = 32; - - private TokenGenerator() { - } - - public String createInitialAccessToken() { - return null; - } - - public static String createRegistrationAccessToken() { - byte[] buf = new byte[REGISTRATION_ACCESS_TOKEN_BYTES]; - new SecureRandom().nextBytes(buf); - return Base64Url.encode(buf); - } - -} diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java index 4d131cc913..b27ddb0383 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java @@ -15,7 +15,6 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.clientregistration.ClientRegAuth; import org.keycloak.services.clientregistration.ClientRegistrationProvider; -import org.keycloak.services.clientregistration.TokenGenerator; import javax.ws.rs.Consumes; import javax.ws.rs.POST; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index 5d76778196..da3eeb0cd4 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -214,6 +214,24 @@ public class ClientResource { return rep; } + /** + * Generate a new registration access token for the client + * + * @return + */ + @Path("registration-access-token") + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public ClientRepresentation regenerateRegistrationAccessToken() { + auth.requireManage(); + + KeycloakModelUtils.generateRegistrationAccessToken(client); + ClientRepresentation rep = ModelToRepresentation.toRepresentation(client); + adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).representation(rep).success(); + return rep; + } + /** * Get the client secret * diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java index b8bdb412a3..bf98364dbd 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java @@ -5,6 +5,7 @@ import org.junit.Test; import org.keycloak.client.registration.Auth; import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.HttpErrorException; +import org.keycloak.common.enums.SslRequired; import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.idm.ClientRepresentation; @@ -20,11 +21,14 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes private ClientRepresentation client; private ClientRepresentation client2; private ClientRepresentation clientPublic; + private String publicKey; @Before public void before() throws Exception { super.before(); + publicKey = adminClient.realm(REALM_NAME).toRepresentation().getPublicKey(); + client = new ClientRepresentation(); client.setEnabled(true); client.setClientId("RegistrationAccessTokenTest"); @@ -66,6 +70,16 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes AdapterConfig config = reg.getAdapterConfig(client.getClientId()); assertNotNull(config); + + assertEquals(testContext.getAuthServerContextRoot() + "/auth", config.getAuthServerUrl()); + assertEquals("test", config.getRealm()); + + assertEquals(1, config.getCredentials().size()); + assertEquals(client.getSecret(), config.getCredentials().get("secret")); + + assertEquals(publicKey, config.getRealmKey()); + assertEquals(client.getClientId(), config.getResource()); + assertEquals(SslRequired.EXTERNAL.name().toLowerCase(), config.getSslRequired()); } @Test @@ -98,6 +112,14 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes AdapterConfig config = reg.getAdapterConfig(clientPublic.getClientId()); assertNotNull(config); + + assertEquals("test", config.getRealm()); + + assertEquals(0, config.getCredentials().size()); + + assertEquals(publicKey, config.getRealmKey()); + assertEquals(clientPublic.getClientId(), config.getResource()); + assertEquals(SslRequired.EXTERNAL.name().toLowerCase(), config.getSslRequired()); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java index d76da19bcb..be880bf293 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java @@ -33,10 +33,33 @@ public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest reg.auth(Auth.token(client.getRegistrationAccessToken())); } + private ClientRepresentation assertRead(String id, String registrationAccess, boolean expectSuccess) throws ClientRegistrationException { + if (expectSuccess) { + reg.auth(Auth.token(registrationAccess)); + ClientRepresentation rep = reg.get(client.getClientId()); + assertNotNull(rep); + return rep; + } else { + reg.auth(Auth.token(registrationAccess)); + try { + reg.get(client.getClientId()); + fail("Expected 403"); + } catch (ClientRegistrationException e) { + assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode()); + } + } + return null; + } + @Test public void getClientWithRegistrationToken() throws ClientRegistrationException { ClientRepresentation rep = reg.get(client.getClientId()); assertNotNull(rep); + assertNotEquals(client.getRegistrationAccessToken(), rep.getRegistrationAccessToken()); + + // check registration access token is updated + assertRead(client.getClientId(), client.getRegistrationAccessToken(), false); + assertRead(client.getClientId(), rep.getRegistrationAccessToken(), true); } @Test @@ -53,9 +76,14 @@ public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest @Test public void updateClientWithRegistrationToken() throws ClientRegistrationException { client.setRootUrl("http://newroot"); - reg.update(client); + ClientRepresentation rep = reg.update(client); assertEquals("http://newroot", getClient(client.getId()).getRootUrl()); + assertNotEquals(client.getRegistrationAccessToken(), rep.getRegistrationAccessToken()); + + // check registration access token is updated + assertRead(client.getClientId(), client.getRegistrationAccessToken(), false); + assertRead(client.getClientId(), rep.getRegistrationAccessToken(), true); } @Test From 22c5c803cae74b6b7090581bc8cd118cbec4fc10 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 17 Nov 2015 09:17:19 -0200 Subject: [PATCH 14/16] KEYCLOAK-2100: 500 Internal Server Error with Update the events provider via Admin REST API --- .../org/keycloak/services/managers/RealmManager.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index 688c242f9a..d44a622828 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -50,6 +50,7 @@ import org.keycloak.timer.TimerProvider; import java.util.Collections; import java.util.HashSet; import java.util.List; +import static java.lang.Boolean.TRUE; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.protocol.ProtocolMapperUtils; @@ -221,9 +222,12 @@ public class RealmManager implements RealmImporter { if(rep.getEnabledEventTypes() != null) { realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes())); } - - realm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); - realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); + if(rep.isAdminEventsEnabled() != null) { + realm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); + } + if(rep.isAdminEventsDetailsEnabled() != null){ + realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); + } } private void setupMasterAdminManagement(RealmModel realm) { From c81d0c0898d7b44c0d053ce6666acdad4967bffc Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Tue, 17 Nov 2015 22:26:32 +0100 Subject: [PATCH 15/16] KEYCLOAK-2101 - Ensure consistent order for properties in json configuration export. We now explicitly order the AuthenticatorFlows, AuthenticatorConfig, as well as RequiredActions to ensure a consistent order for the configuration export to make it easier to diff / version keycloak configuration files. Previously the order of the json properties of an configuration export were non deterministic. The configuration file could look partially different after a export, import, export sequence even if no changes were made. --- .../models/utils/ModelToRepresentation.java | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 0df3516980..a69bb2b971 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -44,13 +44,7 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.common.util.Time; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author Bill Burke @@ -292,19 +286,50 @@ public class ModelToRepresentation { public static void exportAuthenticationFlows(RealmModel realm, RealmRepresentation rep) { rep.setAuthenticationFlows(new LinkedList()); rep.setAuthenticatorConfig(new LinkedList()); - for (AuthenticationFlowModel model : realm.getAuthenticationFlows()) { + + List authenticationFlows = new ArrayList<>(realm.getAuthenticationFlows()); + //ensure consistent ordering of authenticationFlows. + Collections.sort(authenticationFlows, new Comparator() { + @Override + public int compare(AuthenticationFlowModel left, AuthenticationFlowModel right) { + return left.getAlias().compareTo(right.getAlias()); + } + }); + + for (AuthenticationFlowModel model : authenticationFlows) { AuthenticationFlowRepresentation flowRep = toRepresentation(realm, model); rep.getAuthenticationFlows().add(flowRep); } - for (AuthenticatorConfigModel model : realm.getAuthenticatorConfigs()) { + + List authenticatorConfigs = new ArrayList<>(realm.getAuthenticatorConfigs()); + //ensure consistent ordering of authenticatorConfigs. + Collections.sort(authenticatorConfigs, new Comparator() { + @Override + public int compare(AuthenticatorConfigModel left, AuthenticatorConfigModel right) { + return left.getAlias().compareTo(right.getAlias()); + } + }); + + for (AuthenticatorConfigModel model : authenticatorConfigs) { rep.getAuthenticatorConfig().add(toRepresentation(model)); } } public static void exportRequiredActions(RealmModel realm, RealmRepresentation rep) { + rep.setRequiredActions(new LinkedList()); - for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) { + + List requiredActionProviders = realm.getRequiredActionProviders(); + //ensure consistent ordering of requiredActionProviders. + Collections.sort(requiredActionProviders, new Comparator() { + @Override + public int compare(RequiredActionProviderModel left, RequiredActionProviderModel right) { + return left.getAlias().compareTo(right.getAlias()); + } + }); + + for (RequiredActionProviderModel model : requiredActionProviders) { RequiredActionProviderRepresentation action = toRepresentation(model); rep.getRequiredActions().add(action); } From bc22abf8cb1277ca22ac171692e312ae57a1e432 Mon Sep 17 00:00:00 2001 From: mposolda Date: Wed, 18 Nov 2015 12:34:22 +0100 Subject: [PATCH 16/16] KEYCLOAK-2102 added AbstractKerberosTest.caseInsensitiveTest --- .../federation/AbstractKerberosTest.java | 29 ++++++++++++++++++- .../resources/kerberos/users-kerberos.ldif | 14 +++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java index ac858ddb16..4e52db00c7 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java @@ -127,7 +127,34 @@ public abstract class AbstractKerberosTest { spnegoResponse.close(); events.clear(); - } + } + + // KEYCLOAK-2102 + @Test + public void spnegoCaseInsensitiveTest() throws Exception { + KeycloakRule keycloakRule = getKeycloakRule(); + AssertEvents events = getAssertEvents(); + + Response spnegoResponse = spnegoLogin("MyDuke", "theduke"); + Assert.assertEquals(302, spnegoResponse.getStatus()); + + events.expectLogin() + .client("kerberos-app") + .user(keycloakRule.getUser("test", "myduke").getId()) + .detail(Details.REDIRECT_URI, KERBEROS_APP_URL) + //.detail(Details.AUTH_METHOD, "spnego") + .detail(Details.USERNAME, "myduke") + .assertEvent(); + + String location = spnegoResponse.getLocation().toString(); + driver.navigate().to(location); + + String pageSource = driver.getPageSource(); + Assert.assertTrue(pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content")); + + spnegoResponse.close(); + events.clear(); + } @Test diff --git a/testsuite/integration/src/test/resources/kerberos/users-kerberos.ldif b/testsuite/integration/src/test/resources/kerberos/users-kerberos.ldif index fd9936cfa9..9d55092804 100644 --- a/testsuite/integration/src/test/resources/kerberos/users-kerberos.ldif +++ b/testsuite/integration/src/test/resources/kerberos/users-kerberos.ldif @@ -76,6 +76,20 @@ userPassword: theduke krb5PrincipalName: jduke@KEYCLOAK.ORG krb5KeyVersionNumber: 0 +dn: uid=MyDuke,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: My +sn: Duke +mail: MyDuke@keycloak.org +uid: MyDuke +userPassword: theduke +krb5PrincipalName: MyDuke@KEYCLOAK.ORG +krb5KeyVersionNumber: 0 + dn: uid=gsstestserver,ou=People,dc=keycloak,dc=org objectClass: top objectClass: person