Skip to content

Commit f67c6c7

Browse files
authored
Merge pull request #1913 from Hosch250/Issue1910
Allow passing credentials into Clone to clone a private repository
2 parents 16155cc + 46a560b commit f67c6c7

File tree

7 files changed

+65
-15
lines changed

7 files changed

+65
-15
lines changed

RetailCoder.VBE/UI/SourceControl/SourceControlViewViewModel.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,15 @@ private void DismissErrorMessage()
569569

570570
public void CreateProviderWithCredentials(SecureCredentials credentials)
571571
{
572-
Provider = _providerFactory.CreateProvider(_vbe.ActiveVBProject, Provider.CurrentRepository, credentials, _wrapperFactory);
572+
if (!_isCloning)
573+
{
574+
Provider = _providerFactory.CreateProvider(_vbe.ActiveVBProject, Provider.CurrentRepository, credentials,
575+
_wrapperFactory);
576+
}
577+
else
578+
{
579+
CloneRepo(credentials);
580+
}
573581
}
574582

575583
private void InitRepo()
@@ -667,15 +675,17 @@ private void OpenRepo()
667675
}
668676
}
669677

670-
private void CloneRepo()
678+
private bool _isCloning = false;
679+
private void CloneRepo(SecureCredentials credentials = null)
671680
{
681+
_isCloning = true;
672682
OnOpenRepoStarted();
673683

674684
Logger.Trace("Cloning repo");
675685
try
676686
{
677687
_provider = _providerFactory.CreateProvider(_vbe.ActiveVBProject);
678-
var repo = _provider.Clone(CloneRemotePath, LocalDirectory);
688+
var repo = _provider.Clone(CloneRemotePath, LocalDirectory, credentials);
679689
AddOrUpdateLocalPathConfig(new Repository
680690
{
681691
Id = _vbe.ActiveVBProject.HelpFile,
@@ -687,10 +697,17 @@ private void CloneRepo()
687697
}
688698
catch (SourceControlException ex)
689699
{
700+
const string unauthorizedMessage = "Request failed with status code: 401";
701+
if (ex.InnerException.Message != unauthorizedMessage)
702+
{
703+
_isCloning = false;
704+
}
705+
690706
ViewModel_ErrorThrown(null, new ErrorEventArgs(ex.Message, ex.InnerException.Message, NotificationType.Error));
691707
return;
692708
}
693709

710+
_isCloning = false;
694711
OnOpenRepoCompleted();
695712
CloseCloneRepoGrid();
696713

Rubberduck.SourceControl/GitProvider.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,6 @@ public GitProvider(VBProject project, IRepository repository, string userName, s
5353
_credentialsHandler = (url, user, cred) => _credentials;
5454
}
5555

56-
public GitProvider(VBProject project, IRepository repository, ICredentials<string> credentials, ICodePaneWrapperFactory wrapperFactory)
57-
:this(project, repository, credentials.Username, credentials.Password, wrapperFactory)
58-
{ }
59-
6056
public GitProvider(VBProject project, IRepository repository, ICredentials<SecureString> credentials, ICodePaneWrapperFactory wrapperFactory)
6157
: this(project, repository, wrapperFactory)
6258
{
@@ -104,12 +100,28 @@ public override IList<ICommit> UnsyncedRemoteCommits
104100
get { return _unsyncedRemoteCommits; }
105101
}
106102

107-
public override IRepository Clone(string remotePathOrUrl, string workingDirectory)
103+
public override IRepository Clone(string remotePathOrUrl, string workingDirectory, SecureCredentials credentials = null)
108104
{
109105
try
110106
{
111107
var name = GetProjectNameFromDirectory(remotePathOrUrl);
112-
LibGit2Sharp.Repository.Clone(remotePathOrUrl, workingDirectory);
108+
109+
if (credentials == null)
110+
{
111+
LibGit2Sharp.Repository.Clone(remotePathOrUrl, workingDirectory);
112+
}
113+
else
114+
{
115+
var credentialsHandler = new CredentialsHandler((url, usernameFromUrl, types) => new SecureUsernamePasswordCredentials
116+
{
117+
Username = credentials.Username,
118+
Password = credentials.Password
119+
});
120+
121+
var options = new CloneOptions {CredentialsProvider = credentialsHandler};
122+
LibGit2Sharp.Repository.Clone(remotePathOrUrl, workingDirectory, options);
123+
}
124+
113125
return new Repository(name, workingDirectory, remotePathOrUrl);
114126
}
115127
catch (LibGit2SharpException ex)

Rubberduck.SourceControl/ISourceControlProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ public interface ISourceControlProvider
1919
/// <summary>Clone a remote repository.</summary>
2020
/// <param name="remotePathOrUrl">Either a Url "https://github.com/retailcoder/Rubberduck.git" or a UNC path. "//server/share/path/to/repo.git"</param>
2121
/// <param name="workingDirectory">Directory the repository will be cloned to.</param>
22+
/// <param name="credentials">Credentials required if repository is private.</param>
2223
/// <returns>Newly cloned repository.</returns>
23-
IRepository Clone(string remotePathOrUrl, string workingDirectory);
24+
IRepository Clone(string remotePathOrUrl, string workingDirectory, SecureCredentials credentials = null);
2425

2526
/// <summary>
2627
/// Creates a new repository in/from the given directory.

Rubberduck.SourceControl/Interop/GitProvider.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.ComponentModel;
44
using System.Linq;
55
using System.Runtime.InteropServices;
6+
using System.Security;
67
using Microsoft.Vbe.Interop;
78
using Rubberduck.VBEditor.VBEInterfaces.RubberduckCodePane;
89

@@ -67,5 +68,24 @@ public override void Commit(string message)
6768
Stage(filePaths);
6869
base.Commit(message);
6970
}
71+
/// <summary>
72+
/// For use by COM API only.
73+
/// </summary>
74+
/// <param name="remotePathOrUrl"></param>
75+
/// <param name="workingDirectory"></param>
76+
/// <param name="credentials"></param>
77+
/// <returns></returns>
78+
public IRepository Clone(string remotePathOrUrl, string workingDirectory, Credentials credentials)
79+
{
80+
var password = new SecureString();
81+
foreach (var chr in credentials.Password)
82+
{
83+
password.AppendChar(chr);
84+
}
85+
86+
credentials.Password = string.Empty;
87+
88+
return base.Clone(remotePathOrUrl, workingDirectory, new SecureCredentials(credentials.Username, password));
89+
}
7090
}
7191
}

Rubberduck.SourceControl/Interop/ISourceControlProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public interface ISourceControlProvider
1919

2020
[DispId(3)]
2121
[Description("Clones a remote repository to the local file system.")]
22-
IRepository Clone(string remotePathOrUrl, string workingDirectory);
22+
IRepository Clone(string remotePathOrUrl, string workingDirectory, Credentials credentials = null);
2323

2424
[DispId(4)]
2525
[Description("Creates a new repository in/from the specified directory.")]

Rubberduck.SourceControl/SourceControlProviderBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ protected SourceControlProviderBase(VBProject project, IRepository repository, I
3333
public abstract IList<ICommit> UnsyncedRemoteCommits { get; }
3434
public bool NotifyExternalFileChanges { get; protected set; }
3535
public bool HandleVbeSinkEvents { get; protected set; }
36-
public abstract IRepository Clone(string remotePathOrUrl, string workingDirectory);
36+
public abstract IRepository Clone(string remotePathOrUrl, string workingDirectory, SecureCredentials credentials = null);
3737
public abstract void Push();
3838
public abstract void Fetch(string remoteName);
3939
public abstract void AddFile(string filePath);

RubberduckTests/SourceControl/SourceControlViewModelTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public void InitializeMocks()
7070
_provider.SetupGet(git => git.UnsyncedLocalCommits).Returns(new List<ICommit>());
7171
_provider.SetupGet(git => git.UnsyncedRemoteCommits).Returns(new List<ICommit>());
7272
_provider.Setup(git => git.InitVBAProject(It.IsAny<string>())).Returns(GetDummyRepo());
73-
_provider.Setup(git => git.Clone(It.IsAny<string>(), It.IsAny<string>())).Returns(GetDummyRepo());
73+
_provider.Setup(git => git.Clone(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<SecureCredentials>())).Returns(GetDummyRepo());
7474
_provider.Setup(git => git.CurrentRepository).Returns(GetDummyRepo());
7575

7676
_providerFactory = new Mock<ISourceControlProviderFactory>();
@@ -647,7 +647,7 @@ public void CloneRepo_ClonesRepo()
647647
_vm.CloneRepoOkButtonCommand.Execute(null);
648648

649649
//Assert
650-
_provider.Verify(git => git.Clone(remotePath, localDirectory));
650+
_provider.Verify(git => git.Clone(remotePath, localDirectory, null));
651651
}
652652

653653
[TestMethod]
@@ -741,7 +741,7 @@ public void CloneRepo_ActionFailedEventIsRaised()
741741
SetupValidVbProject();
742742
SetupVM();
743743

744-
_provider.Setup(p => p.Clone(It.IsAny<string>(), It.IsAny<string>()))
744+
_provider.Setup(p => p.Clone(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<SecureCredentials>()))
745745
.Throws(
746746
new SourceControlException("A source control exception was thrown.",
747747
new LibGit2Sharp.LibGit2SharpException("With an inner libgit2sharp exception"))

0 commit comments

Comments
 (0)