diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/util/SimpleHttp.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/util/SimpleHttp.java index 870e951a96..c9050b8296 100644 --- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/util/SimpleHttp.java +++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/util/SimpleHttp.java @@ -13,9 +13,11 @@ import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; +import java.util.zip.GZIPInputStream; /** * @author Stian Thorgersen + * @author Vlastimil Elias (velias at redhat dot com) */ public class SimpleHttp { @@ -116,7 +118,11 @@ public class SimpleHttp { connection.setDoOutput(false); } + String ce = connection.getHeaderField("Content-Encoding"); is = connection.getInputStream(); + if ("gzip".equals(ce)) { + is = new GZIPInputStream(is); + } return toString(is); } finally { if (os != null) { diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index a7bd5223bb..2c1f6f350f 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -127,6 +127,11 @@ keycloak-social-linkedin ${project.version} + + org.keycloak + keycloak-social-stackoverflow + ${project.version} + diff --git a/distribution/modules/build.xml b/distribution/modules/build.xml index ad0f9e7c11..80ff507230 100755 --- a/distribution/modules/build.xml +++ b/distribution/modules/build.xml @@ -239,6 +239,10 @@ + + + + diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml index f8a251d7a5..f553d24263 100755 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml @@ -58,6 +58,7 @@ + diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml index 6166b9b6ac..86e86f492e 100755 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml @@ -61,6 +61,7 @@ + diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-stackoverflow/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-stackoverflow/main/module.xml new file mode 100755 index 0000000000..6ddd2a4b93 --- /dev/null +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-stackoverflow/main/module.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml index 6a97ca5866..6caa2c81e5 100755 --- a/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml +++ b/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -51,6 +51,7 @@ + diff --git a/docbook/reference/en/en-US/modules/identity-broker.xml b/docbook/reference/en/en-US/modules/identity-broker.xml index 3a74b734d8..5ebbbe239f 100755 --- a/docbook/reference/en/en-US/modules/identity-broker.xml +++ b/docbook/reference/en/en-US/modules/identity-broker.xml @@ -807,6 +807,78 @@ + +
+ StackOverflow + + To enable login with StackOverflow you first have to register an OAuth application on + StackApps. Then you need to copy the client id, secret and key into the Keycloak Admin Console. + + + Let's see first how to create an application with StackOverflow. + + + + + Go to registering your application on Stack Apps url and login here. + Use any value for Application Name, Application Website and Description you want. + Set OAuth Domain to the domain where your Keycloak instance runs. + Click the Register Your Application button. + + + + + Copy Client Id, Client Secret and Key from the shown page. + + + + + Now that you have the client id, secret and key, you can proceed with the creation of a StackOverflow Identity Provider in Keycloak. As follows: + + + + + Select the StackOverflow identity provider from the drop-down box on the top right corner of the identity providers table in Keycloak's Admin Console. You should be presented with a specific page to configure the selected provided. + + + + + Copy the client id, client secret and key to their corresponding fields in the Keycloak Admin Console. Click Save. + + + + + That is it! This pretty much what you need to do in order to setup this identity provider. + + + The table below lists some additional configuration options you may use when configuring this provider. + + + Configuration Options + + + + + Configuration + + + Description + + + + + + + Default Scopes + + + Allows you to manually specify the scopes that users must authorize when authenticating with this provider. + For a complete list of scopes, please take a look at application configuration in StackExchange API Authentication documentation. Keycloak uses the empty scope by default. + + + + +
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-facebook-ext.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-facebook-ext.html new file mode 100755 index 0000000000..e69de29bb2 diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-github-ext.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-github-ext.html new file mode 100755 index 0000000000..e69de29bb2 diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-google-ext.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-google-ext.html new file mode 100755 index 0000000000..e69de29bb2 diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-linkedin-ext.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-linkedin-ext.html new file mode 100755 index 0000000000..e69de29bb2 diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-linkedin.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-linkedin.html similarity index 100% rename from forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-linkedin.html rename to forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-linkedin.html diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html index aa6a5ebe98..4a23349a38 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html @@ -28,12 +28,13 @@ +
- +
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html new file mode 100755 index 0000000000..86516dfaaa --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html @@ -0,0 +1,7 @@ +
+ +
+ +
+ +
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow.html new file mode 100755 index 0000000000..a4630ac786 --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-twitter-ext.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-twitter-ext.html new file mode 100755 index 0000000000..e69de29bb2 diff --git a/forms/common-themes/src/main/resources/theme/patternfly/login/resources/css/login.css b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/css/login.css index 345f594a06..2bc07bb274 100644 --- a/forms/common-themes/src/main/resources/theme/patternfly/login/resources/css/login.css +++ b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/css/login.css @@ -238,7 +238,7 @@ ol#kc-totp-settings li:first-of-type { } .zocial { - width: 125px; + width: 150px; } .zocial:hover { diff --git a/social/pom.xml b/social/pom.xml index ded7c60eed..ec08ca2309 100755 --- a/social/pom.xml +++ b/social/pom.xml @@ -21,6 +21,7 @@ twitter facebook linkedin + stackoverflow diff --git a/social/stackoverflow/.gitignore b/social/stackoverflow/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/social/stackoverflow/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/social/stackoverflow/pom.xml b/social/stackoverflow/pom.xml new file mode 100755 index 0000000000..f05a77b563 --- /dev/null +++ b/social/stackoverflow/pom.xml @@ -0,0 +1,40 @@ + + + keycloak-social-parent + org.keycloak + 1.2.0.Beta1-SNAPSHOT + ../pom.xml + + 4.0.0 + jar + + keycloak-social-stackoverflow + Keycloak Social StackOverflow + + + + + org.keycloak + keycloak-social-core + ${project.version} + provided + + + org.keycloak + keycloak-broker-oidc + ${project.version} + provided + + + org.codehaus.jackson + jackson-mapper-asl + provided + + + org.jboss.logging + jboss-logging + provided + + + diff --git a/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackOverflowIdentityProviderConfig.java b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackOverflowIdentityProviderConfig.java new file mode 100644 index 0000000000..f531d7c71d --- /dev/null +++ b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackOverflowIdentityProviderConfig.java @@ -0,0 +1,40 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates. + * + * 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.social.stackoverflow; + +import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; +import org.keycloak.models.IdentityProviderModel; + +/** + * @author Vlastimil Elias (velias at redhat dot com) + */ +public class StackOverflowIdentityProviderConfig extends OAuth2IdentityProviderConfig { + + public StackOverflowIdentityProviderConfig(IdentityProviderModel model) { + super(model); + } + + public String getKey() { + return getConfig().get("key"); + } + + public void setKey(String key) { + getConfig().put("key", key); + } + +} \ No newline at end of file diff --git a/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java new file mode 100755 index 0000000000..40fb32f2cd --- /dev/null +++ b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java @@ -0,0 +1,214 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2015 Red Hat, Inc. and/or its affiliates. + * + * 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.social.stackoverflow; + +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.HashMap; + +import org.codehaus.jackson.JsonNode; +import org.jboss.logging.Logger; +import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; +import org.keycloak.broker.oidc.util.SimpleHttp; +import org.keycloak.broker.provider.FederatedIdentity; +import org.keycloak.broker.provider.IdentityBrokerException; +import org.keycloak.social.SocialIdentityProvider; + +/** + * Stackoverflow social provider. See https://api.stackexchange.com/docs/authentication + * + * @author Vlastimil Elias (velias at redhat dot com) + */ +public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvider + implements SocialIdentityProvider { + + private static final Logger log = Logger.getLogger(StackoverflowIdentityProvider.class); + + public static final String AUTH_URL = "https://stackexchange.com/oauth"; + public static final String TOKEN_URL = "https://stackexchange.com/oauth/access_token"; + public static final String PROFILE_URL = "https://api.stackexchange.com/2.2/me?order=desc&sort=name&site=stackoverflow"; + public static final String DEFAULT_SCOPE = ""; + + public StackoverflowIdentityProvider(StackOverflowIdentityProviderConfig config) { + super(config); + config.setAuthorizationUrl(AUTH_URL); + config.setTokenUrl(TOKEN_URL); + config.setUserInfoUrl(PROFILE_URL); + } + + @Override + protected FederatedIdentity doGetFederatedIdentity(String accessToken) { + log.debug("doGetFederatedIdentity()"); + try { + + String URL = PROFILE_URL + "&access_token=" + accessToken + "&key=" + getConfig().getKey(); + if (log.isDebugEnabled()) { + log.debug("StackOverflow profile request to: " + URL); + } + JsonNode profile = SimpleHttp.doGet(URL).asJson().get("items").get(0); + + FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "user_id")); + + user.setUsername(extractUsernameFromProfileURL(getJsonProperty(profile, "link"))); + user.setName(unescapeHtml3(getJsonProperty(profile, "display_name"))); + // email is not provided + // user.setEmail(getJsonProperty(profile, "email")); + + return user; + } catch (Exception e) { + throw new IdentityBrokerException("Could not obtain user profile from Stackoverflow: " + e.getMessage(), e); + } + } + + protected static String extractUsernameFromProfileURL(String profileURL) { + if (isNotBlank(profileURL)) { + + try { + log.debug("go to extract username from profile URL " + profileURL); + URL u = new URL(profileURL); + String path = u.getPath(); + if (isNotBlank(path) && path.length() > 1) { + if (path.startsWith("/")) { + path = path.substring(1); + } + String[] pe = path.split("/"); + if (pe.length >= 3) { + return URLDecoder.decode(pe[2], "UTF-8"); + } else { + log.warn("Stackoverflow profile URL path is without third part: " + profileURL); + } + } else { + log.warn("Stackoverflow profile URL is without path part: " + profileURL); + } + } catch (MalformedURLException e) { + log.warn("Stackoverflow profile URL is malformed: " + profileURL); + } catch (Exception e) { + log.warn("Stackoverflow profile URL " + profileURL + " username extraction failed due: " + e.getMessage()); + } + } + return null; + } + + private static boolean isNotBlank(String s) { + return s != null && s.trim().length() > 0; + } + + @Override + protected String getDefaultScopes() { + return DEFAULT_SCOPE; + } + + public static final String unescapeHtml3(final String input) { + if (input == null) + return null; + StringWriter writer = null; + int len = input.length(); + int i = 1; + int st = 0; + while (true) { + // look for '&' + while (i < len && input.charAt(i - 1) != '&') + i++; + if (i >= len) + break; + + // found '&', look for ';' + int j = i; + while (j < len && j < i + MAX_ESCAPE + 1 && input.charAt(j) != ';') + j++; + if (j == len || j < i + MIN_ESCAPE || j == i + MAX_ESCAPE + 1) { + i++; + continue; + } + + // found escape + if (input.charAt(i) == '#') { + // numeric escape + int k = i + 1; + int radix = 10; + + final char firstChar = input.charAt(k); + if (firstChar == 'x' || firstChar == 'X') { + k++; + radix = 16; + } + + try { + int entityValue = Integer.parseInt(input.substring(k, j), radix); + + if (writer == null) + writer = new StringWriter(input.length()); + writer.append(input.substring(st, i - 1)); + + if (entityValue > 0xFFFF) { + final char[] chrs = Character.toChars(entityValue); + writer.write(chrs[0]); + writer.write(chrs[1]); + } else { + writer.write(entityValue); + } + + } catch (NumberFormatException ex) { + i++; + continue; + } + } else { + // named escape + CharSequence value = lookupMap.get(input.substring(i, j)); + if (value == null) { + i++; + continue; + } + + if (writer == null) + writer = new StringWriter(input.length()); + writer.append(input.substring(st, i - 1)); + + writer.append(value); + } + + // skip escape + st = j + 1; + i = st; + } + + if (writer != null) { + writer.append(input.substring(st, len)); + return writer.toString(); + } + return input; + } + + private static final String[][] ESCAPES = { { "\"", "quot" }, // " - double-quote + { "&", "amp" }, // & - ampersand + { "<", "lt" }, // < - less-than + { ">", "gt" }, // > - greater-than + }; + + private static final int MIN_ESCAPE = 2; + private static final int MAX_ESCAPE = 6; + + private static final HashMap lookupMap; + static { + lookupMap = new HashMap(); + for (final CharSequence[] seq : ESCAPES) + lookupMap.put(seq[1].toString(), seq[0]); + } +} diff --git a/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProviderFactory.java b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProviderFactory.java new file mode 100644 index 0000000000..d02c2d7261 --- /dev/null +++ b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProviderFactory.java @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2015 Red Hat, Inc. and/or its affiliates. + * + * 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.social.stackoverflow; + +import org.keycloak.broker.provider.AbstractIdentityProviderFactory; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.social.SocialIdentityProviderFactory; + +/** + * @author Vlastimil Elias (velias at redhat dot com) + */ +public class StackoverflowIdentityProviderFactory extends + AbstractIdentityProviderFactory implements + SocialIdentityProviderFactory { + + public static final String PROVIDER_ID = "stackoverflow"; + + @Override + public String getName() { + return "StackOverflow"; + } + + @Override + public StackoverflowIdentityProvider create(IdentityProviderModel model) { + return new StackoverflowIdentityProvider(new StackOverflowIdentityProviderConfig(model)); + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} diff --git a/social/stackoverflow/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory b/social/stackoverflow/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory new file mode 100644 index 0000000000..4bbbe9c0e6 --- /dev/null +++ b/social/stackoverflow/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory @@ -0,0 +1 @@ +org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory \ No newline at end of file diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java index 84bbc9c51f..57886e7af2 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java @@ -25,6 +25,7 @@ import org.keycloak.social.github.GitHubIdentityProviderFactory; import org.keycloak.social.google.GoogleIdentityProviderFactory; import org.keycloak.social.twitter.TwitterIdentityProviderFactory; import org.keycloak.social.linkedin.LinkedInIdentityProviderFactory; +import org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory; import org.keycloak.testsuite.model.AbstractModelTest; import java.util.Collections; @@ -49,6 +50,7 @@ public abstract class AbstractIdentityProviderModelTest extends AbstractModelTes this.expectedProviders.add(GitHubIdentityProviderFactory.PROVIDER_ID); this.expectedProviders.add(TwitterIdentityProviderFactory.PROVIDER_ID); this.expectedProviders.add(LinkedInIdentityProviderFactory.PROVIDER_ID); + this.expectedProviders.add(StackoverflowIdentityProviderFactory.PROVIDER_ID); this.expectedProviders = Collections.unmodifiableSet(this.expectedProviders); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java index f596f1b272..eda8b6902c 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java @@ -17,6 +17,11 @@ */ package org.keycloak.testsuite.broker; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import org.junit.Test; import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; import org.keycloak.broker.oidc.OIDCIdentityProvider; @@ -36,15 +41,13 @@ import org.keycloak.social.github.GitHubIdentityProvider; import org.keycloak.social.github.GitHubIdentityProviderFactory; import org.keycloak.social.google.GoogleIdentityProvider; import org.keycloak.social.google.GoogleIdentityProviderFactory; -import org.keycloak.social.twitter.TwitterIdentityProvider; -import org.keycloak.social.twitter.TwitterIdentityProviderFactory; import org.keycloak.social.linkedin.LinkedInIdentityProvider; import org.keycloak.social.linkedin.LinkedInIdentityProviderFactory; - -import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import org.keycloak.social.stackoverflow.StackOverflowIdentityProviderConfig; +import org.keycloak.social.stackoverflow.StackoverflowIdentityProvider; +import org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory; +import org.keycloak.social.twitter.TwitterIdentityProvider; +import org.keycloak.social.twitter.TwitterIdentityProviderFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -164,6 +167,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes assertTwitterIdentityProviderConfig(identityProvider); } else if (LinkedInIdentityProviderFactory.PROVIDER_ID.equals(providerId)) { assertLinkedInIdentityProviderConfig(identityProvider); + } else if (StackoverflowIdentityProviderFactory.PROVIDER_ID.equals(providerId)) { + assertStackoverflowIdentityProviderConfig(identityProvider); } else { continue; } @@ -262,8 +267,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes } private void assertLinkedInIdentityProviderConfig(IdentityProviderModel identityProvider) { - LinkedInIdentityProvider gitHubIdentityProvider = new LinkedInIdentityProviderFactory().create(identityProvider); - OAuth2IdentityProviderConfig config = gitHubIdentityProvider.getConfig(); + LinkedInIdentityProvider liIdentityProvider = new LinkedInIdentityProviderFactory().create(identityProvider); + OAuth2IdentityProviderConfig config = liIdentityProvider.getConfig(); assertEquals("model-linkedin", config.getAlias()); assertEquals(LinkedInIdentityProviderFactory.PROVIDER_ID, config.getProviderId()); @@ -278,6 +283,24 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes assertEquals(LinkedInIdentityProvider.PROFILE_URL, config.getUserInfoUrl()); } + private void assertStackoverflowIdentityProviderConfig(IdentityProviderModel identityProvider) { + StackoverflowIdentityProvider soIdentityProvider = new StackoverflowIdentityProviderFactory().create(identityProvider); + StackOverflowIdentityProviderConfig config = soIdentityProvider.getConfig(); + + assertEquals("model-stackoverflow", config.getAlias()); + assertEquals(StackoverflowIdentityProviderFactory.PROVIDER_ID, config.getProviderId()); + assertEquals(true, config.isEnabled()); + assertEquals(true, config.isUpdateProfileFirstLogin()); + assertEquals(false, config.isAuthenticateByDefault()); + assertEquals(false, config.isStoreToken()); + assertEquals("clientId", config.getClientId()); + assertEquals("clientSecret", config.getClientSecret()); + assertEquals("keyValue", config.getKey()); + assertEquals(StackoverflowIdentityProvider.AUTH_URL, config.getAuthorizationUrl()); + assertEquals(StackoverflowIdentityProvider.TOKEN_URL, config.getTokenUrl()); + assertEquals(StackoverflowIdentityProvider.PROFILE_URL, config.getUserInfoUrl()); + } + private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) { TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider); OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig(); diff --git a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json index 96cdc9602f..71744205a0 100755 --- a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json +++ b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json @@ -75,6 +75,21 @@ "clientSecret": "clientSecret" } }, + { + "alias" : "model-stackoverflow", + "providerId" : "stackoverflow", + "enabled": true, + "updateProfileFirstLogin" : "true", + "storeToken": false, + "config": { + "key": "keyValue", + "authorizationUrl": "authorizationUrl", + "tokenUrl": "tokenUrl", + "userInfoUrl": "userInfoUrl", + "clientId": "clientId", + "clientSecret": "clientSecret" + } + }, { "alias" : "model-saml-signed-idp", "providerId" : "saml",