keycloak-aplcache

Details

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 0000000..ad3ca58
--- /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 <a href="https://docs.openshift.com/enterprise/3.0/architecture/additional_concepts/authentication.html">official documentation</a> for more details.
+ */
+public class OpenshiftV3IdentityProvider extends AbstractOAuth2IdentityProvider<OpenshifV3IdentityProviderConfig> implements SocialIdentityProvider<OpenshifV3IdentityProviderConfig> {
+
+    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 0000000..b370530
--- /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<OpenshiftV3IdentityProvider> implements SocialIdentityProviderFactory<OpenshiftV3IdentityProvider> {
+
+    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/java/org/keycloak/social/openshift/OpenshifV3IdentityProviderConfig.java b/services/src/main/java/org/keycloak/social/openshift/OpenshifV3IdentityProviderConfig.java
new file mode 100644
index 0000000..d945aac
--- /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/resources/META-INF/services/org.keycloak.broker.social.SocialIdentityProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.broker.social.SocialIdentityProviderFactory
index 1311132..00a5e51 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 09ddb39..1fb0a63 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 1bfc295..09fa373 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
@@ -92,6 +92,20 @@
             }
         },
         {
+            "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",
             "displayName": "My SAML",
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 0000000..a4630ac
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3.html
@@ -0,0 +1 @@
+<div data-ng-include data-src="resourceUrl + '/partials/realm-identity-provider-social.html'"></div>
\ No newline at end of file
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 0000000..e3e82a1
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-openshift-v3-ext.html
@@ -0,0 +1,6 @@
+<div class="form-group clearfix">
+    <label class="col-md-2 control-label" for="baseUrl"><span class="required">*</span> {{:: 'Base URL' | translate}}</label>
+    <div class="col-md-6">
+        <input class="form-control" id="baseUrl" type="text" ng-model="identityProvider.config.baseUrl" required>
+    </div>
+</div>