summaryrefslogtreecommitdiff
path: root/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java')
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java303
1 files changed, 303 insertions, 0 deletions
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java
new file mode 100644
index 000000000000..9fb377e1dafe
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.svn.auth;
+
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.popup.util.PopupUtil;
+import com.intellij.openapi.util.Getter;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.WaitForProgressToShow;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.net.HttpConfigurable;
+import com.intellij.util.proxy.CommonProxy;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.svn.SvnBundle;
+import org.jetbrains.idea.svn.SvnConfiguration;
+import org.jetbrains.idea.svn.SvnVcs;
+import org.jetbrains.idea.svn.dialogs.SimpleCredentialsDialog;
+import org.tmatesoft.svn.core.SVNURL;
+import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
+import org.tmatesoft.svn.core.auth.SVNAuthentication;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/26/13
+ * Time: 1:27 PM
+ */
+public class AuthenticationService {
+
+ @NotNull private final SvnVcs myVcs;
+ private final boolean myIsActive;
+ private static final Logger LOG = Logger.getInstance(AuthenticationService.class);
+ private File myTempDirectory;
+ private boolean myProxyCredentialsWereReturned;
+ private SvnConfiguration myConfiguration;
+ private final Set<String> myRequestedCredentials;
+
+ public AuthenticationService(@NotNull SvnVcs vcs, boolean isActive) {
+ myVcs = vcs;
+ myIsActive = isActive;
+ myConfiguration = SvnConfiguration.getInstance(myVcs.getProject());
+ myRequestedCredentials = ContainerUtil.newHashSet();
+ }
+
+ @NotNull
+ public SvnVcs getVcs() {
+ return myVcs;
+ }
+
+ @Nullable
+ public File getTempDirectory() {
+ return myTempDirectory;
+ }
+
+ public boolean isActive() {
+ return myIsActive;
+ }
+
+ public boolean authenticateFor(@Nullable String realm, SVNURL repositoryUrl, boolean passwordRequest) {
+ if (repositoryUrl == null) {
+ return false;
+ }
+ return new CredentialsAuthenticator(this, repositoryUrl, realm).tryAuthenticate(passwordRequest);
+ }
+
+ @Nullable
+ public SVNAuthentication requestCredentials(final SVNURL repositoryUrl, final String type) {
+ SVNAuthentication authentication = null;
+
+ if (repositoryUrl != null) {
+ final String realm = repositoryUrl.toDecodedString();
+
+ authentication = requestCredentials(realm, type, new Getter<SVNAuthentication>() {
+ @Override
+ public SVNAuthentication get() {
+ return myVcs.getSvnConfiguration().getInteractiveManager(myVcs).getInnerProvider()
+ .requestClientAuthentication(type, repositoryUrl, realm, null, null, true);
+ }
+ });
+ }
+
+ if (authentication == null) {
+ LOG.warn("Could not get authentication. Type - " + type + ", Url - " + repositoryUrl);
+ }
+
+ return authentication;
+ }
+
+ @Nullable
+ private <T> T requestCredentials(@NotNull String realm, @NotNull String type, @NotNull Getter<T> fromUserProvider) {
+ T result = null;
+ // Search for stored credentials not only by key but also by "parent" keys. This is useful when we work just with URLs
+ // (not working copy) and can't detect repository url beforehand because authentication is required. If found credentials of "parent"
+ // are not correct then current key will already be stored in myRequestedCredentials - thus user will be asked for credentials and
+ // provided result will be stored in cache (with necessary key).
+ Object data = SvnConfiguration.RUNTIME_AUTH_CACHE.getDataWithLowerCheck(type, realm);
+ String key = SvnConfiguration.AuthStorage.getKey(type, realm);
+
+ // we return credentials from cache if they are asked for the first time during command execution, otherwise - user is asked
+ if (data != null && !myRequestedCredentials.contains(key)) {
+ // we already have credentials in memory cache
+ result = (T)data;
+ myRequestedCredentials.add(key);
+ }
+ else if (myIsActive) {
+ // ask user for credentials
+ result = fromUserProvider.get();
+ if (result != null) {
+ // save user credentials to memory cache
+ myVcs.getSvnConfiguration().acknowledge(type, realm, result);
+ myRequestedCredentials.add(key);
+ }
+ }
+
+ return result;
+ }
+
+ @Nullable
+ public String requestSshCredentials(@NotNull final String realm,
+ @NotNull final SimpleCredentialsDialog.Mode mode,
+ @NotNull final String key) {
+ return requestCredentials(realm, ISVNAuthenticationManager.SSH, new Getter<String>() {
+ @Override
+ public String get() {
+ final Ref<String> answer = new Ref<String>();
+
+ Runnable command = new Runnable() {
+ public void run() {
+ SimpleCredentialsDialog dialog = new SimpleCredentialsDialog(myVcs.getProject());
+
+ dialog.setup(mode, realm, key, true);
+ dialog.setTitle(SvnBundle.message("dialog.title.authentication.required"));
+ dialog.setSaveEnabled(false);
+ dialog.show();
+ if (dialog.isOK()) {
+ answer.set(dialog.getPassword());
+ }
+ }
+ };
+
+ // Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in
+ // the thread that started progress
+ WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any());
+
+ return answer.get();
+ }
+ });
+ }
+
+ public boolean acceptSSLServerCertificate(final SVNURL repositoryUrl, final String realm) {
+ if (repositoryUrl == null) {
+ return false;
+ }
+
+ return new SSLServerCertificateAuthenticator(this, repositoryUrl, realm).tryAuthenticate();
+ }
+
+ public void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password) {
+ if (repositoryUrl == null) {
+ return;
+ }
+
+ final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
+ final List<String> kinds = getKinds(repositoryUrl, password);
+
+ for (String kind : kinds) {
+ configuration.clearCredentials(kind, realm);
+ }
+ }
+
+ public boolean haveDataForTmpConfig() {
+ final HttpConfigurable instance = HttpConfigurable.getInstance();
+ return SvnConfiguration.getInstance(myVcs.getProject()).isIsUseDefaultProxy() &&
+ (instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC);
+ }
+
+ @Nullable
+ public static Proxy getIdeaDefinedProxy(@NotNull final SVNURL url) {
+ // SVNKit authentication implementation sets repositories as noProxy() to provide custom proxy authentication logic - see for instance,
+ // SvnAuthenticationManager.getProxyManager(). But noProxy() setting is not cleared correctly in all cases - so if svn command
+ // (for command line) is executed on thread where repository url was added as noProxy() => proxies are not retrieved for such commands
+ // and execution logic is incorrect.
+
+ // To prevent such behavior repositoryUrl is manually removed from noProxy() list (for current thread).
+ // NOTE, that current method is only called from code flows for executing commands through command line client and should not be called
+ // from SVNKit code flows.
+ CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort());
+
+ final List<Proxy> proxies = CommonProxy.getInstance().select(URI.create(url.toString()));
+ if (proxies != null && !proxies.isEmpty()) {
+ for (Proxy proxy : proxies) {
+ if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) {
+ return proxy;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public PasswordAuthentication getProxyAuthentication(@NotNull SVNURL repositoryUrl) {
+ Proxy proxy = getIdeaDefinedProxy(repositoryUrl);
+ PasswordAuthentication result = null;
+
+ if (proxy != null) {
+ if (myProxyCredentialsWereReturned) {
+ showFailedAuthenticateProxy();
+ }
+ else {
+ result = getProxyAuthentication(proxy, repositoryUrl);
+ myProxyCredentialsWereReturned = result != null;
+ }
+ }
+
+ return result;
+ }
+
+ private static void showFailedAuthenticateProxy() {
+ HttpConfigurable instance = HttpConfigurable.getInstance();
+ String message = instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC
+ ? "Failed to authenticate to proxy. You can change proxy credentials in HTTP proxy settings."
+ : "Failed to authenticate to proxy.";
+
+ PopupUtil.showBalloonForActiveComponent(message, MessageType.ERROR);
+ }
+
+ @Nullable
+ private static PasswordAuthentication getProxyAuthentication(@NotNull Proxy proxy, @NotNull SVNURL repositoryUrl) {
+ PasswordAuthentication result = null;
+
+ try {
+ result = Authenticator.requestPasswordAuthentication(repositoryUrl.getHost(), ((InetSocketAddress)proxy.address()).getAddress(),
+ repositoryUrl.getPort(), repositoryUrl.getProtocol(), repositoryUrl.getHost(),
+ repositoryUrl.getProtocol(), new URL(repositoryUrl.toString()),
+ Authenticator.RequestorType.PROXY);
+ }
+ catch (MalformedURLException e) {
+ LOG.info(e);
+ }
+
+ return result;
+ }
+
+ public void reset() {
+ if (myTempDirectory != null) {
+ FileUtil.delete(myTempDirectory);
+ }
+ }
+
+ @NotNull
+ public static List<String> getKinds(final SVNURL url, boolean passwordRequest) {
+ if (passwordRequest || "http".equals(url.getProtocol())) {
+ return Collections.singletonList(ISVNAuthenticationManager.PASSWORD);
+ }
+ else if ("https".equals(url.getProtocol())) {
+ return Collections.singletonList(ISVNAuthenticationManager.SSL);
+ }
+ else if ("svn".equals(url.getProtocol())) {
+ return Collections.singletonList(ISVNAuthenticationManager.PASSWORD);
+ }
+ else if (url.getProtocol().contains("svn+")) { // todo +-
+ return Arrays.asList(ISVNAuthenticationManager.SSH, ISVNAuthenticationManager.USERNAME);
+ }
+ return Collections.singletonList(ISVNAuthenticationManager.USERNAME);
+ }
+
+ @Nullable
+ public File getSpecialConfigDir() {
+ return myTempDirectory != null ? myTempDirectory : new File(myConfiguration.getConfigurationDirectory());
+ }
+
+ public void initTmpDir(SvnConfiguration configuration) throws IOException {
+ if (myTempDirectory == null) {
+ myTempDirectory = FileUtil.createTempDirectory("tmp", "Subversion");
+ FileUtil.copyDir(new File(configuration.getConfigurationDirectory()), myTempDirectory);
+ }
+ }
+}