keycloak-memoizeit
Changes
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProvider.java 170(+170 -0)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProviderFactory.java 40(+40 -0)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory 3(+2 -1)
Details
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProvider.java
new file mode 100644
index 0000000..2e99385
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProvider.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.federation;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialInput;
+import org.keycloak.credential.CredentialInputUpdater;
+import org.keycloak.credential.CredentialInputValidator;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.storage.StorageId;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
+import org.keycloak.storage.user.UserLookupProvider;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Provides one user where everything is stored in user federated storage
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PassThroughFederatedUserStorageProvider implements
+ UserStorageProvider,
+ UserLookupProvider,
+ CredentialInputValidator,
+ CredentialInputUpdater
+{
+
+ public static final Set<String> CREDENTIAL_TYPES = Collections.singleton(UserCredentialModel.PASSWORD);
+ public static final String PASSTHROUGH_USERNAME = "passthrough";
+ public static final String INITIAL_PASSWORD = "secret";
+ private KeycloakSession session;
+ private ComponentModel component;
+
+ public PassThroughFederatedUserStorageProvider(KeycloakSession session, ComponentModel component) {
+ this.session = session;
+ this.component = component;
+ }
+
+ public Set<String> getSupportedCredentialTypes() {
+ return CREDENTIAL_TYPES;
+ }
+
+ @Override
+ public boolean supportsCredentialType(String credentialType) {
+ return getSupportedCredentialTypes().contains(credentialType);
+ }
+
+ @Override
+ public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
+ if (!CredentialModel.PASSWORD.equals(credentialType)) return false;
+ return true;
+ }
+
+ @Override
+ public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
+ UserCredentialModel password = (UserCredentialModel)input;
+ if (password.getType().equals(UserCredentialModel.PASSWORD)) {
+ if (INITIAL_PASSWORD.equals(password.getValue())) {
+ return true;
+ }
+ List<CredentialModel> existing = session.userFederatedStorage().getStoredCredentialsByType(realm, user.getId(), "CLEAR_TEXT_PASSWORD");
+ if (existing.isEmpty()) return false;
+ return existing.get(0).getConfig().getFirst("VALUE").equals(password.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
+ // testing federated credential attributes
+ UserCredentialModel password = (UserCredentialModel)input;
+ if (password.getType().equals(UserCredentialModel.PASSWORD)) {
+ List<CredentialModel> existing = session.userFederatedStorage().getStoredCredentialsByType(realm, user.getId(), "CLEAR_TEXT_PASSWORD");
+ if (existing.isEmpty()) {
+ CredentialModel model = new CredentialModel();
+ model.setType("CLEAR_TEXT_PASSWORD");
+ model.getConfig().putSingle("VALUE", password.getValue());
+ session.userFederatedStorage().createCredential(realm, user.getId(), model);
+ } else {
+ CredentialModel model = existing.get(0);
+ model.setType("CLEAR_TEXT_PASSWORD");
+ model.getConfig().putSingle("VALUE", password.getValue());
+ session.userFederatedStorage().updateCredential(realm, user.getId(), model);
+
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
+ List<CredentialModel> existing = session.userFederatedStorage().getStoredCredentialsByType(realm, user.getId(), "CLEAR_TEXT_PASSWORD");
+ for (CredentialModel model : existing) {
+ session.userFederatedStorage().removeStoredCredential(realm, user.getId(), model.getId());
+ }
+ }
+
+ @Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return CREDENTIAL_TYPES;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public UserModel getUserById(String id, RealmModel realm) {
+ if (!StorageId.externalId(id).equals(PASSTHROUGH_USERNAME)) return null;
+ return getUserModel(realm);
+ }
+
+ @Override
+ public UserModel getUserByUsername(String username, RealmModel realm) {
+ if (!PASSTHROUGH_USERNAME.equals(username)) return null;
+
+ return getUserModel(realm);
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email, RealmModel realm) {
+ List<String> list = session.userFederatedStorage().getUsersByUserAttribute(realm, AbstractUserAdapterFederatedStorage.EMAIL_ATTRIBUTE, email);
+ for (String user : list) {
+ StorageId storageId = new StorageId(user);
+ if (!storageId.getExternalId().equals(PASSTHROUGH_USERNAME)) continue;
+ if (!storageId.getProviderId().equals(component.getId())) continue;
+ return getUserModel(realm);
+
+ }
+ return null;
+ }
+
+ private UserModel getUserModel(final RealmModel realm) {
+ return new AbstractUserAdapterFederatedStorage(session, realm, component) {
+ @Override
+ public String getUsername() {
+ return PASSTHROUGH_USERNAME;
+ }
+
+ @Override
+ public void setUsername(String username) {
+
+ }
+ };
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProviderFactory.java
new file mode 100644
index 0000000..1b3cb55
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/PassThroughFederatedUserStorageProviderFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.federation;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.storage.UserStorageProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PassThroughFederatedUserStorageProviderFactory implements UserStorageProviderFactory<PassThroughFederatedUserStorageProvider> {
+
+ public static final String PROVIDER_ID = "pass-through-federated";
+
+ @Override
+ public PassThroughFederatedUserStorageProvider create(KeycloakSession session, ComponentModel model) {
+ return new PassThroughFederatedUserStorageProvider(session, model);
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
index a97dd1e..a9ae823 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
@@ -1 +1,2 @@
-org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
\ No newline at end of file
+org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
+org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java
new file mode 100644
index 0000000..7e796cd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.broker;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.representations.idm.ComponentRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProvider;
+import org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory;
+import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
+
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
+import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AccountLinkTest extends AbstractKeycloakTest {
+ public static final String CHILD_IDP = "child";
+ public static final String PARENT_IDP = "parent-idp";
+ public static final String PARENT_USERNAME = "parent";
+
+ @Page
+ protected AccountFederatedIdentityPage accountFederatedIdentityPage;
+
+ @Page
+ protected UpdateAccountInformationPage profilePage;
+
+ @Page
+ protected LoginPage loginPage;
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation realm = new RealmRepresentation();
+ realm.setRealm(CHILD_IDP);
+ realm.setEnabled(true);
+ testRealms.add(realm);
+
+ realm = new RealmRepresentation();
+ realm.setRealm(PARENT_IDP);
+ realm.setEnabled(true);
+
+ testRealms.add(realm);
+
+ }
+
+ @Before
+ public void addIdpUser() {
+ RealmResource realm = adminClient.realms().realm(PARENT_IDP);
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername(PARENT_USERNAME);
+ user.setEnabled(true);
+ String userId = createUserAndResetPasswordWithAdminClient(realm, user, "password");
+
+ }
+
+ @Before
+ public void addChildUser() {
+ RealmResource realm = adminClient.realms().realm(CHILD_IDP);
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername("child");
+ user.setEnabled(true);
+ String userId = createUserAndResetPasswordWithAdminClient(realm, user, "password");
+
+ }
+
+ @Before
+ public void setupUserStorageProvider() {
+ ComponentRepresentation provider = new ComponentRepresentation();
+ provider.setName("passthrough");
+ provider.setProviderId(PassThroughFederatedUserStorageProviderFactory.PROVIDER_ID);
+ provider.setProviderType(UserStorageProvider.class.getName());
+ provider.setConfig(new MultivaluedHashMap<>());
+ provider.getConfig().putSingle("priority", Integer.toString(1));
+
+ RealmResource realm = adminClient.realms().realm(CHILD_IDP);
+ realm.components().add(provider);
+
+
+
+
+ }
+
+ @Before
+ public void createBroker() {
+ createParentChild();
+ }
+
+ public void createParentChild() {
+ BrokerTestTools.createKcOidcBroker(adminClient, CHILD_IDP, PARENT_IDP, suiteContext);
+ }
+
+ @Test
+ public void testAccountLink() {
+ String childUsername = "child";
+ String childPassword = "password";
+ String childIdp = CHILD_IDP;
+
+ testAccountLink(childUsername, childPassword, childIdp);
+
+ }
+
+ @Test
+ public void testAccountLinkWithUserStorageProvider() {
+ String childUsername = PassThroughFederatedUserStorageProvider.PASSTHROUGH_USERNAME;
+ String childPassword = PassThroughFederatedUserStorageProvider.INITIAL_PASSWORD;
+ String childIdp = CHILD_IDP;
+
+ testAccountLink(childUsername, childPassword, childIdp);
+
+ }
+
+ protected void testAccountLink(String childUsername, String childPassword, String childIdp) {
+ accountFederatedIdentityPage.realm(childIdp);
+ accountFederatedIdentityPage.open();
+ loginPage.isCurrent();
+ loginPage.login(childUsername, childPassword);
+ assertTrue(accountFederatedIdentityPage.isCurrent());
+
+ accountFederatedIdentityPage.clickAddProvider(PARENT_IDP);
+
+ this.loginPage.isCurrent();
+ loginPage.login(PARENT_USERNAME, "password");
+
+ // Assert identity linked in account management
+ assertTrue(accountFederatedIdentityPage.isCurrent());
+ assertTrue(driver.getPageSource().contains("id=\"remove-" + PARENT_IDP + "\""));
+
+ // Logout from account management
+ accountFederatedIdentityPage.logout();
+
+ // Assert I am logged immediately to account management due to previously linked "test-user" identity
+ loginPage.isCurrent();
+ loginPage.clickSocial(PARENT_IDP);
+ loginPage.login(PARENT_USERNAME, "password");
+ System.out.println(driver.getCurrentUrl());
+ System.out.println("--------------------------------");
+ System.out.println(driver.getPageSource());
+ assertTrue(accountFederatedIdentityPage.isCurrent());
+ assertTrue(driver.getPageSource().contains("id=\"remove-" + PARENT_IDP + "\""));
+
+ // Unlink my "test-user"
+ accountFederatedIdentityPage.clickRemoveProvider(PARENT_IDP);
+ assertTrue(driver.getPageSource().contains("id=\"add-" + PARENT_IDP + "\""));
+
+
+ // Logout from account management
+ accountFederatedIdentityPage.logout();
+
+ this.loginPage.clickSocial(PARENT_IDP);
+ this.loginPage.login(PARENT_USERNAME, "password");
+ this.profilePage.assertCurrent();
+ }
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java
index f021a36..c5b7b31 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java
@@ -1,17 +1,29 @@
package org.keycloak.testsuite.broker;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.testsuite.arquillian.SuiteContext;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.CLIENT_ID;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.CLIENT_SECRET;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME;
+
/**
*
* @author hmlnarik
@@ -62,4 +74,43 @@ public class BrokerTestTools {
return result;
}
+
+ /**
+ * Expects a child idp and parent idp running on same Keycloak instance. Links the two with non-signature checks.
+ *
+ * @param adminClient
+ * @param childRealm
+ * @param idpRealm
+ * @param suiteContext
+ */
+ public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, SuiteContext suiteContext) {
+ IdentityProviderRepresentation idp = createIdentityProvider(idpRealm, IDP_OIDC_PROVIDER_ID);
+ Map<String, String> config = idp.getConfig();
+
+ config.put("clientId", childRealm);
+ config.put("clientSecret", childRealm);
+ config.put("prompt", "login");
+ config.put("authorizationUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/auth");
+ config.put("tokenUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/token");
+ config.put("logoutUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/logout");
+ config.put("userInfoUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/userinfo");
+ config.put("backchannelSupported", "true");
+ adminClient.realm(childRealm).identityProviders().create(idp);
+
+ ClientRepresentation client = new ClientRepresentation();
+ client.setClientId(childRealm);
+ client.setName(childRealm);
+ client.setSecret(childRealm);
+ client.setEnabled(true);
+
+ client.setRedirectUris(Collections.singletonList(getAuthRoot(suiteContext) +
+ "/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint/*"));
+
+ client.setAdminUrl(getAuthRoot(suiteContext) +
+ "/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint");
+ adminClient.realm(idpRealm).clients().create(client);
+
+
+
+ }
}