Add ability to use internal network for keycloak. (#18225)

* Add ability to use internal network for keycloak.

Signed-off-by: Oleksandr Andriienko <oandriie@redhat.com>
7.24.x
Oleksandr Andriienko 2020-11-26 17:27:18 +02:00 committed by GitHub
parent b562d092c6
commit 4100db3053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 73 additions and 14 deletions

View File

@ -119,6 +119,9 @@ che.infra.openshift.oauth_identity_provider=NULL
# is used
che.keycloak.auth_server_url=http://${CHE_HOST}:5050/auth
# Internal network service Url to keycloak identity provider server
che.keycloak.auth_internal_server_url=NULL
# Keycloak realm is used to authenticate users
# Can be set to NULL only if `che.keycloak.oidcProvider`
# is used

View File

@ -84,6 +84,8 @@ public class IdentityProviderConfigFactory extends OpenShiftClientConfigFactory
+ "</strong> \n"
+ "identity provider by visiting the "
+ "<a href='"
// Here should be used public url. User should have it to make manual actions in the
// browser.
+ keycloakSettings.get().get(AUTH_SERVER_URL_SETTING)
+ "/realms/"
+ keycloakSettings.get().get(REALM_SETTING)

View File

@ -28,7 +28,10 @@ public class KeycloakJwkProvider implements Provider<JwkProvider> {
@Inject
public KeycloakJwkProvider(KeycloakSettings keycloakSettings) throws MalformedURLException {
final String jwksUrl = keycloakSettings.get().get(KeycloakConstants.JWKS_ENDPOINT_SETTING);
final String jwksUrl =
keycloakSettings.getInternalSettings().get(KeycloakConstants.JWKS_ENDPOINT_SETTING);
if (jwksUrl == null) {
throw new ConfigurationException("Jwks endpoint url not found in keycloak settings");
}

View File

@ -40,7 +40,7 @@ public class KeycloakProfileRetriever {
KeycloakSettings keycloakSettings, HttpJsonRequestFactory requestFactory) {
this.requestFactory = requestFactory;
this.keyclockCurrentUserInfoUrl =
keycloakSettings.get().get(KeycloakConstants.USERINFO_ENDPOINT_SETTING);
keycloakSettings.getInternalSettings().get(KeycloakConstants.USERINFO_ENDPOINT_SETTING);
}
/**

View File

@ -11,7 +11,7 @@
*/
package org.eclipse.che.multiuser.keycloak.server;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_INTERNAL_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
import com.google.common.io.CharStreams;
@ -101,7 +101,8 @@ public class KeycloakServiceClient {
byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
final String hash = Base64.getUrlEncoder().encodeToString(check);
return UriBuilder.fromUri(keycloakSettings.get().get(AUTH_SERVER_URL_SETTING))
return UriBuilder.fromUri(
keycloakSettings.getInternalSettings().get(AUTH_SERVER_URL_INTERNAL_SETTING))
.path("/realms/{realm}/broker/{provider}/link")
.queryParam("nonce", nonce)
.queryParam("hash", hash)
@ -127,7 +128,8 @@ public class KeycloakServiceClient {
throws ForbiddenException, BadRequestException, IOException, NotFoundException,
ServerException, UnauthorizedException {
String url =
UriBuilder.fromUri(keycloakSettings.get().get(AUTH_SERVER_URL_SETTING))
UriBuilder.fromUri(
keycloakSettings.getInternalSettings().get(AUTH_SERVER_URL_INTERNAL_SETTING))
.path("/realms/{realm}/broker/{provider}/token")
.build(keycloakSettings.get().get(REALM_SETTING), oauthProvider)
.toString();

View File

@ -11,6 +11,8 @@
*/
package org.eclipse.che.multiuser.keycloak.server;
import static com.google.common.base.MoreObjects.firstNonNull;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_INTERNAL_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_DASHBOARD;
@ -54,13 +56,26 @@ public class KeycloakSettings {
private static final Logger LOG = LoggerFactory.getLogger(KeycloakSettings.class);
private static final String DEFAULT_USERNAME_CLAIM = "preferred_username";
/**
* Public Keycloak connection settings. It contains information about keycloak api urls and
* information required to make Keycloak connection using public domain hostname. This info will
* be shared with frontend.
*/
private final Map<String, String> settings;
/**
* Internal network Keycloak connection settings. It contains information about keycloak api urls
* and information required to make connection using k8s/openshift internal services hostname.
* This info will be used only on the Che server side. If using internal network is disabled, then
* will be included settings with public domain hostname.
*/
private final Map<String, String> internalSettings;
@Inject
public KeycloakSettings(
@Named("che.api") String cheServerEndpoint,
@Nullable @Named(JS_ADAPTER_URL_SETTING) String jsAdapterUrl,
@Nullable @Named(AUTH_SERVER_URL_SETTING) String serverURL,
@Nullable @Named(AUTH_SERVER_URL_INTERNAL_SETTING) String serverInternalURL,
@Nullable @Named(REALM_SETTING) String realm,
@Named(CLIENT_ID_SETTING) String clientId,
@Nullable @Named(OIDC_PROVIDER_SETTING) String oidcProvider,
@ -70,10 +85,14 @@ public class KeycloakSettings {
@Nullable @Named(GITHUB_ENDPOINT_SETTING) String gitHubEndpoint,
@Named(USE_FIXED_REDIRECT_URLS_SETTING) boolean useFixedRedirectUrls) {
serverInternalURL = firstNonNull(serverInternalURL, serverURL);
if (serverURL == null && oidcProvider == null) {
throw new RuntimeException(
"Either the '"
+ AUTH_SERVER_URL_SETTING
+ "'or'"
+ AUTH_SERVER_URL_INTERNAL_SETTING
+ "' or '"
+ OIDC_PROVIDER_SETTING
+ "' property should be set");
@ -83,7 +102,7 @@ public class KeycloakSettings {
throw new RuntimeException("The '" + REALM_SETTING + "' property should be set");
}
String wellKnownEndpoint = oidcProvider != null ? oidcProvider : serverURL + "/realms/" + realm;
String wellKnownEndpoint = firstNonNull(oidcProvider, serverInternalURL + "/realms/" + realm);
if (!wellKnownEndpoint.endsWith("/")) {
wellKnownEndpoint = wellKnownEndpoint + "/";
}
@ -109,10 +128,16 @@ public class KeycloakSettings {
LOG.info("openid configuration = {}", openIdConfiguration);
Map<String, String> settings = Maps.newHashMap();
Map<String, String> internalSettings = Maps.newHashMap();
settings.put(
USERNAME_CLAIM_SETTING, usernameClaim == null ? DEFAULT_USERNAME_CLAIM : usernameClaim);
settings.put(CLIENT_ID_SETTING, clientId);
settings.put(REALM_SETTING, realm);
if (serverInternalURL != null) {
internalSettings.put(AUTH_SERVER_URL_INTERNAL_SETTING, serverInternalURL);
}
if (serverURL != null) {
settings.put(AUTH_SERVER_URL_SETTING, serverURL);
settings.put(PROFILE_ENDPOINT_SETTING, serverURL + "/realms/" + realm + "/account");
@ -132,14 +157,24 @@ public class KeycloakSettings {
if (tokenEndpoint != null) {
settings.put(TOKEN_ENDPOINT_SETTING, tokenEndpoint);
}
String userInfoEndpoint = (String) openIdConfiguration.get("userinfo_endpoint");
if (userInfoEndpoint != null) {
settings.put(USERINFO_ENDPOINT_SETTING, userInfoEndpoint);
if (serverURL != null) {
String internalInfoEndpoint = userInfoEndpoint.replace(serverURL, serverInternalURL);
internalSettings.put(USERINFO_ENDPOINT_SETTING, internalInfoEndpoint);
}
}
String jwksUriEndpoint = (String) openIdConfiguration.get("jwks_uri");
if (jwksUriEndpoint != null) {
settings.put(JWKS_ENDPOINT_SETTING, jwksUriEndpoint);
if (serverURL != null) {
String internalJwksUriEndpoint = jwksUriEndpoint.replace(serverURL, serverInternalURL);
internalSettings.put(JWKS_ENDPOINT_SETTING, internalJwksUriEndpoint);
}
}
settings.put(OSO_ENDPOINT_SETTING, osoEndpoint);
settings.put(GITHUB_ENDPOINT_SETTING, gitHubEndpoint);
@ -161,9 +196,14 @@ public class KeycloakSettings {
settings.put(JS_ADAPTER_URL_SETTING, jsAdapterUrl);
this.settings = Collections.unmodifiableMap(settings);
this.internalSettings = Collections.unmodifiableMap(internalSettings);
}
public Map<String, String> get() {
return settings;
}
public Map<String, String> getInternalSettings() {
return internalSettings;
}
}

View File

@ -25,7 +25,7 @@ import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.multiuser.keycloak.server.KeycloakProfileRetriever;
/**
* Fetches user profile from Keycloack server.
* Fetches user profile from Keycloak server.
*
* @author Max Shaposhnik (mshaposh@redhat.com)
* @author Sergii Leshchenko

View File

@ -13,7 +13,7 @@ package org.eclipse.che.multiuser.keycloak.server;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_INTERNAL_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@ -67,10 +67,12 @@ public class KeycloakServiceClientTest {
public void setUp() throws Exception {
keycloakServiceClient = new KeycloakServiceClient(keycloakSettings, jwtParser);
Map<String, String> conf = new HashMap<>();
conf.put(
AUTH_SERVER_URL_SETTING,
Map<String, String> confInternal = new HashMap<>();
confInternal.put(
AUTH_SERVER_URL_INTERNAL_SETTING,
RestAssured.baseURI + ":" + RestAssured.port + RestAssured.basePath);
conf.put(REALM_SETTING, "che");
when(keycloakSettings.getInternalSettings()).thenReturn(confInternal);
when(keycloakSettings.get()).thenReturn(conf);
}

View File

@ -18,6 +18,9 @@ public class KeycloakConstants {
private static final String KEYCLOAK_SETTINGS_ENDPOINT_PATH = "/keycloak/settings";
public static final String AUTH_SERVER_URL_SETTING = KEYCLOAK_SETTING_PREFIX + "auth_server_url";
public static final String AUTH_SERVER_URL_INTERNAL_SETTING =
KEYCLOAK_SETTING_PREFIX + "auth_internal_server_url";
public static final String REALM_SETTING = KEYCLOAK_SETTING_PREFIX + "realm";
public static final String CLIENT_ID_SETTING = KEYCLOAK_SETTING_PREFIX + "client_id";
public static final String OIDC_PROVIDER_SETTING = KEYCLOAK_SETTING_PREFIX + "oidc_provider";

View File

@ -13,8 +13,7 @@ package org.eclipse.che.multiuser.keycloak.server;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.ws.rs.HttpMethod.POST;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.*;
import com.google.common.base.Strings;
import com.google.gson.JsonElement;
@ -76,9 +75,14 @@ public class KeycloakUserRemover {
this.keycloakPassword = keycloakPassword;
this.requestFactory = requestFactory;
if (userRemovalEnabled) {
String serverUrl = keycloakSettings.get().get(AUTH_SERVER_URL_SETTING);
String serverUrl =
keycloakSettings.getInternalSettings().get(AUTH_SERVER_URL_INTERNAL_SETTING);
if (serverUrl == null) {
throw new ConfigurationException(AUTH_SERVER_URL_SETTING + " is not configured");
throw new ConfigurationException(
AUTH_SERVER_URL_SETTING
+ " or "
+ AUTH_SERVER_URL_INTERNAL_SETTING
+ " is not configured");
}
String realm = keycloakSettings.get().get(REALM_SETTING);
if (realm == null) {