mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
SVN: correct authentication fix. SVN authentication tests
This commit is contained in:
@@ -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> " + 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> " + 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) {
|
||||
|
||||
+4
-2
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
+20
-2
@@ -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
Reference in New Issue
Block a user