SVN: correct authentication fix. SVN authentication tests

This commit is contained in:
irengrig
2010-08-03 16:55:14 +04:00
parent 664609c0d7
commit ed613701ae
10 changed files with 1375 additions and 131 deletions
@@ -22,6 +22,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.util.Consumer;
import com.intellij.util.concurrency.Semaphore;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Future;
@@ -33,7 +34,7 @@ public class ConcurrentTasks<T> {
private volatile T myResult;
private volatile int myCntAlive;
private final ProgressIndicator myParentIndicator;
private final Consumer<Consumer<T>>[] myTasks;
private final List<Consumer<Consumer<T>>> myTasks;
public void compute() {
final EmptyProgressIndicator pi = new EmptyProgressIndicator() {
@@ -45,7 +46,7 @@ public class ConcurrentTasks<T> {
super.checkCanceled();
}
};
myCntAlive = myTasks.length;
myCntAlive = myTasks.size();
mySemaphore.down();
final List<Future<?>> futures = new LinkedList<Future<?>>();
@@ -85,7 +86,7 @@ public class ConcurrentTasks<T> {
if (myResultKnown) break;
if (myCntAlive <= 0) break;
pi.checkCanceled();
mySemaphore.waitFor(1000);
mySemaphore.waitFor(300);
}
// in it possible to even interrupt() threads involved, but at the moment it's better for tasks themselves to check cancel status
for (Future<?> future : futures) {
@@ -104,6 +105,12 @@ public class ConcurrentTasks<T> {
}
public ConcurrentTasks(final ProgressIndicator parentIndicator, final Consumer<Consumer<T>>... tasks) {
myParentIndicator = parentIndicator;
myTasks = Arrays.asList(tasks);
mySemaphore = new Semaphore();
}
public ConcurrentTasks(final ProgressIndicator parentIndicator, final List<Consumer<Consumer<T>>> tasks) {
myParentIndicator = parentIndicator;
myTasks = tasks;
mySemaphore = new Semaphore();
@@ -16,17 +16,25 @@
package org.jetbrains.idea.svn;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater;
import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier;
import com.intellij.ui.GuiUtils;
import com.intellij.util.containers.SoftHashMap;
import com.intellij.util.EventDispatcher;
import com.intellij.util.net.HttpConfigurable;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.auth.ProviderType;
import org.jetbrains.idea.svn.auth.SvnAuthenticationInteraction;
import org.jetbrains.idea.svn.auth.SvnAuthenticationListener;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
@@ -37,43 +45,74 @@ import org.tmatesoft.svn.core.io.SVNRepository;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.*;
/**
* @author alex
*/
public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager implements SvnAuthenticationListener {
private static final Logger LOG = Logger.getInstance(SvnAuthenticationManager.class.getName());
private final Project myProject;
private final File myConfigDirectory;
private PersistentAuthenticationProviderProxy myPersistentAuthenticationProviderProxy;
private SvnConfiguration myConfig;
private AtomicBoolean myStoreInPlainTextAnyway = new AtomicBoolean(false);
// instead of ThreadLocal
private final Set<Thread> myPlainTextAllowed;
private static final ThreadLocal<Boolean> ourJustEntered = new ThreadLocal<Boolean>();
private SvnAuthenticationInteraction myInteraction;
private final EventDispatcher<SvnAuthenticationListener> myListener;
public SvnAuthenticationManager(final Project project, final File configDirectory) {
super(configDirectory, true, null, null);
myProject = project;
myConfigDirectory = configDirectory;
myListener = EventDispatcher.create(SvnAuthenticationListener.class);
myConfig = SvnConfiguration.getInstance(myProject);
if (myPersistentAuthenticationProviderProxy != null) {
myPersistentAuthenticationProviderProxy.setProject(myProject);
}
myPlainTextAllowed = Collections.synchronizedSet(new HashSet<Thread>());
myInteraction = new MySvnAuthenticationInteraction(myProject);
}
public void addListener(final SvnAuthenticationListener listener) {
myListener.addListener(listener);
}
@Override
public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) {
myListener.getMulticaster().actualSaveWillBeTried(type, url, realm, kind);
}
@Override
public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) {
if (ProviderType.interactive.equals(type) && (! canceled)) {
ourJustEntered.set(true);
}
myListener.getMulticaster().requested(type, url, realm, kind, canceled);
}
@Override
protected ISVNAuthenticationProvider createCacheAuthenticationProvider(File authDir, String userName) {
myStoreInPlainTextAnyway = new AtomicBoolean(false);
myPersistentAuthenticationProviderProxy = new PersistentAuthenticationProviderProxy(super.createCacheAuthenticationProvider(authDir, userName), authDir);
return myPersistentAuthenticationProviderProxy;
}
@Override
public void acknowledgeAuthentication(boolean accepted,
String kind,
String realm,
SVNErrorMessage errorMessage,
SVNAuthentication authentication) throws SVNException {
try {
super.acknowledgeAuthentication(accepted, kind, realm, errorMessage, authentication);
} finally {
final Thread currentThread = Thread.currentThread();
myPlainTextAllowed.remove(currentThread);
}
}
private class PersistentAuthenticationProviderProxy implements ISVNAuthenticationProvider, IPersistentAuthenticationProvider {
private final Map<SvnAuthWrapperEqualable, Long> myRewritePreventer;
private static final long ourRefreshInterval = 6000 * 1000;
private final ISVNAuthenticationProvider myDelegate;
private final File myAuthDir;
private Project myProject;
@@ -81,7 +120,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
private PersistentAuthenticationProviderProxy(final ISVNAuthenticationProvider delegate, final File authDir) {
myDelegate = delegate;
myAuthDir = authDir;
myRewritePreventer = new SoftHashMap<SvnAuthWrapperEqualable, Long>();
}
public void setProject(Project project) {
@@ -89,9 +127,11 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
}
public SVNAuthentication requestClientAuthentication(final String kind, final SVNURL url, final String realm, final SVNErrorMessage errorMessage,
final SVNAuthentication previousAuth,
final boolean authMayBeStored) {
return myDelegate.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, false);
final SVNAuthentication previousAuth, final boolean authMayBeStored) {
final SVNAuthentication svnAuthentication =
myDelegate.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
myListener.getMulticaster().requested(ProviderType.persistent, url, realm, kind, svnAuthentication == null);
return svnAuthentication;
}
public int acceptServerAuthentication(final SVNURL url, final String realm, final Object certificate, final boolean resultMayBeStored) {
@@ -99,44 +139,37 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
}
public void saveAuthentication(final SVNAuthentication auth, final String kind, final String realm) throws SVNException {
try {
final SvnAuthWrapperEqualable newKey = new SvnAuthWrapperEqualable(auth);
final Long recent = myRewritePreventer.get(newKey);
final long currTime = System.currentTimeMillis();
File dir = new File(myAuthDir, kind);
String fileName = SVNFileUtil.computeChecksum(realm);
File authFile = new File(dir, fileName);
final Boolean fromInteractive = ourJustEntered.get();
ourJustEntered.set(null);
if (! Boolean.TRUE.equals(fromInteractive)) {
// not what user entered
return;
}
if ((! authFile.exists()) || recent == null || ((recent != null) && ((currTime - recent.longValue()) > ourRefreshInterval))) {
if ((!auth.isStorageAllowed()) || (USERNAME.equals(kind))) return;
if (auth.isStorageAllowed() && ISVNAuthenticationManager.USERNAME != kind) {
try {
GuiUtils.runOrInvokeAndWait(new Runnable() {
public void run() {
checkContinueSaveCredentials(auth, kind, realm);
saveCredentialsIfAllowed(auth, kind, realm, new Runnable() {
@Override
public void run() {
File dir = new File(myAuthDir, kind);
String fileName = SVNFileUtil.computeChecksum(realm);
File authFile = new File(dir, fileName);
myListener.getMulticaster().actualSaveWillBeTried(ProviderType.persistent, auth.getURL(), realm, kind);
try {
((IPersistentAuthenticationProvider) myDelegate).saveAuthentication(auth, kind, realm);
}
catch (SVNException e) {
if (myProject != null) {
ApplicationManager.getApplication().invokeLater(new ChangesViewBalloonProblemNotifier(myProject,
"<b>Problem when storing Subversion credentials:</b>&nbsp;" + e.getMessage(), MessageType.ERROR));
}
});
} catch (InvocationTargetException e) {
LOG.error(e);
} catch (InterruptedException e) {
LOG.error(e);
} finally {
// do not make password file readonly
setWriteable(authFile);
}
}
}
((DefaultSVNAuthenticationManager.IPersistentAuthenticationProvider)myDelegate).saveAuthentication(auth, kind, realm);
// do not make password file readonly
setWriteable(authFile);
myRewritePreventer.put(newKey, currTime);
}
}
catch (final SVNException e) {
// show notification so that user was aware his credentials were not saved
if (myProject == null) return;
ApplicationManager.getApplication().invokeLater(new ChangesViewBalloonProblemNotifier(myProject,
"<b>Problem when storing Subversion credentials:</b>&nbsp;" + e.getMessage(), MessageType.ERROR));
}
});
}
private final static int maxAttempts = 10;
@@ -163,13 +196,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
}
}
public boolean haveStoredCredentials(final String kind, final SVNURL url, final String realm, final SVNErrorMessage errorMessage,
final SVNAuthentication previousAuth) {
final SVNAuthentication result =
myPersistentAuthenticationProviderProxy.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, false);
return result != null;
}
public ISVNProxyManager getProxyManager(SVNURL url) throws SVNException {
// this code taken from default manager (changed for system properties reading)
String host = url.getHost();
@@ -358,27 +384,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
return null;
}
private static class SvnAuthWrapperEqualable extends Wrapper<SVNAuthentication> {
private SvnAuthWrapperEqualable(SVNAuthentication svnAuthentication) {
super(svnAuthentication);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (obj instanceof SvnAuthWrapperEqualable) {
return SvnAuthEquals.equals(this.getT(), ((SvnAuthWrapperEqualable) obj).getT());
}
return false;
}
@Override
public int hashCode() {
return SvnAuthEquals.hashCode(getT());
}
}
private void setPropertyForHost(final String host, final String property, final String value) {
final SVNConfigFile userConfig = new SVNConfigFile(new File(myConfigDirectory, "servers"));
@@ -418,84 +423,176 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
return "yes".equalsIgnoreCase(storeAuthCreds) || "on".equalsIgnoreCase(storeAuthCreds) || "true".equalsIgnoreCase(storeAuthCreds);
}
public boolean checkContinueSaveCredentials(final SVNAuthentication auth, final String kind, final String realm) {
public void saveCredentialsIfAllowed(final SVNAuthentication auth, final String kind, final String realm,
final Runnable saveRunnable) {
final SVNURL url = auth.getURL();
final String storeCredentials = getConfigFile().getPropertyValue("auth", "store-auth-creds");
if ((Boolean.FALSE.equals(isAuthStorageEnabledMy(url))) || (! isTurned(storeCredentials))) {
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store credentials: forbidden by \"store-auth-creds=no\"", MessageType.ERROR);
return false;
myInteraction.warnOnAuthStorageDisabled(url);
return;
}
final boolean passwordStorageEnabled = isStorePasswords(url);
// check can store
if ((! ISVNAuthenticationManager.SSL.equals(kind)) && (! passwordStorageEnabled)) {
// but it should be
//userConfig.setPropertyValue("auth", "store-passwords", "yes", true);
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store password: forbidden by \"store-passwords=no\"", MessageType.ERROR);
return false;
myInteraction.warnOnPasswordStorageDisabled(url);
return;
}
if (ISVNAuthenticationManager.SSL.equals(kind) && (! isStoreSSLClientCertificatePassphrases(url))) {
//setPropertyForHost(url.getHost(), "store-ssl-client-cert-pp", "yes");
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store passphrase: forbidden by \"store-ssl-client-cert-pp=no\"", MessageType.ERROR);
return false;
myInteraction.warnOnSSLPassphraseStorageDisabled(url);
return;
}
// check can encrypt
if (!(SystemInfo.isWindows && SVNJNAUtil.isWinCryptEnabled())) {
if (ISVNAuthenticationManager.SSL.equals(kind)) {
try {
try {
if (ISVNAuthenticationManager.SSL.equals(kind)) {
if (!isStorePlainTextPassphrases(realm, auth)) {
return askToStoreUnencrypted("Store the passphrase in plaintext?",
String.format("Your passphrase for client certificate:\n%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?",
((SVNSSLAuthentication)auth).getCertificateFile().getPath()));
promptAndSaveWhenWeLackEncryption(saveRunnable,
new Getter<Boolean>() {
@Override
public Boolean get() {
return myInteraction.promptForSSLPlaintextPassphraseSaving(url, realm, ((SVNSSLAuthentication)auth).getCertificateFile());
}
});
return;
}
} catch (SVNException e) {
LOG.error(e); // should not occur, anyway means not allowed
}
} else {
try {
} else {
if (!isStorePlainTextPasswords(realm, auth)) {
return askToStoreUnencrypted("Store the password in plaintext?",
String.format("Your password for authentication realm:\n%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?",
realm));
promptAndSaveWhenWeLackEncryption(saveRunnable,
new Getter<Boolean>() {
@Override
public Boolean get() {
return myInteraction.promptForPlaintextPasswordSaving(url, realm);
}
});
return;
}
} catch (SVNException e) {
LOG.error(e);
}
} catch (SVNException e) {
LOG.info(e);
return;
}
}
return true;
saveRunnable.run();
}
@Override
protected boolean isStorePlainTextPasswords(String realm, SVNAuthentication auth) throws SVNException {
// normally check user preferences, but the user may override them via the dialog from checkContinueSaveCredentials()
return myStoreInPlainTextAnyway.get() || super.isStorePlainTextPasswords(realm, auth);
return myPlainTextAllowed.contains(Thread.currentThread()) || super.isStorePlainTextPasswords(realm, auth);
}
@Override
protected boolean isStorePlainTextPassphrases(String realm, SVNAuthentication auth) throws SVNException {
return myStoreInPlainTextAnyway.get() || super.isStorePlainTextPassphrases(realm, auth);
return myPlainTextAllowed.contains(Thread.currentThread()) || super.isStorePlainTextPassphrases(realm, auth);
}
private ModalityState getCurrent() {
if (ApplicationManager.getApplication().isDispatchThread()) {
return ModalityState.current();
}
final ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator();
if (pi == null) {
return ModalityState.defaultModalityState();
}
return pi.getModalityState();
}
private void promptAndSaveWhenWeLackEncryption(
final Runnable saveRunnable,
final Getter<Boolean> prompt) {
final Runnable actualSave = new Runnable() {
@Override
public void run() {
final Thread currentThread = Thread.currentThread();
myPlainTextAllowed.add(currentThread);
try {
saveRunnable.run();
}
finally {
myPlainTextAllowed.remove(currentThread);
}
}
};
if (myInteraction.promptInAwt()) {
new AbstractCalledLater(myProject, getCurrent()) {
@Override
public void run() {
final boolean saveOnce = Boolean.TRUE.equals(prompt.get());
if (saveOnce) {
ApplicationManager.getApplication().executeOnPooledThread(actualSave);
} else {
}
}
}.callMe();
} else {
final boolean saveOnce = Boolean.TRUE.equals(prompt.get());
if (saveOnce) {
actualSave.run();
}
}
}
/**
* Shows a yes/no question whether user wants to store his password in plain text and returns his answer.
* Also updates the 'myStoreInPlainTextAnyway' variable correspondingly.
* This method shuld be called from the event dispatching thread.
* @param title title of the questioning dialog.
* @param message questioning message to be displayed.
* @return true if user agrees to store his password in plaintext, false if he doesn't.
*/
@CalledInAwt
private boolean askToStoreUnencrypted(String title, String message) {
final int answer = Messages.showYesNoDialog(myProject, message, title, Messages.getQuestionIcon());
if (answer == 0) {
myStoreInPlainTextAnyway.set(true);
} else {
myStoreInPlainTextAnyway.set(false);
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store password", MessageType.ERROR);
}
return myStoreInPlainTextAnyway.get();
return answer == 0;
}
}
public void setInteraction(SvnAuthenticationInteraction interaction) {
myInteraction = interaction;
}
private static class MySvnAuthenticationInteraction implements SvnAuthenticationInteraction {
private final Project myProject;
private MySvnAuthenticationInteraction(Project project) {
myProject = project;
}
@Override
public void warnOnAuthStorageDisabled(SVNURL url) {
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store credentials: forbidden by \"store-auth-creds=no\"", MessageType.ERROR);
}
@Override
public void warnOnPasswordStorageDisabled(SVNURL url) {
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store password: forbidden by \"store-passwords=no\"", MessageType.ERROR);
}
@Override
public void warnOnSSLPassphraseStorageDisabled(SVNURL url) {
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store passphrase: forbidden by \"store-ssl-client-cert-pp=no\"", MessageType.ERROR);
}
@Override
public boolean promptForPlaintextPasswordSaving(SVNURL url, String realm) {
final int answer = Messages.showYesNoDialog(myProject, String.format("Your password for authentication realm:\n" +
"%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?", realm),
"Store the password in plaintext?", Messages.getQuestionIcon());
return answer == 0;
}
@Override
public boolean promptInAwt() {
return true;
}
@Override
public boolean promptForSSLPlaintextPassphraseSaving(SVNURL url, String realm, File certificateFile) {
final int answer = Messages.showYesNoDialog(myProject, String.format("Your passphrase for client certificate:\n%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?",
certificateFile.getPath()),
"Store the passphrase in plaintext?", Messages.getQuestionIcon());
return answer == 0;
}
}
}
@@ -93,6 +93,7 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
private final Map<File, MergeRootInfo> myMergeRootInfos = new HashMap<File, MergeRootInfo>();
private final Map<File, UpdateRootInfo> myUpdateRootInfos = new HashMap<File, UpdateRootInfo>();
private final List<AnnotationListener> myAnnotationListeners;
private SvnInteractiveAuthenticationProvider myInteractiveProvider;
public static SvnConfiguration getInstance(Project project) {
return project.getComponent(SvnConfiguration.class);
@@ -200,6 +201,7 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
myAuthManager = null;
myPassiveAuthManager = null;
myInteractiveManager = null;
myInteractiveProvider = null;
RUNTIME_AUTH_CACHE.clear();
}
@@ -209,6 +211,7 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
myAuthManager = null;
myPassiveAuthManager = null;
myInteractiveManager = null;
myInteractiveProvider = null;
RUNTIME_AUTH_CACHE.clear();
}
@@ -224,8 +227,9 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
if (myAuthManager == null) {
// reloaded when configuration directory changes
myAuthManager = new SvnAuthenticationManager(myProject, new File(getConfigurationDirectory()));
myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, getInteractiveManager(svnVcs)));
myAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
// to init
myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, myInteractiveProvider));
myAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
}
return myAuthManager;
}
@@ -242,7 +246,8 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
if (myInteractiveManager == null) {
myInteractiveManager = new SvnAuthenticationManager(myProject, new File(getConfigurationDirectory()));
myInteractiveManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
myInteractiveManager.setAuthenticationProvider(new SvnInteractiveAuthenticationProvider(svnVcs, myInteractiveManager));
myInteractiveProvider = new SvnInteractiveAuthenticationProvider(svnVcs, myInteractiveManager);
myInteractiveManager.setAuthenticationProvider(myInteractiveProvider);
}
return myInteractiveManager;
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2010 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;
public enum ProviderType {
dumb,
interactive,
persistent,
memory_cache
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2010 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 org.tmatesoft.svn.core.SVNURL;
import java.io.File;
public interface SvnAuthenticationInteraction {
void warnOnAuthStorageDisabled(final SVNURL url);
void warnOnPasswordStorageDisabled(final SVNURL url);
void warnOnSSLPassphraseStorageDisabled(final SVNURL url);
boolean promptForSSLPlaintextPassphraseSaving(final SVNURL url, String realm, File certificateFile);
boolean promptForPlaintextPasswordSaving(final SVNURL url, String realm);
boolean promptInAwt();
}
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2010 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 org.tmatesoft.svn.core.SVNURL;
import java.util.EventListener;
public interface SvnAuthenticationListener extends EventListener {
void requested(final ProviderType type, final SVNURL url, String realm, String kind, boolean canceled);
void actualSaveWillBeTried(final ProviderType type, final SVNURL url, String realm, String kind);
}
@@ -20,7 +20,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.ui.GuiUtils;
import com.intellij.util.SystemProperties;
import org.jetbrains.idea.svn.SvnAuthenticationManager;
import org.jetbrains.idea.svn.SvnAuthenticationNotifier;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.SVNErrorMessage;
@@ -40,12 +39,12 @@ public class SvnAuthenticationProvider implements ISVNAuthenticationProvider {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider");
private final Project myProject;
private final SvnAuthenticationNotifier myAuthenticationNotifier;
private final SvnInteractiveAuthenticationProvider mySvnInteractiveAuthenticationProvider;
private final ISVNAuthenticationProvider mySvnInteractiveAuthenticationProvider;
public SvnAuthenticationProvider(final SvnVcs svnVcs, final SvnAuthenticationManager manager) {
public SvnAuthenticationProvider(final SvnVcs svnVcs, final ISVNAuthenticationProvider provider) {
myProject = svnVcs.getProject();
myAuthenticationNotifier = svnVcs.getAuthNotifier();
mySvnInteractiveAuthenticationProvider = new SvnInteractiveAuthenticationProvider(svnVcs, manager);
mySvnInteractiveAuthenticationProvider = provider;
}
private void log(final String s) {
@@ -24,6 +24,7 @@ import org.jetbrains.idea.svn.SvnAuthenticationManager;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnConfiguration;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.auth.ProviderType;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.*;
@@ -35,11 +36,9 @@ public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationP
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider");
private final Project myProject;
private static final ThreadLocal<MyCallState> myCallState = new ThreadLocal<MyCallState>();
private final SvnVcs myVcs;
private final SvnAuthenticationManager myManager;
public SvnInteractiveAuthenticationProvider(final SvnVcs vcs, SvnAuthenticationManager manager) {
myVcs = vcs;
myManager = manager;
myProject = vcs.getProject();
}
@@ -176,6 +175,9 @@ public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationP
final boolean wasCanceled = result[0] == null;
callState.setWasCancelled(wasCanceled);
if (! ISVNAuthenticationManager.USERNAME.equals(kind)) {
myManager.requested(ProviderType.interactive, url, realm, kind, wasCanceled);
}
return result[0];
}
@@ -16,6 +16,9 @@
package org.jetbrains.idea.svn.dialogs.browserCache;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Pair;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.dialogs.RepositoryTreeNode;
@@ -45,7 +48,7 @@ class RepositoryLoader extends Loader {
final Pair<RepositoryTreeNode, Expander> data = new Pair<RepositoryTreeNode, Expander>(node, afterRefreshExpander);
if (! myQueueProcessorActive) {
ApplicationManager.getApplication().executeOnPooledThread(new LoadTask(data));
startLoadTask(data);
myQueueProcessorActive = true;
} else {
myLoadQueue.offer(data);
@@ -74,10 +77,25 @@ class RepositoryLoader extends Loader {
// ignore if node is already disposed
startNext();
} else {
ApplicationManager.getApplication().executeOnPooledThread(new LoadTask(data));
startLoadTask(data);
}
}
private void startLoadTask(final Pair<RepositoryTreeNode, Expander> data) {
final ModalityState state = ModalityState.current();
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
ProgressManager.getInstance().runProcess(new LoadTask(data), new EmptyProgressIndicator() {
@Override
public ModalityState getModalityState() {
return state;
}
});
}
});
}
public void forceRefresh(final String repositoryRootUrl) {
// ? remove
}
File diff suppressed because it is too large Load Diff