Ability to authenticate Oauth flow (#6326)
* Add keycloak token to oauth authenticate call * fixup! Add keycloak token to oauth authenticate call * fixup! Add keycloak token to oauth authenticate call * Fix dashboard build * fixup! Add keycloak token to oauth authenticate call * fixup! Add keycloak token to oauth authenticate call * Add security token for websocket url (#6319) * Add security token for websocket url Signed-off-by: Vitalii Parfonov <vparfonov@redhat.com> * Fix failed test (#6325) Signed-off-by: Vitalii Parfonov <vparfonov@redhat.com>6.19.x
parent
36f73bb4b4
commit
d4f03cbc4a
|
|
@ -11,18 +11,10 @@
|
|||
package org.eclipse.che.api.deploy;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import org.eclipse.che.api.environment.server.MachineLinksInjector;
|
||||
import org.eclipse.che.api.workspace.server.WorkspaceServiceLinksInjector;
|
||||
import org.eclipse.che.commons.auth.token.HeaderRequestTokenExtractor;
|
||||
import org.eclipse.che.commons.auth.token.ChainedTokenExtractor;
|
||||
import org.eclipse.che.commons.auth.token.RequestTokenExtractor;
|
||||
import org.eclipse.che.inject.DynaModule;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.AuthWsAgentHealthChecker;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.MachineAuthLinksInjector;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.MachineSessionInvalidator;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.MachineTokenPermissionsFilter;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.MachineTokenRegistry;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.MachineTokenService;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.WorkspaceServiceAuthLinksInjector;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.interceptor.InterceptorModule;
|
||||
|
||||
/**
|
||||
|
|
@ -32,18 +24,23 @@ import org.eclipse.che.multiuser.machine.authentication.server.interceptor.Inter
|
|||
*/
|
||||
@DynaModule
|
||||
public class MachineAuthModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(new InterceptorModule());
|
||||
bind(MachineLinksInjector.class).to(MachineAuthLinksInjector.class);
|
||||
bind(org.eclipse.che.api.agent.server.WsAgentHealthChecker.class)
|
||||
.to(AuthWsAgentHealthChecker.class);
|
||||
bind(MachineTokenPermissionsFilter.class);
|
||||
bind(MachineTokenService.class);
|
||||
bind(MachineTokenRegistry.class);
|
||||
bind(MachineSessionInvalidator.class);
|
||||
bind(RequestTokenExtractor.class).to(HeaderRequestTokenExtractor.class);
|
||||
bind(WorkspaceServiceLinksInjector.class).to(WorkspaceServiceAuthLinksInjector.class);
|
||||
.to(org.eclipse.che.multiuser.machine.authentication.server.AuthWsAgentHealthChecker.class);
|
||||
bind(
|
||||
org.eclipse.che.multiuser.machine.authentication.server.MachineTokenPermissionsFilter
|
||||
.class);
|
||||
bind(org.eclipse.che.multiuser.machine.authentication.server.MachineTokenService.class);
|
||||
bind(org.eclipse.che.multiuser.machine.authentication.server.MachineTokenRegistry.class);
|
||||
bind(org.eclipse.che.multiuser.machine.authentication.server.MachineSessionInvalidator.class);
|
||||
bind(RequestTokenExtractor.class).to(ChainedTokenExtractor.class);
|
||||
bind(WorkspaceServiceLinksInjector.class)
|
||||
.to(
|
||||
org.eclipse.che.multiuser.machine.authentication.server
|
||||
.WorkspaceServiceAuthLinksInjector.class);
|
||||
bind(org.eclipse.che.api.environment.server.MachineInstanceProvider.class)
|
||||
.to(org.eclipse.che.plugin.docker.machine.AuthMachineProviderImpl.class);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import org.eclipse.che.ide.api.oauth.OAuth2Authenticator;
|
|||
import org.eclipse.che.security.oauth.JsOAuthWindow;
|
||||
import org.eclipse.che.security.oauth.OAuthCallback;
|
||||
import org.eclipse.che.security.oauth.OAuthStatus;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
/**
|
||||
* Default implementation of authenticator, used when no provider-specific one is present.
|
||||
|
|
@ -34,13 +35,18 @@ public class DefaultOAuthAuthenticatorImpl implements OAuth2Authenticator, OAuth
|
|||
|
||||
private final DialogFactory dialogFactory;
|
||||
private final CoreLocalizationConstant localizationConstant;
|
||||
private final SecurityTokenProvider provider;
|
||||
private String authenticationUrl;
|
||||
|
||||
@Inject
|
||||
public DefaultOAuthAuthenticatorImpl(
|
||||
DialogFactory dialogFactory, CoreLocalizationConstant localizationConstant) {
|
||||
DialogFactory dialogFactory,
|
||||
CoreLocalizationConstant localizationConstant,
|
||||
SecurityTokenProvider provider) {
|
||||
|
||||
this.dialogFactory = dialogFactory;
|
||||
this.localizationConstant = localizationConstant;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -96,7 +102,7 @@ public class DefaultOAuthAuthenticatorImpl implements OAuth2Authenticator, OAuth
|
|||
|
||||
private void showAuthWindow() {
|
||||
JsOAuthWindow authWindow;
|
||||
authWindow = new JsOAuthWindow(authenticationUrl, "error.url", 500, 980, this);
|
||||
authWindow = new JsOAuthWindow(authenticationUrl, "error.url", 500, 980, this, provider);
|
||||
authWindow.loginWithOAuth();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ import static java.util.Collections.emptySet;
|
|||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.eclipse.che.api.promises.client.Operation;
|
||||
import org.eclipse.che.api.promises.client.OperationException;
|
||||
import org.eclipse.che.ide.util.loging.Log;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
/**
|
||||
* Contain all routines related to a web socket connection initialization
|
||||
|
|
@ -28,17 +31,20 @@ public class WebSocketInitializer {
|
|||
private final WebSocketPropertyManager propertyManager;
|
||||
private final WebSocketActionManager actionManager;
|
||||
private final UrlResolver urlResolver;
|
||||
private SecurityTokenProvider securityTokenProvider;
|
||||
|
||||
@Inject
|
||||
public WebSocketInitializer(
|
||||
WebSocketConnectionManager connectionManager,
|
||||
WebSocketPropertyManager propertyManager,
|
||||
WebSocketActionManager actionManager,
|
||||
UrlResolver urlResolver) {
|
||||
UrlResolver urlResolver,
|
||||
SecurityTokenProvider securityTokenProvider) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.propertyManager = propertyManager;
|
||||
this.actionManager = actionManager;
|
||||
this.urlResolver = urlResolver;
|
||||
this.securityTokenProvider = securityTokenProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,16 +67,27 @@ public class WebSocketInitializer {
|
|||
* @param initActions actions to be performed each time the connection is established
|
||||
*/
|
||||
public void initialize(String endpointId, String url, Set<Runnable> initActions) {
|
||||
Log.debug(getClass(), "Initializing with url: " + url);
|
||||
securityTokenProvider
|
||||
.getSecurityToken()
|
||||
.then(
|
||||
new Operation<String>() {
|
||||
@Override
|
||||
public void apply(String token) throws OperationException {
|
||||
String separator = url.contains("?") ? "&" : "?";
|
||||
final String secureUrl = url + separator + "token=" + token;
|
||||
|
||||
urlResolver.setMapping(endpointId, url);
|
||||
Log.debug(getClass(), "Initializing with secureUrl: " + secureUrl);
|
||||
|
||||
propertyManager.initializeConnection(url);
|
||||
urlResolver.setMapping(endpointId, secureUrl);
|
||||
|
||||
actionManager.setOnEstablishActions(url, initActions);
|
||||
propertyManager.initializeConnection(secureUrl);
|
||||
|
||||
connectionManager.initializeConnection(url);
|
||||
connectionManager.establishConnection(url);
|
||||
actionManager.setOnEstablishActions(secureUrl, initActions);
|
||||
|
||||
connectionManager.initializeConnection(secureUrl);
|
||||
connectionManager.establishConnection(secureUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
package org.eclipse.che.security.oauth;
|
||||
|
||||
import com.google.gwt.user.client.Window;
|
||||
import org.eclipse.che.api.promises.client.Operation;
|
||||
import org.eclipse.che.api.promises.client.OperationException;
|
||||
import org.eclipse.che.api.promises.client.PromiseError;
|
||||
import org.eclipse.che.ide.util.loging.Log;
|
||||
|
||||
/** @author Vladislav Zhukovskii */
|
||||
public class JsOAuthWindow {
|
||||
|
|
@ -19,16 +23,23 @@ public class JsOAuthWindow {
|
|||
private OAuthStatus authStatus;
|
||||
private int popupHeight;
|
||||
private int popupWidth;
|
||||
private SecurityTokenProvider provider;
|
||||
private int clientHeight;
|
||||
private int clientWidth;
|
||||
private OAuthCallback callback;
|
||||
|
||||
public JsOAuthWindow(
|
||||
String authUrl, String errUrl, int popupHeight, int popupWidth, OAuthCallback callback) {
|
||||
String authUrl,
|
||||
String errUrl,
|
||||
int popupHeight,
|
||||
int popupWidth,
|
||||
OAuthCallback callback,
|
||||
SecurityTokenProvider provider) {
|
||||
this.authUrl = authUrl;
|
||||
this.errUrl = errUrl;
|
||||
this.popupHeight = popupHeight;
|
||||
this.popupWidth = popupWidth;
|
||||
this.provider = provider;
|
||||
this.clientHeight = Window.getClientHeight();
|
||||
this.clientWidth = Window.getClientWidth();
|
||||
this.callback = callback;
|
||||
|
|
@ -46,7 +57,25 @@ public class JsOAuthWindow {
|
|||
}
|
||||
|
||||
public void loginWithOAuth() {
|
||||
loginWithOAuth(authUrl, errUrl, popupHeight, popupWidth, clientHeight, clientWidth);
|
||||
provider
|
||||
.getSecurityToken()
|
||||
.then(
|
||||
new Operation<String>() {
|
||||
@Override
|
||||
public void apply(String arg) throws OperationException {
|
||||
if (arg != null) {
|
||||
authUrl = authUrl + "&token=" + arg;
|
||||
}
|
||||
loginWithOAuth(authUrl, errUrl, popupHeight, popupWidth, clientHeight, clientWidth);
|
||||
}
|
||||
})
|
||||
.catchError(
|
||||
new Operation<PromiseError>() {
|
||||
@Override
|
||||
public void apply(PromiseError arg) throws OperationException {
|
||||
Log.error(getClass(), arg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private native void loginWithOAuth(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.security.oauth;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.api.promises.client.PromiseProvider;
|
||||
|
||||
/** Provider of tokens that needed to authenticate requests. */
|
||||
@Singleton
|
||||
public class SecurityTokenProvider {
|
||||
|
||||
@Inject PromiseProvider promiseProvider;
|
||||
|
||||
public Promise<String> getSecurityToken() {
|
||||
return promiseProvider.resolve(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
<inherits name="org.eclipse.che.ide.useragents"/>
|
||||
<inherits name="elemental.Elemental"/>
|
||||
<inherits name="com.google.gwt.json.JSON"/>
|
||||
<inherits name="org.eclipse.che.api.promises.Promises"/>
|
||||
|
||||
<source path=""/>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,16 @@ package org.eclipse.che.ide.websocket.impl;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.eclipse.che.api.promises.client.Operation;
|
||||
import org.eclipse.che.api.promises.client.OperationException;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
|
@ -32,40 +38,53 @@ public class WebSocketInitializerTest {
|
|||
@Mock private WebSocketPropertyManager propertyManager;
|
||||
@Mock private UrlResolver urlResolver;
|
||||
@Mock private WebSocketActionManager webSocketActionManager;
|
||||
@Mock private SecurityTokenProvider securityTokenProvider;
|
||||
@Mock private Promise promise;
|
||||
@Captor private ArgumentCaptor<Operation<String>> operation;
|
||||
@InjectMocks private WebSocketInitializer initializer;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {}
|
||||
public void setUp() throws Exception {
|
||||
when(securityTokenProvider.getSecurityToken()).thenReturn(promise);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {}
|
||||
|
||||
@Test
|
||||
public void shouldSetUrlMappingOnInitialize() {
|
||||
initializer.initialize("id", "url");
|
||||
public void shouldSetUrlMappingOnInitialize() throws OperationException {
|
||||
|
||||
verify(urlResolver).setMapping("id", "url");
|
||||
initializer.initialize("id", "http://test.com");
|
||||
verify(promise).then(operation.capture());
|
||||
operation.getValue().apply("token");
|
||||
verify(securityTokenProvider).getSecurityToken();
|
||||
verify(urlResolver).setMapping("id", "http://test.com?token=token");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRunConnectionManagerInitializeConnectionOnInitialize() {
|
||||
initializer.initialize("id", "url");
|
||||
|
||||
verify(connectionManager).initializeConnection("url");
|
||||
public void shouldRunConnectionManagerInitializeConnectionOnInitialize()
|
||||
throws OperationException {
|
||||
initializer.initialize("id", "http://test.com");
|
||||
verify(promise).then(operation.capture());
|
||||
operation.getValue().apply("token");
|
||||
verify(securityTokenProvider).getSecurityToken();
|
||||
verify(connectionManager).initializeConnection("http://test.com?token=token");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRunPropertyManagerInitializeConnectionOnInitialize() {
|
||||
public void shouldRunPropertyManagerInitializeConnectionOnInitialize() throws OperationException {
|
||||
initializer.initialize("id", "url");
|
||||
|
||||
verify(propertyManager).initializeConnection("url");
|
||||
verify(promise).then(operation.capture());
|
||||
operation.getValue().apply("token");
|
||||
verify(propertyManager).initializeConnection("url?token=token");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRunEstablishConnectionOnInitialize() {
|
||||
public void shouldRunEstablishConnectionOnInitialize() throws OperationException {
|
||||
initializer.initialize("id", "url");
|
||||
|
||||
verify(connectionManager).establishConnection("url");
|
||||
verify(promise).then(operation.capture());
|
||||
operation.getValue().apply("token");
|
||||
verify(connectionManager).establishConnection("url?token=token");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@
|
|||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-gwt</artifactId>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public final class Keycloak extends JavaScriptObject {
|
|||
console.log('[Keycloak] Failed to initialize Keycloak');
|
||||
reject();
|
||||
});
|
||||
console.log('[Keycloak] Initializing complete');
|
||||
} catch (ex) {
|
||||
console.log('[Keycloak] Failed to initialize Keycloak with exception: ', ex);
|
||||
reject();
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@ import org.eclipse.che.ide.util.loging.Log;
|
|||
/** KeycloakAsyncRequests */
|
||||
public class KeycloakAsyncRequest extends AsyncRequest {
|
||||
|
||||
private Promise<Keycloak> keycloakPromise;
|
||||
private KeycloakProvider keycloakProvider;
|
||||
|
||||
public KeycloakAsyncRequest(
|
||||
Promise<Keycloak> keycloakPromise, RequestBuilder.Method method, String url, boolean async) {
|
||||
KeycloakProvider keycloakProvider, RequestBuilder.Method method, String url, boolean async) {
|
||||
super(method, url, async);
|
||||
this.keycloakPromise = keycloakPromise;
|
||||
this.keycloakProvider = keycloakProvider;
|
||||
}
|
||||
|
||||
private void addAuthorizationHeader(Keycloak keycloak) {
|
||||
header(HTTPHeader.AUTHORIZATION, "Bearer " + keycloak.getToken());
|
||||
private void addAuthorizationHeader(String keycloakToken) {
|
||||
header(HTTPHeader.AUTHORIZATION, "Bearer " + keycloakToken);
|
||||
}
|
||||
|
||||
private static interface Sender<R> {
|
||||
|
|
@ -39,47 +39,21 @@ public class KeycloakAsyncRequest extends AsyncRequest {
|
|||
}
|
||||
|
||||
private <R> Promise<R> doAfterKeycloakInitAndUpdate(Sender<R> sender) {
|
||||
return keycloakPromise.thenPromise(
|
||||
new Function<Keycloak, Promise<R>>() {
|
||||
@Override
|
||||
public Promise<R> apply(Keycloak keycloak) {
|
||||
Log.debug(getClass(), "Keycloak initialized with token: ", keycloak.getToken());
|
||||
try {
|
||||
return keycloak
|
||||
.updateToken(5)
|
||||
.thenPromise(
|
||||
new Function<Boolean, Promise<R>>() {
|
||||
@Override
|
||||
public Promise<R> apply(Boolean refreshed) {
|
||||
if (refreshed) {
|
||||
Log.debug(
|
||||
getClass(),
|
||||
"Keycloak updated token before sending the request `",
|
||||
KeycloakAsyncRequest.this.requestBuilder.getUrl(),
|
||||
"`. New token is : ",
|
||||
keycloak.getToken());
|
||||
} else {
|
||||
Log.debug(
|
||||
getClass(),
|
||||
"Keycloak didn't need to update token before sending the request `",
|
||||
KeycloakAsyncRequest.this.requestBuilder.getUrl(),
|
||||
"`");
|
||||
}
|
||||
addAuthorizationHeader(keycloak);
|
||||
try {
|
||||
return sender.doSend();
|
||||
} catch (Throwable t) {
|
||||
Log.error(getClass(), t);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
Log.error(getClass(), t);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
});
|
||||
return keycloakProvider
|
||||
.getUpdatedToken(5)
|
||||
.thenPromise(
|
||||
new Function<String, Promise<R>>() {
|
||||
@Override
|
||||
public Promise<R> apply(String keycloakToken) {
|
||||
addAuthorizationHeader(keycloakToken);
|
||||
try {
|
||||
return sender.doSend();
|
||||
} catch (Throwable t) {
|
||||
Log.error(getClass(), t);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -10,30 +10,18 @@
|
|||
*/
|
||||
package org.eclipse.che.multiuser.keycloak.ide;
|
||||
|
||||
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.REALM_SETTING;
|
||||
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
import com.google.gwt.core.client.ScriptInjector;
|
||||
import com.google.gwt.http.client.RequestBuilder;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.web.bindery.event.shared.EventBus;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.api.promises.client.callback.CallbackPromiseHelper;
|
||||
import org.eclipse.che.ide.MimeType;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.dto.DtoFactory;
|
||||
import org.eclipse.che.ide.json.JsonHelper;
|
||||
import org.eclipse.che.ide.rest.AsyncRequest;
|
||||
import org.eclipse.che.ide.rest.HTTPHeader;
|
||||
import org.eclipse.che.ide.util.loging.Log;
|
||||
import org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants;
|
||||
import org.eclipse.che.multiuser.machine.authentication.ide.MachineAsyncRequestFactory;
|
||||
import org.eclipse.che.multiuser.machine.authentication.ide.MachineTokenServiceClient;
|
||||
|
||||
|
|
@ -42,50 +30,18 @@ import org.eclipse.che.multiuser.machine.authentication.ide.MachineTokenServiceC
|
|||
public class KeycloakAsyncRequestFactory extends MachineAsyncRequestFactory {
|
||||
|
||||
private final DtoFactory dtoFactory;
|
||||
private Promise<Keycloak> keycloak;
|
||||
private KeycloakProvider keycloakProvider;
|
||||
|
||||
@Inject
|
||||
public KeycloakAsyncRequestFactory(
|
||||
KeycloakProvider keycloakProvider,
|
||||
DtoFactory dtoFactory,
|
||||
Provider<MachineTokenServiceClient> machineTokenServiceProvider,
|
||||
AppContext appContext,
|
||||
EventBus eventBus) {
|
||||
super(dtoFactory, machineTokenServiceProvider, appContext, eventBus);
|
||||
this.dtoFactory = dtoFactory;
|
||||
String keycloakSettings =
|
||||
getKeycloakSettings(KeycloakConstants.getEndpoint(appContext.getMasterEndpoint()));
|
||||
Map<String, String> settings = JsonHelper.toMap(keycloakSettings);
|
||||
Log.info(getClass(), "Keycloak settings: ", settings);
|
||||
|
||||
keycloak =
|
||||
CallbackPromiseHelper.createFromCallback(
|
||||
new CallbackPromiseHelper.Call<Void, Throwable>() {
|
||||
@Override
|
||||
public void makeCall(final Callback<Void, Throwable> callback) {
|
||||
ScriptInjector.fromUrl(
|
||||
settings.get(AUTH_SERVER_URL_SETTING) + "/js/keycloak.js")
|
||||
.setCallback(
|
||||
new Callback<Void, Exception>() {
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
callback.onSuccess(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception reason) {
|
||||
callback.onFailure(reason);
|
||||
}
|
||||
})
|
||||
.setWindow(getWindow())
|
||||
.inject();
|
||||
}
|
||||
})
|
||||
.thenPromise(
|
||||
(v) ->
|
||||
Keycloak.init(
|
||||
settings.get(AUTH_SERVER_URL_SETTING),
|
||||
settings.get(REALM_SETTING),
|
||||
settings.get(CLIENT_ID_SETTING)));
|
||||
this.keycloakProvider = keycloakProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -93,7 +49,7 @@ public class KeycloakAsyncRequestFactory extends MachineAsyncRequestFactory {
|
|||
RequestBuilder.Method method, String url, Object dtoBody, boolean async) {
|
||||
AsyncRequest request = super.doCreateRequest(method, url, dtoBody, async);
|
||||
if (!isWsAgentRequest(url)) {
|
||||
AsyncRequest asyncRequest = new KeycloakAsyncRequest(keycloak, method, url, async);
|
||||
AsyncRequest asyncRequest = new KeycloakAsyncRequest(keycloakProvider, method, url, async);
|
||||
if (dtoBody != null) {
|
||||
if (dtoBody instanceof List<?>) {
|
||||
asyncRequest.data(dtoFactory.toJson((List<?>) dtoBody));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.multiuser.keycloak.ide;
|
||||
|
||||
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.REALM_SETTING;
|
||||
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
import com.google.gwt.core.client.ScriptInjector;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.promises.client.Function;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.api.promises.client.PromiseProvider;
|
||||
import org.eclipse.che.api.promises.client.callback.CallbackPromiseHelper;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.json.JsonHelper;
|
||||
import org.eclipse.che.ide.util.loging.Log;
|
||||
import org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants;
|
||||
|
||||
/** KeycloakProvider */
|
||||
@Singleton
|
||||
public class KeycloakProvider {
|
||||
private AppContext appContext;
|
||||
private boolean keycloakDisabled = false;
|
||||
private Promise<Keycloak> keycloak;
|
||||
|
||||
@Inject
|
||||
public KeycloakProvider(AppContext appContext, PromiseProvider promiseProvider) {
|
||||
this.appContext = appContext;
|
||||
String keycloakSettings =
|
||||
getKeycloakSettings(KeycloakConstants.getEndpoint(appContext.getMasterEndpoint()));
|
||||
Map<String, String> settings = JsonHelper.toMap(keycloakSettings);
|
||||
Log.info(getClass(), "Keycloak settings: ", settings);
|
||||
|
||||
keycloak =
|
||||
CallbackPromiseHelper.createFromCallback(
|
||||
new CallbackPromiseHelper.Call<Void, Throwable>() {
|
||||
@Override
|
||||
public void makeCall(final Callback<Void, Throwable> callback) {
|
||||
ScriptInjector.fromUrl(
|
||||
settings.get(AUTH_SERVER_URL_SETTING) + "/js/keycloak.js")
|
||||
.setCallback(
|
||||
new Callback<Void, Exception>() {
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
callback.onSuccess(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception reason) {
|
||||
callback.onFailure(reason);
|
||||
}
|
||||
})
|
||||
.setWindow(getWindow())
|
||||
.inject();
|
||||
}
|
||||
})
|
||||
.thenPromise(
|
||||
(v) ->
|
||||
Keycloak.init(
|
||||
settings.get(AUTH_SERVER_URL_SETTING),
|
||||
settings.get(REALM_SETTING),
|
||||
settings.get(CLIENT_ID_SETTING)));
|
||||
Log.info(getClass(), "Keycloak init complete: ", this);
|
||||
}
|
||||
|
||||
public static native String getKeycloakSettings(String keycloakSettingsEndpoint) /*-{
|
||||
var myReq = new XMLHttpRequest();
|
||||
myReq.open('GET', '' + keycloakSettingsEndpoint, false);
|
||||
myReq.send(null);
|
||||
return myReq.responseText;
|
||||
}-*/;
|
||||
|
||||
public static native JavaScriptObject getWindow() /*-{
|
||||
return $wnd;
|
||||
}-*/;
|
||||
|
||||
public Promise<Keycloak> getKeycloak() {
|
||||
return keycloak;
|
||||
}
|
||||
|
||||
public Promise<String> getUpdatedToken(int minValidity) {
|
||||
return keycloak.thenPromise(
|
||||
new Function<Keycloak, Promise<String>>() {
|
||||
@Override
|
||||
public Promise<String> apply(Keycloak keycloak) {
|
||||
Log.debug(getClass(), "Keycloak initialized with token: ", keycloak.getToken());
|
||||
try {
|
||||
return keycloak
|
||||
.updateToken(minValidity)
|
||||
.then(
|
||||
new Function<Boolean, String>() {
|
||||
@Override
|
||||
public String apply(Boolean refreshed) {
|
||||
if (refreshed) {
|
||||
Log.debug(
|
||||
getClass(),
|
||||
"Keycloak updated token. New token is : ",
|
||||
keycloak.getToken());
|
||||
} else {
|
||||
Log.debug(getClass(), "Keycloak didn't need to update token.");
|
||||
}
|
||||
return keycloak.getToken();
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
Log.error(getClass(), t);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.multiuser.keycloak.ide;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
public class KeycloakSecurityTokenProvider extends SecurityTokenProvider {
|
||||
@Inject KeycloakProvider keycloakProvider;
|
||||
|
||||
@Override
|
||||
public Promise<String> getSecurityToken() {
|
||||
return keycloakProvider.getUpdatedToken(5);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,9 @@ package org.eclipse.che.multiuser.keycloak.ide.inject;
|
|||
import com.google.gwt.inject.client.AbstractGinModule;
|
||||
import org.eclipse.che.ide.api.extension.ExtensionGinModule;
|
||||
import org.eclipse.che.ide.rest.AsyncRequestFactory;
|
||||
import org.eclipse.che.multiuser.keycloak.ide.KeycloakProvider;
|
||||
import org.eclipse.che.multiuser.keycloak.ide.KeycloakSecurityTokenProvider;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
/** KeycloakAuthGinModule */
|
||||
@ExtensionGinModule
|
||||
|
|
@ -20,7 +23,9 @@ public class KeycloakAuthGinModule extends AbstractGinModule {
|
|||
|
||||
@Override
|
||||
public void configure() {
|
||||
bind(KeycloakProvider.class).asEagerSingleton();
|
||||
bind(AsyncRequestFactory.class)
|
||||
.to(org.eclipse.che.multiuser.keycloak.ide.KeycloakAsyncRequestFactory.class);
|
||||
bind(SecurityTokenProvider.class).to(KeycloakSecurityTokenProvider.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ public class KeycloakServletModule extends ServletModule {
|
|||
|
||||
// Not contains '/websocket', /docs/ (for swagger) and not ends with '/ws' or '/eventbus' or '/settings/' or '/api/system/state' or '/api/stack/[^/]+/icon/'
|
||||
filterRegex(
|
||||
"^(?!.*(/websocket/?|/docs/))(?!.*(/ws/?|/eventbus/?|/settings/?|/api/system/state/?|/api/stack/[^/]+/icon/?)$).*")
|
||||
"^(?!.*(/websocket/?|/docs/))(?!.*(/ws/?|/eventbus/?|/settings/?|/api/oauth/callback/?|/api/system/state/?|/api/stack/[^/]+/icon/?)$).*")
|
||||
.through(KeycloakAuthenticationFilter.class);
|
||||
filterRegex(
|
||||
"^(?!.*(/websocket/?|/docs/))(?!.*(/ws/?|/eventbus/?|/settings/?|/api/system/state/?|/api/stack/[^/]+/icon/?)$).*")
|
||||
"^(?!.*(/websocket/?|/docs/))(?!.*(/ws/?|/eventbus/?|/settings/?|/api/oauth/callback/?|/api/system/state/?|/api/stack/[^/]+/icon/?)$).*")
|
||||
.through(KeycloakEnvironmentInitalizationFilter.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.eclipse.che.plugin.ssh.key.client.SshKeyUploader;
|
|||
import org.eclipse.che.security.oauth.JsOAuthWindow;
|
||||
import org.eclipse.che.security.oauth.OAuthCallback;
|
||||
import org.eclipse.che.security.oauth.OAuthStatus;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
/**
|
||||
* Uploads SSH keys for github.com.
|
||||
|
|
@ -45,6 +46,7 @@ public class GitHubSshKeyUploader implements SshKeyUploader, OAuthCallback {
|
|||
private final ProductInfoDataProvider productInfoDataProvider;
|
||||
private final DialogFactory dialogFactory;
|
||||
private final AppContext appContext;
|
||||
private final SecurityTokenProvider securityTokenProvider;
|
||||
|
||||
private AsyncCallback<Void> callback;
|
||||
private String userId;
|
||||
|
|
@ -56,7 +58,8 @@ public class GitHubSshKeyUploader implements SshKeyUploader, OAuthCallback {
|
|||
NotificationManager notificationManager,
|
||||
ProductInfoDataProvider productInfoDataProvider,
|
||||
DialogFactory dialogFactory,
|
||||
AppContext appContext) {
|
||||
AppContext appContext,
|
||||
SecurityTokenProvider securityTokenProvider) {
|
||||
this.gitHubService = gitHubService;
|
||||
this.baseUrl = appContext.getMasterEndpoint();
|
||||
this.constant = constant;
|
||||
|
|
@ -64,6 +67,7 @@ public class GitHubSshKeyUploader implements SshKeyUploader, OAuthCallback {
|
|||
this.productInfoDataProvider = productInfoDataProvider;
|
||||
this.dialogFactory = dialogFactory;
|
||||
this.appContext = appContext;
|
||||
this.securityTokenProvider = securityTokenProvider;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
|
@ -124,7 +128,8 @@ public class GitHubSshKeyUploader implements SshKeyUploader, OAuthCallback {
|
|||
+ Window.Location.getHost()
|
||||
+ "/ws/"
|
||||
+ appContext.getWorkspace().getConfig().getName();
|
||||
JsOAuthWindow authWindow = new JsOAuthWindow(authUrl, "error.url", 500, 980, this);
|
||||
JsOAuthWindow authWindow =
|
||||
new JsOAuthWindow(authUrl, "error.url", 500, 980, this, securityTokenProvider);
|
||||
authWindow.loginWithOAuth();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import org.eclipse.che.plugin.ssh.key.client.manage.SshKeyManagerPresenter;
|
|||
import org.eclipse.che.security.oauth.JsOAuthWindow;
|
||||
import org.eclipse.che.security.oauth.OAuthCallback;
|
||||
import org.eclipse.che.security.oauth.OAuthStatus;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
/** @author Roman Nikitenko */
|
||||
public class GitHubAuthenticatorImpl
|
||||
|
|
@ -55,6 +56,7 @@ public class GitHubAuthenticatorImpl
|
|||
private final GitHubLocalizationConstant locale;
|
||||
private final String baseUrl;
|
||||
private final AppContext appContext;
|
||||
private final SecurityTokenProvider securityTokenPærovider;
|
||||
private String authenticationUrl;
|
||||
|
||||
@Inject
|
||||
|
|
@ -65,10 +67,12 @@ public class GitHubAuthenticatorImpl
|
|||
DialogFactory dialogFactory,
|
||||
GitHubLocalizationConstant locale,
|
||||
NotificationManager notificationManager,
|
||||
AppContext appContext) {
|
||||
AppContext appContext,
|
||||
SecurityTokenProvider securityTokenPærovider) {
|
||||
this.registry = registry;
|
||||
this.sshServiceClient = sshServiceClient;
|
||||
this.view = view;
|
||||
this.securityTokenPærovider = securityTokenPærovider;
|
||||
this.view.setDelegate(this);
|
||||
this.locale = locale;
|
||||
this.baseUrl = appContext.getMasterEndpoint();
|
||||
|
|
@ -125,9 +129,11 @@ public class GitHubAuthenticatorImpl
|
|||
private void showAuthWindow() {
|
||||
JsOAuthWindow authWindow;
|
||||
if (authenticationUrl == null) {
|
||||
authWindow = new JsOAuthWindow(getAuthUrl(), "error.url", 500, 980, this);
|
||||
authWindow =
|
||||
new JsOAuthWindow(getAuthUrl(), "error.url", 500, 980, this, securityTokenPærovider);
|
||||
} else {
|
||||
authWindow = new JsOAuthWindow(authenticationUrl, "error.url", 500, 980, this);
|
||||
authWindow =
|
||||
new JsOAuthWindow(authenticationUrl, "error.url", 500, 980, this, securityTokenPærovider);
|
||||
}
|
||||
authWindow.loginWithOAuth();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import org.eclipse.che.plugin.pullrequest.client.vcs.hosting.VcsHostingService;
|
|||
import org.eclipse.che.plugin.pullrequest.shared.dto.HostUser;
|
||||
import org.eclipse.che.plugin.pullrequest.shared.dto.PullRequest;
|
||||
import org.eclipse.che.plugin.pullrequest.shared.dto.Repository;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
/**
|
||||
* {@link VcsHostingService} implementation for GitHub.
|
||||
|
|
@ -74,6 +75,7 @@ public class GitHubHostingService implements VcsHostingService {
|
|||
private final GitHubClientService gitHubClientService;
|
||||
private final HostingServiceTemplates templates;
|
||||
private final String baseUrl;
|
||||
private final SecurityTokenProvider securityTokenProvider;
|
||||
|
||||
@Inject
|
||||
public GitHubHostingService(
|
||||
|
|
@ -81,12 +83,14 @@ public class GitHubHostingService implements VcsHostingService {
|
|||
@NotNull final AppContext appContext,
|
||||
@NotNull final DtoFactory dtoFactory,
|
||||
@NotNull final GitHubClientService gitHubClientService,
|
||||
@NotNull final GitHubTemplates templates) {
|
||||
@NotNull final GitHubTemplates templates,
|
||||
SecurityTokenProvider securityTokenProvider) {
|
||||
this.appContext = appContext;
|
||||
this.dtoFactory = dtoFactory;
|
||||
this.gitHubClientService = gitHubClientService;
|
||||
this.templates = templates;
|
||||
this.baseUrl = baseUrl;
|
||||
this.securityTokenProvider = securityTokenProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -512,7 +516,7 @@ public class GitHubHostingService implements VcsHostingService {
|
|||
+ Window.Location.getHost()
|
||||
+ "/ws/"
|
||||
+ workspace.getConfig().getName();
|
||||
return ServiceUtil.performWindowAuth(this, authUrl);
|
||||
return ServiceUtil.performWindowAuth(this, authUrl, securityTokenProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import org.eclipse.che.plugin.pullrequest.shared.dto.HostUser;
|
|||
import org.eclipse.che.security.oauth.JsOAuthWindow;
|
||||
import org.eclipse.che.security.oauth.OAuthCallback;
|
||||
import org.eclipse.che.security.oauth.OAuthStatus;
|
||||
import org.eclipse.che.security.oauth.SecurityTokenProvider;
|
||||
|
||||
/**
|
||||
* Utils for {@link VcsHostingService} implementations.
|
||||
|
|
@ -38,7 +39,9 @@ public final class ServiceUtil {
|
|||
* @return the promise which resolves authorized user or rejects with an error
|
||||
*/
|
||||
public static Promise<HostUser> performWindowAuth(
|
||||
final VcsHostingService service, final String authUrl) {
|
||||
final VcsHostingService service,
|
||||
final String authUrl,
|
||||
final SecurityTokenProvider securityTokenProvider) {
|
||||
final Executor.ExecutorBody<HostUser> exBody =
|
||||
new Executor.ExecutorBody<HostUser>() {
|
||||
@Override
|
||||
|
|
@ -69,7 +72,8 @@ public final class ServiceUtil {
|
|||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
securityTokenProvider)
|
||||
.loginWithOAuth();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue