keycloak-memoizeit

account link tests

1/27/2017 8:37:08 PM

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);
+
+
+
+    }
 }