diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/MachineModule.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/MachineModule.java index 4801fb6061b..aa6795173cb 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/MachineModule.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/MachineModule.java @@ -124,7 +124,7 @@ Pair[] propagateEventsProvider(@Named("event.bus.url") String ev @Named("codenvy.local.infrastructure.users") Set users() { final Set users = new HashSet<>(1); - final User user = new User().withId("codenvy") + final User user = new User().withId("che") .withName("codenvy") .withEmail("che@eclipse.org") .withPassword("secret"); diff --git a/core/che-core-git-impl-native/pom.xml b/core/che-core-git-impl-native/pom.xml index 3534dfadcd6..584fe9aa11c 100644 --- a/core/che-core-git-impl-native/pom.xml +++ b/core/che-core-git-impl-native/pom.xml @@ -55,6 +55,10 @@ javax.inject javax.inject + + javax.validation + validation-api + javax.ws.rs javax.ws.rs-api diff --git a/core/che-core-git-impl-native/src/main/java/org/eclipse/che/git/impl/nativegit/NativeGitConnection.java b/core/che-core-git-impl-native/src/main/java/org/eclipse/che/git/impl/nativegit/NativeGitConnection.java index fe31404b5bc..04ae78badf6 100644 --- a/core/che-core-git-impl-native/src/main/java/org/eclipse/che/git/impl/nativegit/NativeGitConnection.java +++ b/core/che-core-git-impl-native/src/main/java/org/eclipse/che/git/impl/nativegit/NativeGitConnection.java @@ -11,8 +11,10 @@ package org.eclipse.che.git.impl.nativegit; +import com.google.common.collect.ImmutableMap; + +import org.eclipse.che.api.core.ErrorCodes; import org.eclipse.che.api.core.UnauthorizedException; -import org.eclipse.che.api.core.rest.shared.dto.ExtendedError; import org.eclipse.che.api.core.util.LineConsumerFactory; import org.eclipse.che.api.git.Config; import org.eclipse.che.api.git.CredentialsLoader; @@ -21,6 +23,7 @@ import org.eclipse.che.api.git.GitException; import org.eclipse.che.api.git.GitUserResolver; import org.eclipse.che.api.git.LogPage; +import org.eclipse.che.api.git.shared.ProviderInfo; import org.eclipse.che.api.git.UserCredential; import org.eclipse.che.api.git.shared.AddRequest; import org.eclipse.che.api.git.shared.Branch; @@ -90,6 +93,9 @@ import java.util.List; import java.util.regex.Pattern; +import static org.eclipse.che.api.git.shared.ProviderInfo.AUTHENTICATE_URL; +import static org.eclipse.che.api.git.shared.ProviderInfo.PROVIDER_NAME; + /** * Native implementation of GitConnection * @@ -575,7 +581,14 @@ private void executeRemoteCommand(RemoteOperationCommand command) throws GitE if (!isOperationNeedAuth(gitEx.getMessage())) { throw gitEx; } - throw new UnauthorizedException(gitEx.getMessage()); + ProviderInfo info = credentialsLoader.getProviderInfo(command.getRemoteUri()); + if (info != null) { + throw new UnauthorizedException(gitEx.getMessage(), + ErrorCodes.UNAUTHORIZED_GIT_OPERATION, + ImmutableMap.of(PROVIDER_NAME, info.getProviderName(), + AUTHENTICATE_URL, info.getAuthenticateUrl())); + } + throw new UnauthorizedException(gitEx.getMessage(), ErrorCodes.UNAUTHORIZED_GIT_OPERATION); } } diff --git a/core/commons/che-core-commons-gwt/src/main/java/org/eclipse/che/ide/util/ExceptionUtils.java b/core/commons/che-core-commons-gwt/src/main/java/org/eclipse/che/ide/util/ExceptionUtils.java index 0ede46cdfbf..73433c9a396 100644 --- a/core/commons/che-core-commons-gwt/src/main/java/org/eclipse/che/ide/util/ExceptionUtils.java +++ b/core/commons/che-core-commons-gwt/src/main/java/org/eclipse/che/ide/util/ExceptionUtils.java @@ -14,8 +14,17 @@ package org.eclipse.che.ide.util; +import org.eclipse.che.ide.commons.exception.ServerException; +import org.eclipse.che.ide.dto.DtoFactory; + +import java.util.Collections; +import java.util.Map; + /** Utility class for common Exception related operations. */ public class ExceptionUtils { + + private static DtoFactory dtoFactory = new DtoFactory(); + public static final int MAX_CAUSE = 10; public static String getStackTraceAsString(Throwable e) { @@ -59,4 +68,38 @@ public static String getThrowableAsString(Throwable e, String newline, String in return s.toString(); } + + /** + * Returns error code of the exception if it is of type {@clink ServerException} and has error code set, or -1 otherwise. + * + * @param exception + * passed exception + * @return error code + */ + public static int getErrorCode(Throwable exception) { + if (exception instanceof ServerException) { + return ((ServerException)exception).getErrorCode(); + } else if (exception instanceof org.eclipse.che.ide.websocket.rest.exceptions.ServerException) { + return ((org.eclipse.che.ide.websocket.rest.exceptions.ServerException)exception).getErrorCode(); + } else { + return -1; + } + } + + /** + * Returns attributes of the exception if it is of type {@clink ServerException} and has attributes set, or empty map otherwise. + * + * @param exception + * passed exception + * @return error code + */ + public static Map getAttributes(Throwable exception) { + if (exception instanceof ServerException) { + return ((ServerException)exception).getAttributes(); + } else if (exception instanceof org.eclipse.che.ide.websocket.rest.exceptions.ServerException) { + return ((org.eclipse.che.ide.websocket.rest.exceptions.ServerException)exception).getAttributes(); + } else { + return Collections.emptyMap(); + } + } } diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticator.java b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2Authenticator.java similarity index 77% rename from plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticator.java rename to core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2Authenticator.java index b343df911d7..9b7420c7381 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticator.java +++ b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2Authenticator.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.github.client.authenticator; +package org.eclipse.che.ide.api.oauth; import com.google.gwt.user.client.rpc.AsyncCallback; @@ -17,6 +17,9 @@ /** * @author Roman Nikitenko */ -public interface GitHubAuthenticator { - void authorize(AsyncCallback callback); +public interface OAuth2Authenticator { + + void authorize(String authenticatorUrl, AsyncCallback callback); + + String getProviderName(); } diff --git a/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2AuthenticatorRegistry.java b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2AuthenticatorRegistry.java new file mode 100644 index 00000000000..2faec2719f6 --- /dev/null +++ b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2AuthenticatorRegistry.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.oauth; + +/** + * Authenticators registry. + * + * @author Vitalii Parfonov + */ +public interface OAuth2AuthenticatorRegistry { + + void registerAuthenticator(String providerName, OAuth2Authenticator oAuth2Authenticator); + + OAuth2Authenticator getAuthenticator(String providerName); +} diff --git a/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2AuthenticatorUrlProvider.java b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2AuthenticatorUrlProvider.java new file mode 100644 index 00000000000..c655264a4f4 --- /dev/null +++ b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/oauth/OAuth2AuthenticatorUrlProvider.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.oauth; + +import com.google.common.base.Joiner; +import com.google.gwt.user.client.Window; + +import java.util.List; + + +/** + * Constructs URL's to OAUth authentication depending on current host and rest context. + * + * @author Vitalii Parfonov + */ +public class OAuth2AuthenticatorUrlProvider { + + private final static String oAuthServicePath = "/oauth/authenticate"; + + public static String get(String restContext, String authenticatePath) { + return restContext + authenticatePath + "&redirect_after_login=" + redirect(); + } + + + public static String get(String restContext, String providerName, String userId, List scopes) { + final String scope = Joiner.on(',').join(scopes); + + return restContext + oAuthServicePath + "?oauth_provider=" + providerName + + "&scope=" + scope + + "&userId=" + userId + + "&redirect_after_login=" + redirect(); + } + + private static String redirect() { + return Window.Location.getProtocol() + "//" + Window.Location.getHost() + "/ws/"; + } + + + +} diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/CoreLocalizationConstant.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/CoreLocalizationConstant.java index 5fa1f6cd137..75bc90b6c0b 100644 --- a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/CoreLocalizationConstant.java +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/CoreLocalizationConstant.java @@ -292,6 +292,19 @@ public interface CoreLocalizationConstant extends Messages { @Key("import.project.error") String importProjectError(); + /* Authorization */ + @Key("authorization.dialog.title") + String authorizationDialogTitle(); + + @Key("authorization.dialog.text") + String authorizationDialogText(); + + @Key("oauth.failed.to.get.authenticator.title") + String oauthFailedToGetAuthenticatorTitle(); + + @Key("oauth.failed.to.get.authenticator.text") + String oauthFailedToGetAuthenticatorText(); + /* Actions */ @Key("action.newFolder.title") String actionNewFolderTitle(); diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java index b46ec4b8588..130b10d2cb8 100644 --- a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java @@ -68,6 +68,8 @@ import org.eclipse.che.ide.api.icon.IconRegistry; import org.eclipse.che.ide.api.keybinding.KeyBindingAgent; import org.eclipse.che.ide.api.notification.NotificationManager; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorRegistry; import org.eclipse.che.ide.api.parts.EditorPartStack; import org.eclipse.che.ide.api.parts.PartStack; import org.eclipse.che.ide.api.parts.PartStackUIResources; @@ -96,6 +98,7 @@ import org.eclipse.che.ide.api.theme.ThemeAgent; import org.eclipse.che.ide.client.StartUpActionsProcessor; import org.eclipse.che.ide.icon.DefaultIconsComponent; +import org.eclipse.che.ide.oauth.DefaultOAuthAuthenticatorImpl; import org.eclipse.che.ide.preferences.PreferencesComponent; import org.eclipse.che.ide.projecttype.ProjectTemplatesComponent; import org.eclipse.che.ide.projecttype.ProjectTypeComponent; @@ -120,6 +123,7 @@ import org.eclipse.che.ide.notification.NotificationManagerImpl; import org.eclipse.che.ide.notification.NotificationManagerView; import org.eclipse.che.ide.notification.NotificationManagerViewImpl; +import org.eclipse.che.ide.oauth.OAuth2AuthenticatorRegistryImpl; import org.eclipse.che.ide.part.FocusManager; import org.eclipse.che.ide.part.PartStackPresenter; import org.eclipse.che.ide.part.PartStackPresenter.PartStackEventHandler; @@ -262,9 +266,12 @@ protected void configure() { bind(ThemeAgent.class).to(ThemeAgentImpl.class).in(Singleton.class); bind(FileTypeRegistry.class).to(FileTypeRegistryImpl.class).in(Singleton.class); + bind(AnalyticsEventLogger.class).to(DummyAnalyticsLoger.class).in(Singleton.class); bind(AnalyticsEventLoggerExt.class).to(DummyAnalyticsLoger.class).in(Singleton.class); + GinMultibinder.newSetBinder(binder(), OAuth2Authenticator.class).addBinding().to(DefaultOAuthAuthenticatorImpl.class); + configureComponents(); configureProjectWizard(); configureImportWizard(); @@ -307,6 +314,7 @@ private void configureProjectWizard() { bind(ProjectWizardRegistry.class).to(ProjectWizardRegistryImpl.class).in(Singleton.class); install(new GinFactoryModuleBuilder().build(ProjectWizardFactory.class)); bind(PreSelectedProjectTypeManager.class).to(PreSelectedProjectTypeManagerImpl.class).in(Singleton.class); + bind(OAuth2AuthenticatorRegistry.class).to(OAuth2AuthenticatorRegistryImpl.class).in(Singleton.class); } private void configureImportWizard() { diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/StandardComponentInitializer.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/StandardComponentInitializer.java index 702b3185c13..a1605cd452a 100644 --- a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/StandardComponentInitializer.java +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/StandardComponentInitializer.java @@ -326,9 +326,12 @@ public interface ParserResource extends ClientBundle { @Inject private WsConnectionListener wsConnectionListener; + + /** Instantiates {@link StandardComponentInitializer} an creates standard content. */ @Inject - public StandardComponentInitializer(IconRegistry iconRegistry, StandardComponentInitializer.ParserResource parserResource) { + public StandardComponentInitializer(IconRegistry iconRegistry, + StandardComponentInitializer.ParserResource parserResource) { iconRegistry.registerIcon(new Icon(BLANK_CATEGORY + ".samples.category.icon", parserResource.samplesCategoryBlank())); } diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/oauth/DefaultOAuthAuthenticatorImpl.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/oauth/DefaultOAuthAuthenticatorImpl.java new file mode 100644 index 00000000000..b89315ae2a5 --- /dev/null +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/oauth/DefaultOAuthAuthenticatorImpl.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.oauth; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.inject.Inject; + +import org.eclipse.che.ide.CoreLocalizationConstant; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; +import org.eclipse.che.ide.ui.dialogs.CancelCallback; +import org.eclipse.che.ide.ui.dialogs.ConfirmCallback; +import org.eclipse.che.ide.ui.dialogs.DialogFactory; +import org.eclipse.che.security.oauth.JsOAuthWindow; +import org.eclipse.che.security.oauth.OAuthCallback; +import org.eclipse.che.security.oauth.OAuthStatus; + +import javax.validation.constraints.NotNull; + +/** + * Default implementation of authenticator, used when no provider-specific one is present. + * + * @author Max Shaposhnik + */ +public class DefaultOAuthAuthenticatorImpl implements OAuth2Authenticator, OAuthCallback { + AsyncCallback callback; + + private final DialogFactory dialogFactory; + private final CoreLocalizationConstant localizationConstant; + private String authenticationUrl; + + @Inject + public DefaultOAuthAuthenticatorImpl(DialogFactory dialogFactory, + CoreLocalizationConstant localizationConstant) { + this.dialogFactory = dialogFactory; + this.localizationConstant = localizationConstant; + } + + @Override + public void authorize(String authenticationUrl, @NotNull final AsyncCallback callback) { + this.authenticationUrl = authenticationUrl; + this.callback = callback; + showDialog(); + } + + private void showDialog() { + dialogFactory.createConfirmDialog(localizationConstant.authorizationDialogTitle(), localizationConstant.authorizationDialogText(), new ConfirmCallback() { + @Override + public void accepted() { + showAuthWindow(); + } + }, new CancelCallback() { + @Override + public void cancelled() { + callback.onSuccess(OAuthStatus.NOT_PERFORMED); + } + }).show(); + } + + @Override + public String getProviderName() { + return "default"; + } + + + @Override + public void onAuthenticated(OAuthStatus authStatus) { + callback.onSuccess(authStatus); + } + + private void showAuthWindow() { + JsOAuthWindow authWindow; + authWindow = new JsOAuthWindow(authenticationUrl, "error.url", 500, 980, this); + authWindow.loginWithOAuth(); + } +} diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/oauth/OAuth2AuthenticatorRegistryImpl.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/oauth/OAuth2AuthenticatorRegistryImpl.java new file mode 100644 index 00000000000..755f41899ce --- /dev/null +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/oauth/OAuth2AuthenticatorRegistryImpl.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.oauth; + +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorRegistry; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; +import org.eclipse.che.ide.util.loging.Log; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Map based implementation of OAuth registry + * + * @author Vitalii Parfonov + */ +@Singleton +public class OAuth2AuthenticatorRegistryImpl implements OAuth2AuthenticatorRegistry { + + private final Map authenticators; + + @Inject + public OAuth2AuthenticatorRegistryImpl(Set oAuth2Authenticators) { + authenticators = new HashMap<>(oAuth2Authenticators.size()); + for (OAuth2Authenticator authenticator : oAuth2Authenticators) { + final String providerName = authenticator.getProviderName(); + if (authenticators.get(providerName) != null) { + Log.warn(OAuth2AuthenticatorRegistryImpl.class, "OAuth2Authenticator provider " + providerName + " already registered. But can be only one"); + } else { + registerAuthenticator(providerName, authenticator); + } + } + } + + @Override + public void registerAuthenticator(String providerName, OAuth2Authenticator oAuth2Authenticator) { + authenticators.put(providerName, oAuth2Authenticator); + } + + @Override + public OAuth2Authenticator getAuthenticator(String providerName) { + return authenticators.get(providerName); + } +} diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/ErrorMessageUtils.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/ErrorMessageUtils.java deleted file mode 100644 index 68b664f7437..00000000000 --- a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/ErrorMessageUtils.java +++ /dev/null @@ -1,85 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * 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: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.ide.projectimport; - -import org.eclipse.che.api.core.rest.shared.dto.ServiceError; -import org.eclipse.che.ide.commons.exception.JobNotFoundException; -import org.eclipse.che.ide.commons.exception.ServerException; -import org.eclipse.che.ide.commons.exception.UnauthorizedException; -import org.eclipse.che.ide.dto.DtoFactory; - -import java.util.Collections; -import java.util.Map; - -/** - * The class contains business logic which allows define type of error. - * - * @author Dmitry Shnurenko - */ -public class ErrorMessageUtils { - - private static DtoFactory dtoFactory = new DtoFactory(); - - private ErrorMessageUtils() { - throw new UnsupportedOperationException("You can not create instance of Util class."); - } - - /** - * The method defines error type and returns error message from passed exception. - * - * @param exception - * passed exception - * @return error message - */ - public static String getErrorMessage(Throwable exception) { - if (exception instanceof JobNotFoundException) { - return "Project import failed"; - } else if (exception instanceof UnauthorizedException) { - return ((UnauthorizedException)exception).getMessage(); - } else { - return dtoFactory.createDtoFromJson(exception.getMessage(), ServiceError.class).getMessage(); - } - } - - /** - * Returns error code of the exception if it is of type {@clink ServerException} and has error code set, or -1 otherwise. - * - * @param exception - * passed exception - * @return error code - */ - public static int getErrorCode(Throwable exception) { - if (exception instanceof ServerException) { - return ((ServerException)exception).getErrorCode(); - } else if (exception instanceof org.eclipse.che.ide.websocket.rest.exceptions.ServerException) { - return ((org.eclipse.che.ide.websocket.rest.exceptions.ServerException)exception).getErrorCode(); - } else { - return -1; - } - } - - /** - * Returns attributes of the exception if it is of type {@clink ServerException} and has attributes set, or empty map otherwise. - * - * @param exception - * passed exception - * @return error code - */ - public static Map getAttributes(Throwable exception) { - if (exception instanceof ServerException) { - return ((ServerException)exception).getAttributes(); - } else if (exception instanceof org.eclipse.che.ide.websocket.rest.exceptions.ServerException) { - return ((org.eclipse.che.ide.websocket.rest.exceptions.ServerException)exception).getAttributes(); - } else { - return Collections.emptyMap(); - } - } -} diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporter.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporter.java index 0f6091c26ae..10655aa70fa 100644 --- a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporter.java +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporter.java @@ -10,11 +10,12 @@ *******************************************************************************/ package org.eclipse.che.ide.projectimport.wizard; +import com.google.common.base.Strings; +import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; -import org.eclipse.che.api.core.ErrorCodes; import org.eclipse.che.api.project.gwt.client.ProjectServiceClient; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; @@ -28,13 +29,25 @@ import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.event.project.CreateProjectEvent; import org.eclipse.che.ide.api.importer.AbstractImporter; -import org.eclipse.che.ide.api.project.wizard.ProjectNotificationSubscriber; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorRegistry; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorUrlProvider; import org.eclipse.che.ide.api.project.wizard.ImportProjectNotificationSubscriberFactory; +import org.eclipse.che.ide.api.project.wizard.ProjectNotificationSubscriber; import org.eclipse.che.ide.api.wizard.Wizard.CompleteCallback; -import org.eclipse.che.ide.projectimport.ErrorMessageUtils; import org.eclipse.che.ide.rest.AsyncRequestCallback; +import org.eclipse.che.ide.rest.RestContext; +import org.eclipse.che.ide.ui.dialogs.DialogFactory; +import org.eclipse.che.ide.util.ExceptionUtils; +import org.eclipse.che.security.oauth.OAuthStatus; import javax.validation.constraints.NotNull; +import java.util.Map; + +import static org.eclipse.che.api.core.ErrorCodes.UNABLE_GET_PRIVATE_SSH_KEY; +import static org.eclipse.che.api.core.ErrorCodes.UNAUTHORIZED_GIT_OPERATION; +import static org.eclipse.che.api.git.shared.ProviderInfo.AUTHENTICATE_URL; +import static org.eclipse.che.api.git.shared.ProviderInfo.PROVIDER_NAME; /** * @author Dmitry Shnurenko @@ -46,6 +59,9 @@ public class ProjectImporter extends AbstractImporter { private final CoreLocalizationConstant localizationConstant; private final EventBus eventBus; private final ProjectResolver projectResolver; + private final DialogFactory dialogFactory; + private final String restContext; + private final OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry; private ProjectConfigDto projectConfig; private CompleteCallback callback; @@ -56,12 +72,18 @@ public ProjectImporter(ProjectServiceClient projectService, CoreLocalizationConstant localizationConstant, ImportProjectNotificationSubscriberFactory subscriberFactory, AppContext appContext, - EventBus eventBus, - ProjectResolver projectResolver) { + ProjectResolver projectResolver, + DialogFactory dialogFactory, + @RestContext String restContext, + OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry, + EventBus eventBus) { super(appContext, projectService, subscriberFactory); this.vfsServiceClient = vfsServiceClient; this.localizationConstant = localizationConstant; this.projectResolver = projectResolver; + this.dialogFactory = dialogFactory; + this.restContext = restContext; + this.oAuth2AuthenticatorRegistry = oAuth2AuthenticatorRegistry; this.eventBus = eventBus; } @@ -86,34 +108,91 @@ protected void onFailure(Throwable exception) { } @Override - protected Promise importProject(@NotNull String pathToProject, - @NotNull String projectName, - @NotNull SourceStorageDto sourceStorage) { + protected Promise importProject(@NotNull final String pathToProject, + @NotNull final String projectName, + @NotNull final SourceStorageDto sourceStorage) { + return doImport(pathToProject, projectName, sourceStorage); + } + + + private Promise doImport(@NotNull final String pathToProject, + @NotNull final String projectName, + @NotNull final SourceStorageDto sourceStorage) { final ProjectNotificationSubscriber subscriber = subscriberFactory.createSubscriber(); subscriber.subscribe(projectName); - Promise importPromise = projectService.importProject(workspaceId, pathToProject, false, sourceStorage); - return importPromise.then(new Operation() { + importPromise.then(new Operation() { @Override public void apply(Void arg) throws OperationException { eventBus.fireEvent(new CreateProjectEvent(projectConfig)); projectResolver.resolveProject(callback, projectConfig); - subscriber.onSuccess(); } }).catchError(new Operation() { @Override public void apply(PromiseError exception) throws OperationException { - subscriber.onFailure(exception.getMessage()); - int errorCode = ErrorMessageUtils.getErrorCode(exception.getCause()); + int errorCode = ExceptionUtils.getErrorCode(exception.getCause()); // no ssh key found code. See org.eclipse.che.git.impl.nativegit.ssh.SshKeyProviderImpl. - if (errorCode == ErrorCodes.UNABLE_GET_PRIVATE_SSH_KEY) { + if (errorCode == UNABLE_GET_PRIVATE_SSH_KEY) { + subscriber.onFailure(exception.getCause().getMessage()); callback.onFailure(new Exception(localizationConstant.importProjectMessageUnableGetSshKey())); return; } - callback.onFailure(new Exception(exception.getCause().getMessage())); + if (errorCode == UNAUTHORIZED_GIT_OPERATION) { + subscriber.onFailure(exception.getCause().getMessage()); + final Map attributes = ExceptionUtils.getAttributes(exception.getCause()); + final String providerName = attributes.get(PROVIDER_NAME); + final String authenticateUrl = attributes.get(AUTHENTICATE_URL); + if (!Strings.isNullOrEmpty(providerName) && !Strings.isNullOrEmpty(authenticateUrl)) { + tryAuthenticateRepeatImport(providerName, + authenticateUrl, + pathToProject, + projectName, + sourceStorage, + subscriber); + } else { + dialogFactory.createMessageDialog(localizationConstant.oauthFailedToGetAuthenticatorTitle(), + localizationConstant.oauthFailedToGetAuthenticatorText(), null).show(); + } + } else { + subscriber.onFailure(exception.getMessage()); + callback.onFailure(new Exception(exception.getCause().getMessage())); + } } }); + + return importPromise; + } + + private void tryAuthenticateRepeatImport(@NotNull final String providerName, + @NotNull final String authenticateUrl, + @NotNull final String pathToProject, + @NotNull final String projectName, + @NotNull final SourceStorageDto sourceStorage, + @NotNull final ProjectNotificationSubscriber subscriber) { + OAuth2Authenticator authenticator = oAuth2AuthenticatorRegistry.getAuthenticator(providerName); + if(authenticator == null) { + authenticator = oAuth2AuthenticatorRegistry.getAuthenticator("default"); + } + authenticator.authorize(OAuth2AuthenticatorUrlProvider.get(restContext, authenticateUrl), + new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + callback.onFailure(new Exception(caught.getMessage())); + } + + @Override + public void onSuccess(OAuthStatus result) { + if (!result.equals(OAuthStatus.NOT_PERFORMED)) { + doImport(pathToProject, projectName, sourceStorage); + } else { + subscriber.onFailure("Authentication cancelled"); + callback.onCompleted(); + } + } + }); + } + } diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectResolver.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectResolver.java index fe7f329aa0f..5134ec0db7a 100644 --- a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectResolver.java +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectResolver.java @@ -24,7 +24,6 @@ import org.eclipse.che.ide.api.project.type.ProjectTypeRegistry; import org.eclipse.che.ide.api.project.wizard.ProjectNotificationSubscriber; import org.eclipse.che.ide.api.wizard.Wizard.CompleteCallback; -import org.eclipse.che.ide.projectimport.ErrorMessageUtils; import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.rest.Unmarshallable; diff --git a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectUpdater.java b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectUpdater.java index 756c3998563..9c2493ea74b 100644 --- a/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectUpdater.java +++ b/core/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/projectimport/wizard/ProjectUpdater.java @@ -22,7 +22,6 @@ import org.eclipse.che.ide.api.event.project.ProjectUpdatedEvent; import org.eclipse.che.ide.api.project.wizard.ProjectNotificationSubscriber; import org.eclipse.che.ide.api.wizard.Wizard.CompleteCallback; -import org.eclipse.che.ide.projectimport.ErrorMessageUtils; import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.rest.Unmarshallable; diff --git a/core/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/CoreLocalizationConstant.properties b/core/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/CoreLocalizationConstant.properties index 126ea8175b5..07dcea848b6 100644 --- a/core/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/CoreLocalizationConstant.properties +++ b/core/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/CoreLocalizationConstant.properties @@ -154,6 +154,11 @@ importProject.name.prompt = Define the name of your project... importProject.description.prompt = Add a description to your project... importProject.zipImporter.skipFirstLevel = Skip the root folder of the archive import.project.error = Import project failed: +authorization.dialog.title = Authorization +authorization.dialog.text = Remote host requests authorization through OAuth2 protocol +oauth.failed.to.get.authenticator.title=Error +oauth.failed.to.get.authenticator.text=Remote repository requires authentication, but no OAuth provider found for this url + ############### Actions ############################### action.newFolder.title = Folder diff --git a/core/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporterTest.java b/core/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporterTest.java index c850e8c3657..02ccba4d6e1 100644 --- a/core/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporterTest.java +++ b/core/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/projectimport/wizard/ProjectImporterTest.java @@ -24,6 +24,8 @@ import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto; import org.eclipse.che.ide.CoreLocalizationConstant; import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorRegistry; import org.eclipse.che.ide.api.project.wizard.ProjectNotificationSubscriber; import org.eclipse.che.ide.api.project.wizard.ImportProjectNotificationSubscriberFactory; import org.eclipse.che.ide.api.wizard.Wizard; @@ -38,6 +40,8 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import java.util.Collections; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -81,6 +85,8 @@ public class ProjectImporterTest { private Wizard.CompleteCallback completeCallback; @Mock private Promise importPromise; + @Mock + private OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry; @Captor private ArgumentCaptor> callbackCaptorForItem; @@ -104,8 +110,11 @@ public void setUp() { localizationConstant, subscriberFactory, appContext, - eventBus, - resolver); + resolver, + null, + null, + oAuth2AuthenticatorRegistry, + eventBus); } @Test diff --git a/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/ErrorCodes.java b/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/ErrorCodes.java index 0c72a6edb7c..6e843f72f5d 100644 --- a/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/ErrorCodes.java +++ b/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/ErrorCodes.java @@ -21,4 +21,5 @@ private ErrorCodes() { public static final int NO_COMMITTER_NAME_OR_EMAIL_DEFINED = 15216; public static final int UNABLE_GET_PRIVATE_SSH_KEY = 32068; + public static final int UNAUTHORIZED_GIT_OPERATION = 32080; } diff --git a/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/UnauthorizedException.java b/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/UnauthorizedException.java index dccaaab333b..82946299692 100644 --- a/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/UnauthorizedException.java +++ b/core/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/UnauthorizedException.java @@ -10,8 +10,14 @@ *******************************************************************************/ package org.eclipse.che.api.core; +import org.eclipse.che.api.core.rest.shared.dto.ExtendedError; import org.eclipse.che.api.core.rest.shared.dto.ServiceError; +import java.util.Collections; +import java.util.Map; + +import static org.eclipse.che.dto.server.DtoFactory.newDto; + /** * A {@code UnauthorizedException} is thrown when caller isn't authorized to access some resource. *

@@ -29,4 +35,12 @@ public UnauthorizedException(String message) { public UnauthorizedException(ServiceError serviceError) { super(serviceError); } + + public UnauthorizedException(String message, int errorCode, Map attributes) { + super(newDto(ExtendedError.class).withMessage(message).withErrorCode(errorCode).withAttributes(attributes)); + } + + public UnauthorizedException(String message, int errorCode) { + this(message, errorCode, Collections.emptyMap()); + } } diff --git a/core/platform-api/che-core-api-git/pom.xml b/core/platform-api/che-core-api-git/pom.xml index 0c6cbef018e..b6a6d7afc9b 100644 --- a/core/platform-api/che-core-api-git/pom.xml +++ b/core/platform-api/che-core-api-git/pom.xml @@ -46,6 +46,10 @@ javax.inject javax.inject + + javax.validation + validation-api + javax.ws.rs javax.ws.rs-api diff --git a/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsLoader.java b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsLoader.java index 329a4097875..a92017bc6b8 100644 --- a/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsLoader.java +++ b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsLoader.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipse.che.api.git; -import org.eclipse.che.api.git.shared.GitUser; +import org.eclipse.che.api.git.shared.ProviderInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,4 +67,22 @@ public UserCredential getUserCredential(String url) throws GitException { return null; } + + + /** + * Searches for CredentialsProvider instances by url and if needed instance exists, it asks for info, + * or return null otherwise. + * + * @param url + * given URL + * @return credentials from provider + */ + public ProviderInfo getProviderInfo(String url) { + for (CredentialsProvider cp : credentialsProviders.values()) { + if (url != null && cp.canProvideCredentials(url)) { + return cp.getProviderInfo(); + } + } + return null; + } } \ No newline at end of file diff --git a/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsProvider.java b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsProvider.java index ed23736a984..19a676012ea 100644 --- a/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsProvider.java +++ b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/CredentialsProvider.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.che.api.git; +import org.eclipse.che.api.git.shared.ProviderInfo; + /** * Provides credentials to use with git commands that need it * @@ -34,4 +36,9 @@ public interface CredentialsProvider { * @return return true if current provider can provide credentials for the given url. */ boolean canProvideCredentials(String url); + + /** + * @return additional information about given provider + */ + ProviderInfo getProviderInfo(); } diff --git a/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitProjectImporter.java b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitProjectImporter.java index 21092ad0592..4fecfb7e143 100644 --- a/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitProjectImporter.java +++ b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitProjectImporter.java @@ -193,12 +193,7 @@ public void importSources(FolderEntry baseFolder, cleanGit(git.getWorkingDir()); } } - } catch (UnauthorizedException e) { - throw new UnauthorizedException( - "You are not authorized to perform the remote import. You may need to provide accurate keys to the " + - "external system. You can create a new SSH key pair in Window->Preferences->Keys And " + - "Certificates->SSH Keystore."); - } catch (URISyntaxException e) { + } catch (URISyntaxException e) { throw new ServerException( "Your project cannot be imported. The issue is either from git configuration, a malformed URL, " + "or file system corruption. Please contact support for assistance.", diff --git a/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/shared/ProviderInfo.java b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/shared/ProviderInfo.java new file mode 100644 index 00000000000..b7717c9ebee --- /dev/null +++ b/core/platform-api/che-core-api-git/src/main/java/org/eclipse/che/api/git/shared/ProviderInfo.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.git.shared; + +import javax.validation.constraints.NotNull; +import java.util.HashMap; +import java.util.Map; + +/** + * Contains additional info about the credential provider, such as provider name, + * URL ans some other that can be used to authorize in order to perform operations. + * + * @author Max Shaposhnik + */ +public class ProviderInfo { + + public static final String AUTHENTICATE_URL = "authenticateUrl"; + + public static final String PROVIDER_NAME = "providerName"; + + private Map info = new HashMap<>(); + + public ProviderInfo(@NotNull String providerName, + @NotNull String authenticateUrl) { + info.put(PROVIDER_NAME, providerName); + info.put(AUTHENTICATE_URL, authenticateUrl); + } + + public String getProviderName() { + return info.get(PROVIDER_NAME); + } + + public String getAuthenticateUrl() { + return info.get(AUTHENTICATE_URL); + } + + public void put(String key, String value) { + info.put(key, value); + } + + public String get(String key) { + return info.get(key); + } + + +} diff --git a/plugins/plugin-git/che-plugin-git-provider-che/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/CheAccessTokenCredentialProvider.java b/plugins/plugin-git/che-plugin-git-provider-che/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/CheAccessTokenCredentialProvider.java index c8705fe884a..823cd6e8c05 100644 --- a/plugins/plugin-git/che-plugin-git-provider-che/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/CheAccessTokenCredentialProvider.java +++ b/plugins/plugin-git/che-plugin-git-provider-che/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/CheAccessTokenCredentialProvider.java @@ -12,6 +12,7 @@ import org.eclipse.che.api.git.CredentialsProvider; import org.eclipse.che.api.git.GitException; +import org.eclipse.che.api.git.shared.ProviderInfo; import org.eclipse.che.api.git.UserCredential; import org.eclipse.che.commons.env.EnvironmentContext; @@ -60,4 +61,8 @@ public boolean canProvideCredentials(String url) { return url.contains(cheHostName); } + @Override + public ProviderInfo getProviderInfo() { + return null; + } } \ No newline at end of file diff --git a/plugins/plugin-github/che-plugin-github-ext-github/pom.xml b/plugins/plugin-github/che-plugin-github-ext-github/pom.xml index 3661d601c24..c0ebfcdd49f 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/pom.xml +++ b/plugins/plugin-github/che-plugin-github-ext-github/pom.xml @@ -29,6 +29,10 @@ com.google.code.gson gson + + com.google.guava + guava + com.google.gwt.inject gin diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImpl.java b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImpl.java index 0f8bd01137b..e66c9c9f50e 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImpl.java +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImpl.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.che.ide.ext.github.client.authenticator; +import com.google.common.collect.Lists; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; @@ -21,6 +22,8 @@ import org.eclipse.che.api.ssh.shared.dto.SshPairDto; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.notification.NotificationManager; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorUrlProvider; import org.eclipse.che.ide.ext.github.client.GitHubLocalizationConstant; import org.eclipse.che.ide.ext.git.ssh.client.GitSshKeyUploaderRegistry; import org.eclipse.che.ide.ext.git.ssh.client.SshKeyUploader; @@ -40,8 +43,9 @@ /** * @author Roman Nikitenko */ -public class GitHubAuthenticatorImpl implements GitHubAuthenticator, OAuthCallback, GitHubAuthenticatorViewImpl.ActionDelegate { +public class GitHubAuthenticatorImpl implements OAuth2Authenticator, OAuthCallback, GitHubAuthenticatorViewImpl.ActionDelegate { public static final String GITHUB_HOST = "github.com"; + public static final String GITHUB = "github"; AsyncCallback callback; @@ -53,6 +57,7 @@ public class GitHubAuthenticatorImpl implements GitHubAuthenticator, OAuthCallba private final GitHubLocalizationConstant locale; private final String baseUrl; private final AppContext appContext; + private String authenticationUrl; @Inject public GitHubAuthenticatorImpl(GitSshKeyUploaderRegistry registry, @@ -75,11 +80,17 @@ public GitHubAuthenticatorImpl(GitSshKeyUploaderRegistry registry, } @Override - public void authorize(@NotNull final AsyncCallback callback) { + public void authorize(String authenticationUrl, @NotNull final AsyncCallback callback) { + this.authenticationUrl = authenticationUrl; this.callback = callback; view.showDialog(); } + @Override + public String getProviderName() { + return GITHUB; + } + @Override public void onCancelled() { callback.onFailure(new Exception("Authorization request rejected by user.")); @@ -100,19 +111,18 @@ public void onAuthenticated(OAuthStatus authStatus) { } private void showAuthWindow() { - JsOAuthWindow authWindow = new JsOAuthWindow(getAuthUrl(), "error.url", 500, 980, this); + JsOAuthWindow authWindow; + if (authenticationUrl == null) { + authWindow = new JsOAuthWindow(getAuthUrl(), "error.url", 500, 980, this); + } else { + authWindow = new JsOAuthWindow(authenticationUrl, "error.url", 500, 980, this); + } authWindow.loginWithOAuth(); } private String getAuthUrl() { - String userId = appContext.getCurrentUser().getProfile().getId(); - return baseUrl - + "/oauth/authenticate?oauth_provider=github" - + "&scope=user,repo,write:public_key&userId=" + userId - + "&redirect_after_login=" - + Window.Location.getProtocol() + "//" - + Window.Location.getHost() + "/ws/" - + appContext.getWorkspace().getConfig().getName(); + return OAuth2AuthenticatorUrlProvider.get(baseUrl, "github", appContext.getCurrentUser().getProfile().getUserId(), Lists + .asList("user", new String[]{"repo", "write:public_key"})); } private void generateSshKeys(final OAuthStatus authStatus) { @@ -157,7 +167,7 @@ public void apply(List result) throws OperationException { .catchError(new Operation() { @Override public void apply(PromiseError arg) throws OperationException { - Log.error(GitHubAuthenticator.class, arg.getCause()); + Log.error(OAuth2Authenticator.class, arg.getCause()); } }); } @@ -173,7 +183,7 @@ private void removeFailedKey(@NotNull final SshPairDto pair) { .catchError(new Operation() { @Override public void apply(PromiseError arg) throws OperationException { - Log.error(GitHubAuthenticator.class, arg.getCause()); + Log.error(OAuth2Authenticator.class, arg.getCause()); } }); } diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorView.java b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorView.java index 50da355e5b3..b53a1e8d850 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorView.java +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorView.java @@ -20,7 +20,7 @@ @ImplementedBy(GitHubAuthenticatorViewImpl.class) public interface GitHubAuthenticatorView extends View { - public interface ActionDelegate { + interface ActionDelegate { /** Defines what's done when the user clicks cancel. */ void onCancelled(); diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorViewImpl.java b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorViewImpl.java index 1bd6edf20d7..efd3b064282 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorViewImpl.java +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorViewImpl.java @@ -26,15 +26,15 @@ /** * @author Roman Nikitenko */ -public class GitHubAuthenticatorViewImpl implements GitHubAuthenticatorView{ +public class GitHubAuthenticatorViewImpl implements GitHubAuthenticatorView { private DialogFactory dialogFactory; private GitHubLocalizationConstant locale; - private ActionDelegate delegate; + private ActionDelegate delegate; - private CheckBox isGenerateKeys; - private DockLayoutPanel contentPanel; + private CheckBox isGenerateKeys; + private DockLayoutPanel contentPanel; @Inject public GitHubAuthenticatorViewImpl(DialogFactory dialogFactory, diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenter.java b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenter.java index 43a3299f43d..88ab8155652 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenter.java +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenter.java @@ -10,22 +10,27 @@ *******************************************************************************/ package org.eclipse.che.ide.ext.github.client.importer.page; +import com.google.common.collect.Lists; import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.inject.Inject; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorRegistry; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorUrlProvider; import org.eclipse.che.ide.api.wizard.AbstractWizardPage; import org.eclipse.che.ide.commons.exception.UnauthorizedException; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.ext.github.client.GitHubClientService; import org.eclipse.che.ide.ext.github.client.GitHubLocalizationConstant; -import org.eclipse.che.ide.ext.github.client.authenticator.GitHubAuthenticator; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; import org.eclipse.che.ide.ext.github.client.load.ProjectData; import org.eclipse.che.ide.ext.github.client.marshaller.AllRepositoriesUnmarshaller; import org.eclipse.che.ide.ext.github.shared.GitHubRepository; import org.eclipse.che.ide.rest.AsyncRequestCallback; +import org.eclipse.che.ide.rest.RestContext; import org.eclipse.che.ide.util.NameUtils; import org.eclipse.che.security.oauth.OAuthStatus; @@ -60,16 +65,22 @@ public class GithubImporterPagePresenter extends AbstractWizardPage> repositories; private GitHubLocalizationConstant locale; private GithubImporterPageView view; - private GitHubAuthenticator gitHubAuthenticator; + private final String restContext; + private final AppContext appContext; + private OAuth2Authenticator gitHubAuthenticator; @Inject public GithubImporterPagePresenter(GithubImporterPageView view, - GitHubAuthenticator gitHubAuthenticator, + OAuth2AuthenticatorRegistry gitHubAuthenticatorRegistry, GitHubClientService gitHubClientService, DtoFactory dtoFactory, + @RestContext String restContext, + AppContext appContext, GitHubLocalizationConstant locale) { this.view = view; - this.gitHubAuthenticator = gitHubAuthenticator; + this.restContext = restContext; + this.appContext = appContext; + this.gitHubAuthenticator = gitHubAuthenticatorRegistry.getAuthenticator("github"); this.gitHubClientService = gitHubClientService; this.dtoFactory = dtoFactory; this.view.setDelegate(this); @@ -224,18 +235,21 @@ protected void onFailure(Throwable exception) { */ private void authorize() { showProcessing(true); - gitHubAuthenticator.authorize(new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - showProcessing(false); - } + gitHubAuthenticator.authorize( + OAuth2AuthenticatorUrlProvider.get(restContext, "github", appContext.getCurrentUser().getProfile().getUserId(), + Lists.asList("user", new String[]{"repo", "write:public_key"})), + new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + showProcessing(false); + } - @Override - public void onSuccess(OAuthStatus result) { - showProcessing(false); - getUserRepos(); - } - }); + @Override + public void onSuccess(OAuthStatus result) { + showProcessing(false); + getUserRepos(); + } + }); } @Override diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/inject/GitHubGinModule.java b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/inject/GitHubGinModule.java index 064dfcfce73..557d08310ef 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/inject/GitHubGinModule.java +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/main/java/org/eclipse/che/ide/ext/github/client/inject/GitHubGinModule.java @@ -13,7 +13,7 @@ import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.project.wizard.ImportWizardRegistrar; import org.eclipse.che.ide.ext.github.client.GitHubClientService; -import org.eclipse.che.ide.ext.github.client.authenticator.GitHubAuthenticator; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; import org.eclipse.che.ide.ext.github.client.authenticator.GitHubAuthenticatorImpl; import org.eclipse.che.ide.ext.github.client.importer.GitHubImportWizardRegistrar; import org.eclipse.che.ide.ext.github.client.GitHubClientServiceImpl; @@ -29,8 +29,7 @@ public class GitHubGinModule extends AbstractGinModule { @Override protected void configure() { bind(GitHubClientService.class).to(GitHubClientServiceImpl.class).in(Singleton.class); - bind(GitHubAuthenticator.class).to(GitHubAuthenticatorImpl.class).in(Singleton.class); - + GinMultibinder.newSetBinder(binder(), OAuth2Authenticator.class).addBinding().to(GitHubAuthenticatorImpl.class); GinMultibinder.newSetBinder(binder(), ImportWizardRegistrar.class).addBinding().to(GitHubImportWizardRegistrar.class); } } \ No newline at end of file diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/main/resources/org/eclipse/che/ide/ext/github/GitHub.gwt.xml b/plugins/plugin-github/che-plugin-github-ext-github/src/main/resources/org/eclipse/che/ide/ext/github/GitHub.gwt.xml index 7fb787a33c9..bf44fed7a0a 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/main/resources/org/eclipse/che/ide/ext/github/GitHub.gwt.xml +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/main/resources/org/eclipse/che/ide/ext/github/GitHub.gwt.xml @@ -23,6 +23,7 @@ + \ No newline at end of file diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImplTest.java b/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImplTest.java index 84b434b6a82..503de8c444b 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImplTest.java +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/authenticator/GitHubAuthenticatorImplTest.java @@ -128,7 +128,7 @@ public void delegateShouldBeSet() throws Exception { @Test public void dialogShouldBeShow() throws Exception { AsyncCallback callback = getCallBack(); - gitHubAuthenticator.authorize(callback); + gitHubAuthenticator.authorize(null, callback); verify(view).showDialog(); assertThat(gitHubAuthenticator.callback, is(callback)); @@ -171,7 +171,7 @@ public void onAuthenticatedWhenGenerateKeysIsNotSelected() throws Exception { when(user.getProfile()).thenReturn(profile); when(profile.getId()).thenReturn(userId); - gitHubAuthenticator.authorize(getCallBack()); + gitHubAuthenticator.authorize(null, getCallBack()); gitHubAuthenticator.onAuthenticated(authStatus); verify(view).isGenerateKeysSelected(); @@ -198,7 +198,7 @@ public void onAuthenticatedWhenGenerateKeysIsSuccess() throws Exception { when(user.getProfile()).thenReturn(profile); when(profile.getId()).thenReturn(userId); - gitHubAuthenticator.authorize(getCallBack()); + gitHubAuthenticator.authorize(null, getCallBack()); gitHubAuthenticator.onAuthenticated(authStatus); verify(keyProvider).uploadKey(eq(userId), generateKeyCallbackCaptor.capture()); @@ -229,7 +229,7 @@ public void onAuthenticatedWhenGenerateKeysIsFailure() throws Exception { when(profile.getId()).thenReturn(userId); when(dialogFactory.createMessageDialog(anyString(), anyString(), Matchers.anyObject())).thenReturn(messageDialog); - gitHubAuthenticator.authorize(getCallBack()); + gitHubAuthenticator.authorize(null, getCallBack()); gitHubAuthenticator.onAuthenticated(authStatus); verify(keyProvider).uploadKey(eq(userId), generateKeyCallbackCaptor.capture()); @@ -266,7 +266,7 @@ public void onAuthenticatedWhenGetFailedKeyIsSuccess() throws Exception { when(pair.getName()).thenReturn(GITHUB_HOST); when(pair.getService()).thenReturn(SshKeyManagerPresenter.GIT_SSH_SERVICE); - gitHubAuthenticator.authorize(getCallBack()); + gitHubAuthenticator.authorize(null, getCallBack()); gitHubAuthenticator.onAuthenticated(authStatus); verify(keyUploader).uploadKey(eq(userId), generateKeyCallbackCaptor.capture()); diff --git a/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenterTest.java b/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenterTest.java index 7ff405a1293..cca48d620a2 100644 --- a/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenterTest.java +++ b/plugins/plugin-github/che-plugin-github-ext-github/src/test/java/org/eclipse/che/ide/ext/github/client/importer/page/GithubImporterPagePresenterTest.java @@ -16,19 +16,24 @@ import org.eclipse.che.api.project.shared.dto.ProjectImporterDescriptor; import org.eclipse.che.api.user.gwt.client.UserServiceClient; +import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.app.CurrentUser; import org.eclipse.che.ide.api.notification.NotificationManager; +import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorRegistry; import org.eclipse.che.ide.api.wizard.Wizard; import org.eclipse.che.ide.commons.exception.UnauthorizedException; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.ext.github.client.GitHubClientService; import org.eclipse.che.ide.ext.github.client.GitHubLocalizationConstant; -import org.eclipse.che.ide.ext.github.client.authenticator.GitHubAuthenticator; +import org.eclipse.che.ide.api.oauth.OAuth2Authenticator; import org.eclipse.che.ide.ext.github.client.load.ProjectData; import org.eclipse.che.ide.ext.github.shared.GitHubRepository; import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; +import org.eclipse.che.ide.ui.dialogs.message.MessageDialog; import org.eclipse.che.security.oauth.OAuthStatus; import org.eclipse.che.test.GwtReflectionUtils; import org.junit.Before; @@ -93,13 +98,19 @@ public class GithubImporterPagePresenterTest { @Mock private Map parameters; @Mock - private GitHubAuthenticator gitHubAuthenticator; - @InjectMocks + private OAuth2Authenticator gitHubAuthenticator; + @Mock + private OAuth2AuthenticatorRegistry gitHubAuthenticatorRegistry; + @Mock + private AppContext appContext; + private GithubImporterPagePresenter presenter; @Before public void setUp() { when(dataObject.getSource()).thenReturn(source); + when(gitHubAuthenticatorRegistry.getAuthenticator(eq("github"))).thenReturn(gitHubAuthenticator); + presenter = new GithubImporterPagePresenter(view, gitHubAuthenticatorRegistry, gitHubClientService, dtoFactory, "", appContext, locale); presenter.setUpdateDelegate(updateDelegate); presenter.init(dataObject); } @@ -346,6 +357,15 @@ public void projectDescriptionChangedTest() { @Test public void onLoadRepoClickedWhenAuthorizeIsFailed() throws Exception { + String userId = "userId"; + CurrentUser user = mock(CurrentUser.class); + ProfileDescriptor profile = mock(ProfileDescriptor.class); + + when(appContext.getCurrentUser()).thenReturn(user); + when(user.getProfile()).thenReturn(profile); + when(profile.getId()).thenReturn(userId); + + final Throwable exception = mock(UnauthorizedException.class); presenter.onLoadRepoClicked(); @@ -354,7 +374,7 @@ public void onLoadRepoClickedWhenAuthorizeIsFailed() throws Exception { AsyncRequestCallback>> asyncRequestCallback = asyncRequestCallbackRepoListCaptor.getValue(); GwtReflectionUtils.callOnFailure(asyncRequestCallback, exception); - verify(gitHubAuthenticator).authorize(asyncCallbackCaptor.capture()); + verify(gitHubAuthenticator).authorize(anyString(), asyncCallbackCaptor.capture()); AsyncCallback asyncCallback= asyncCallbackCaptor.getValue(); asyncCallback.onFailure(exception); @@ -370,6 +390,13 @@ public void onLoadRepoClickedWhenAuthorizeIsFailed() throws Exception { @Test public void onLoadRepoClickedWhenAuthorizeIsSuccessful() throws Exception { final Throwable exception = mock(UnauthorizedException.class); + String userId = "userId"; + CurrentUser user = mock(CurrentUser.class); + ProfileDescriptor profile = mock(ProfileDescriptor.class); + + when(appContext.getCurrentUser()).thenReturn(user); + when(user.getProfile()).thenReturn(profile); + when(profile.getId()).thenReturn(userId); presenter.onLoadRepoClicked(); @@ -377,7 +404,7 @@ public void onLoadRepoClickedWhenAuthorizeIsSuccessful() throws Exception { AsyncRequestCallback>> asyncRequestCallback = asyncRequestCallbackRepoListCaptor.getValue(); GwtReflectionUtils.callOnFailure(asyncRequestCallback, exception); - verify(gitHubAuthenticator).authorize(asyncCallbackCaptor.capture()); + verify(gitHubAuthenticator).authorize(anyString(), asyncCallbackCaptor.capture()); AsyncCallback asyncCallback= asyncCallbackCaptor.getValue(); asyncCallback.onSuccess(null); diff --git a/plugins/plugin-github/che-plugin-github-provider-github/pom.xml b/plugins/plugin-github/che-plugin-github-provider-github/pom.xml index 757475a76ef..aa9da92d044 100644 --- a/plugins/plugin-github/che-plugin-github-provider-github/pom.xml +++ b/plugins/plugin-github/che-plugin-github-provider-github/pom.xml @@ -33,6 +33,10 @@ javax.inject javax.inject + + javax.ws.rs + javax.ws.rs-api + org.eclipse.che.core che-core-api-auth diff --git a/plugins/plugin-github/che-plugin-github-provider-github/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/GitHubOAuthCredentialProvider.java b/plugins/plugin-github/che-plugin-github-provider-github/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/GitHubOAuthCredentialProvider.java index 2ee62ca4ad5..a45c2102be2 100644 --- a/plugins/plugin-github/che-plugin-github-provider-github/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/GitHubOAuthCredentialProvider.java +++ b/plugins/plugin-github/che-plugin-github-provider-github/src/main/java/org/eclipse/che/ide/ext/git/server/nativegit/GitHubOAuthCredentialProvider.java @@ -13,6 +13,7 @@ import org.eclipse.che.api.auth.oauth.OAuthTokenProvider; import org.eclipse.che.api.auth.shared.dto.OAuthToken; import org.eclipse.che.api.git.GitException; +import org.eclipse.che.api.git.shared.ProviderInfo; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.api.git.CredentialsProvider; import org.eclipse.che.api.git.UserCredential; @@ -22,6 +23,7 @@ import javax.inject.Inject; import javax.inject.Singleton; +import javax.ws.rs.core.UriBuilder; import java.io.IOException; /** @@ -34,12 +36,12 @@ public class GitHubOAuthCredentialProvider implements CredentialsProvider { private static String OAUTH_PROVIDER_NAME = "github"; private final OAuthTokenProvider oAuthTokenProvider; - + private final String authorizationServicePath; @Inject public GitHubOAuthCredentialProvider(OAuthTokenProvider oAuthTokenProvider) { this.oAuthTokenProvider = oAuthTokenProvider; - + this.authorizationServicePath = "/oauth/authenticate"; } @Override @@ -65,5 +67,14 @@ public boolean canProvideCredentials(String url) { return url.contains("github.com"); } + @Override + public ProviderInfo getProviderInfo() { + return new ProviderInfo(OAUTH_PROVIDER_NAME, UriBuilder.fromPath(authorizationServicePath) + .queryParam("oauth_provider", OAUTH_PROVIDER_NAME) + .queryParam("userId", EnvironmentContext.getCurrent().getUser().getId()) + .queryParam("scope", "repo") + .build() + .toString()); + } }