Adds Openshift Identity Provider as part of social brokers
This commit is contained in:
parent
b8767d13d5
commit
1a6bb2fedb
8 changed files with 155 additions and 0 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,3 +22,4 @@ org.keycloak.social.linkedin.LinkedInIdentityProviderFactory
|
||||||
org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory
|
org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory
|
||||||
org.keycloak.social.twitter.TwitterIdentityProviderFactory
|
org.keycloak.social.twitter.TwitterIdentityProviderFactory
|
||||||
org.keycloak.social.microsoft.MicrosoftIdentityProviderFactory
|
org.keycloak.social.microsoft.MicrosoftIdentityProviderFactory
|
||||||
|
org.keycloak.social.openshift.OpenshiftV3IdentityProviderFactory
|
||||||
|
|
|
@ -37,6 +37,9 @@ import org.keycloak.social.google.GoogleIdentityProvider;
|
||||||
import org.keycloak.social.google.GoogleIdentityProviderFactory;
|
import org.keycloak.social.google.GoogleIdentityProviderFactory;
|
||||||
import org.keycloak.social.linkedin.LinkedInIdentityProvider;
|
import org.keycloak.social.linkedin.LinkedInIdentityProvider;
|
||||||
import org.keycloak.social.linkedin.LinkedInIdentityProviderFactory;
|
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.StackOverflowIdentityProviderConfig;
|
||||||
import org.keycloak.social.stackoverflow.StackoverflowIdentityProvider;
|
import org.keycloak.social.stackoverflow.StackoverflowIdentityProvider;
|
||||||
import org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory;
|
import org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory;
|
||||||
|
@ -146,6 +149,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
assertLinkedInIdentityProviderConfig(identityProvider);
|
assertLinkedInIdentityProviderConfig(identityProvider);
|
||||||
} else if (StackoverflowIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
} else if (StackoverflowIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
||||||
assertStackoverflowIdentityProviderConfig(identityProvider);
|
assertStackoverflowIdentityProviderConfig(identityProvider);
|
||||||
|
} else if (OpenshiftV3IdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
||||||
|
assertOpenshiftIdentityProviderConfig(identityProvider);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -283,6 +288,21 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
assertEquals(StackoverflowIdentityProvider.PROFILE_URL, config.getUserInfoUrl());
|
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) {
|
private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||||
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(session, identityProvider);
|
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(session, identityProvider);
|
||||||
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
|
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
|
||||||
|
|
|
@ -91,6 +91,20 @@
|
||||||
"clientSecret": "clientSecret"
|
"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",
|
"alias" : "model-saml-signed-idp",
|
||||||
"providerId" : "saml",
|
"providerId" : "saml",
|
||||||
|
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
||||||
|
<div data-ng-include data-src="resourceUrl + '/partials/realm-identity-provider-social.html'"></div>
|
Loading…
Reference in a new issue