From 1a6bb2fedb16338fef24e6166cee87f50a6ec997 Mon Sep 17 00:00:00 2001 From: Bartosz Majsak Date: Thu, 2 Mar 2017 15:04:33 +0100 Subject: [PATCH] Adds Openshift Identity Provider as part of social brokers --- .../OpenshifV3IdentityProviderConfig.java | 20 ++++++ .../OpenshiftV3IdentityProvider.java | 66 +++++++++++++++++++ .../OpenshiftV3IdentityProviderFactory.java | 27 ++++++++ ...roker.social.SocialIdentityProviderFactory | 1 + .../broker/ImportIdentityProviderTest.java | 20 ++++++ .../broker-test/test-realm-with-broker.json | 14 ++++ ...lm-identity-provider-openshift-v3-ext.html | 6 ++ .../realm-identity-provider-openshift-v3.html | 1 + 8 files changed, 155 insertions(+) create mode 100644 services/src/main/java/org/keycloak/social/openshift/OpenshifV3IdentityProviderConfig.java create mode 100644 services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProvider.java create mode 100644 services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProviderFactory.java create mode 100644 themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3-ext.html create mode 100755 themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3.html diff --git a/services/src/main/java/org/keycloak/social/openshift/OpenshifV3IdentityProviderConfig.java b/services/src/main/java/org/keycloak/social/openshift/OpenshifV3IdentityProviderConfig.java new file mode 100644 index 0000000000..d945aaca21 --- /dev/null +++ b/services/src/main/java/org/keycloak/social/openshift/OpenshifV3IdentityProviderConfig.java @@ -0,0 +1,20 @@ +package org.keycloak.social.openshift; + +import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; +import org.keycloak.models.IdentityProviderModel; + +public class OpenshifV3IdentityProviderConfig extends OAuth2IdentityProviderConfig { + private static final String BASE_URL = "baseUrl"; + + public OpenshifV3IdentityProviderConfig(IdentityProviderModel identityProviderModel) { + super(identityProviderModel); + } + + public String getBaseUrl() { + return getConfig().get(BASE_URL); + } + + public void setBaseUrl(String baseUrl) { + getConfig().put(BASE_URL, baseUrl); + } +} diff --git a/services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProvider.java b/services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProvider.java new file mode 100644 index 0000000000..ad3ca580b5 --- /dev/null +++ b/services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProvider.java @@ -0,0 +1,66 @@ +package org.keycloak.social.openshift; + +import com.fasterxml.jackson.databind.JsonNode; +import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; +import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper; +import org.keycloak.broker.oidc.util.JsonSimpleHttp; +import org.keycloak.broker.provider.BrokeredIdentityContext; +import org.keycloak.broker.provider.IdentityBrokerException; +import org.keycloak.broker.provider.util.SimpleHttp; +import org.keycloak.broker.social.SocialIdentityProvider; +import org.keycloak.models.KeycloakSession; + +import java.io.IOException; +import java.util.Optional; + +/** + * Identity provider for Openshift V3. Check official documentation for more details. + */ +public class OpenshiftV3IdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider { + + public static final String BASE_URL = "https://api.preview.openshift.com"; + private static final String AUTH_RESOURCE = "/oauth/authorize"; + private static final String TOKEN_RESOURCE = "/oauth/token"; + private static final String PROFILE_RESOURCE = "/oapi/v1/users/~"; + private static final String DEFAULT_SCOPE = "user:info"; + + public OpenshiftV3IdentityProvider(KeycloakSession session, OpenshifV3IdentityProviderConfig config) { + super(session, config); + final String baseUrl = Optional.ofNullable(config.getBaseUrl()).orElse(BASE_URL); + config.setAuthorizationUrl(baseUrl + AUTH_RESOURCE); + config.setTokenUrl(baseUrl + TOKEN_RESOURCE); + config.setUserInfoUrl(baseUrl + PROFILE_RESOURCE); + } + + @Override + protected String getDefaultScopes() { + return DEFAULT_SCOPE; + } + + @Override + protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) { + try { + final JsonNode profile = fetchProfile(accessToken); + final BrokeredIdentityContext user = extractUserContext(profile.get("metadata")); + AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias()); + return user; + } catch (Exception e) { + throw new IdentityBrokerException("Could not obtain user profile from Openshift.", e); + } + } + + private BrokeredIdentityContext extractUserContext(JsonNode metadata) { + final BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(metadata, "uid")); + user.setUsername(getJsonProperty(metadata, "name")); + user.setName(getJsonProperty(metadata, "fullName")); + user.setIdpConfig(getConfig()); + user.setIdp(this); + return user; + } + + private JsonNode fetchProfile(String accessToken) throws IOException { + return JsonSimpleHttp.asJson(SimpleHttp.doGet(getConfig().getUserInfoUrl()) + .header("Authorization", "Bearer " + accessToken)); + } + +} diff --git a/services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProviderFactory.java b/services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProviderFactory.java new file mode 100644 index 0000000000..b3705301d6 --- /dev/null +++ b/services/src/main/java/org/keycloak/social/openshift/OpenshiftV3IdentityProviderFactory.java @@ -0,0 +1,27 @@ +package org.keycloak.social.openshift; + +import org.keycloak.broker.provider.AbstractIdentityProviderFactory; +import org.keycloak.broker.social.SocialIdentityProviderFactory; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; + +public class OpenshiftV3IdentityProviderFactory extends AbstractIdentityProviderFactory implements SocialIdentityProviderFactory { + + public static final String PROVIDER_ID = "openshift-v3"; + + @Override + public String getName() { + return "Openshift v3"; + } + + @Override + public OpenshiftV3IdentityProvider create(KeycloakSession keycloakSession, IdentityProviderModel identityProviderModel) { + return new OpenshiftV3IdentityProvider(keycloakSession, new OpenshifV3IdentityProviderConfig(identityProviderModel)); + } + + @Override + public String getId() { + return PROVIDER_ID; + } + +} diff --git a/services/src/main/resources/META-INF/services/org.keycloak.broker.social.SocialIdentityProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.broker.social.SocialIdentityProviderFactory index 1311132928..00a5e5163d 100755 --- a/services/src/main/resources/META-INF/services/org.keycloak.broker.social.SocialIdentityProviderFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.broker.social.SocialIdentityProviderFactory @@ -22,3 +22,4 @@ org.keycloak.social.linkedin.LinkedInIdentityProviderFactory org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory org.keycloak.social.twitter.TwitterIdentityProviderFactory org.keycloak.social.microsoft.MicrosoftIdentityProviderFactory +org.keycloak.social.openshift.OpenshiftV3IdentityProviderFactory 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 09ddb39a3a..1fb0a63090 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 @@ -37,6 +37,9 @@ import org.keycloak.social.google.GoogleIdentityProvider; import org.keycloak.social.google.GoogleIdentityProviderFactory; import org.keycloak.social.linkedin.LinkedInIdentityProvider; import org.keycloak.social.linkedin.LinkedInIdentityProviderFactory; +import org.keycloak.social.openshift.OpenshifV3IdentityProviderConfig; +import org.keycloak.social.openshift.OpenshiftV3IdentityProvider; +import org.keycloak.social.openshift.OpenshiftV3IdentityProviderFactory; import org.keycloak.social.stackoverflow.StackOverflowIdentityProviderConfig; import org.keycloak.social.stackoverflow.StackoverflowIdentityProvider; import org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory; @@ -146,6 +149,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes assertLinkedInIdentityProviderConfig(identityProvider); } else if (StackoverflowIdentityProviderFactory.PROVIDER_ID.equals(providerId)) { assertStackoverflowIdentityProviderConfig(identityProvider); + } else if (OpenshiftV3IdentityProviderFactory.PROVIDER_ID.equals(providerId)) { + assertOpenshiftIdentityProviderConfig(identityProvider); } else { continue; } @@ -283,6 +288,21 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes assertEquals(StackoverflowIdentityProvider.PROFILE_URL, config.getUserInfoUrl()); } + private void assertOpenshiftIdentityProviderConfig(IdentityProviderModel identityProvider) { + OpenshiftV3IdentityProvider osoIdentityProvider = new OpenshiftV3IdentityProviderFactory().create(session, identityProvider); + OpenshifV3IdentityProviderConfig config = osoIdentityProvider.getConfig(); + + assertEquals("model-openshift-v3", config.getAlias()); + assertEquals(OpenshiftV3IdentityProviderFactory.PROVIDER_ID, config.getProviderId()); + assertEquals(true, config.isEnabled()); + assertEquals(false, config.isTrustEmail()); + assertEquals(false, config.isAuthenticateByDefault()); + assertEquals(true, config.isStoreToken()); + assertEquals(OpenshiftV3IdentityProvider.BASE_URL, config.getBaseUrl()); + assertEquals("clientId", config.getClientId()); + assertEquals("clientSecret", config.getClientSecret()); + } + private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) { TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(session, 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 1bfc295905..09fa373bbe 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 @@ -91,6 +91,20 @@ "clientSecret": "clientSecret" } }, + { + "alias" : "model-openshift-v3", + "providerId" : "openshift-v3", + "enabled": true, + "storeToken": true, + "config": { + "baseUrl": "https://api.preview.openshift.com", + "authorizationUrl": "authorizationUrl", + "tokenUrl": "tokenUrl", + "userInfoUrl": "userInfoUrl", + "clientId": "clientId", + "clientSecret": "clientSecret" + } + }, { "alias" : "model-saml-signed-idp", "providerId" : "saml", diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3-ext.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3-ext.html new file mode 100644 index 0000000000..e3e82a1765 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3-ext.html @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3.html new file mode 100755 index 0000000000..a4630ac786 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3.html @@ -0,0 +1 @@ +
\ No newline at end of file