Skip to content

Commit 00244fa

Browse files
authored
Merge pull request #871 from mjcheetham/generic-ui
Add generic username/password UI to all platforms
2 parents 4a73bc7 + feb02ce commit 00244fa

37 files changed

+886
-165
lines changed

Git-Credential-Manager.sln

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitLab.UI.Avalonia", "src\s
6767
EndProject
6868
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitLab.UI.Windows", "src\windows\GitLab.UI.Windows\GitLab.UI.Windows.csproj", "{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}"
6969
EndProject
70+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Git-Credential-Manager.UI.Avalonia", "src\shared\Git-Credential-Manager.UI.Avalonia\Git-Credential-Manager.UI.Avalonia.csproj", "{35659127-8859-4DB9-8DD6-A08C1952632E}"
71+
EndProject
72+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Git-Credential-Manager.UI.Windows", "src\windows\Git-Credential-Manager.UI.Windows\Git-Credential-Manager.UI.Windows.csproj", "{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}"
73+
EndProject
7074
Global
7175
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7276
Debug|Any CPU = Debug|Any CPU
@@ -469,6 +473,32 @@ Global
469473
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
470474
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
471475
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
476+
{35659127-8859-4DB9-8DD6-A08C1952632E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
477+
{35659127-8859-4DB9-8DD6-A08C1952632E}.Debug|Any CPU.Build.0 = Debug|Any CPU
478+
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
479+
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacDebug|Any CPU.Build.0 = Debug|Any CPU
480+
{35659127-8859-4DB9-8DD6-A08C1952632E}.Release|Any CPU.ActiveCfg = Release|Any CPU
481+
{35659127-8859-4DB9-8DD6-A08C1952632E}.Release|Any CPU.Build.0 = Release|Any CPU
482+
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
483+
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
484+
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
485+
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU
486+
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxRelease|Any CPU.ActiveCfg = Release|Any CPU
487+
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxRelease|Any CPU.Build.0 = Release|Any CPU
488+
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
489+
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacRelease|Any CPU.Build.0 = Release|Any CPU
490+
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
491+
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
492+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
493+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
494+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
495+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
496+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
497+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
498+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsRelease|Any CPU.ActiveCfg = Debug|Any CPU
499+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
500+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
501+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.LinuxRelease|Any CPU.ActiveCfg = Release|Any CPU
472502
EndGlobalSection
473503
GlobalSection(SolutionProperties) = preSolution
474504
HideSolutionNode = FALSE
@@ -505,6 +535,8 @@ Global
505535
{9AFD88E2-7E2C-46DA-9D38-4342086426D3} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
506536
{47186A50-8889-4FC7-8A05-F9FCE7F8F4AE} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
507537
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E} = {66722747-1B61-40E4-A89B-1AC8E6D62EA9}
538+
{35659127-8859-4DB9-8DD6-A08C1952632E} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
539+
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C} = {66722747-1B61-40E4-A89B-1AC8E6D62EA9}
508540
EndGlobalSection
509541
GlobalSection(ExtensibilityGlobals) = postSolution
510542
SolutionGuid = {0EF9FC65-E6BA-45D4-A455-262A9EA4366B}

src/linux/Packaging.Linux/build.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )"
4747
SRC="$ROOT/src"
4848
OUT="$ROOT/out"
4949
GCM_SRC="$SRC/shared/Git-Credential-Manager"
50+
GCM_UI_SRC="$SRC/shared/Git-Credential-Manager.UI.Avalonia"
5051
BITBUCKET_UI_SRC="$SRC/shared/Atlassian.Bitbucket.UI.Avalonia"
5152
GITHUB_UI_SRC="$SRC/shared/GitHub.UI.Avalonia"
5253
GITLAB_UI_SRC="$SRC/shared/GitLab.UI.Avalonia"
@@ -120,6 +121,15 @@ $DOTNET_ROOT/dotnet publish "$GCM_SRC" \
120121
-p:PublishSingleFile=true \
121122
--output="$(make_absolute "$PAYLOAD")" || exit 1
122123

124+
echo "Publishing core UI helper..."
125+
$DOTNET_ROOT/dotnet publish "$GCM_UI_SRC" \
126+
--configuration="$CONFIGURATION" \
127+
--framework="$FRAMEWORK" \
128+
--runtime="$RUNTIME" \
129+
--self-contained=true \
130+
-p:PublishSingleFile=true \
131+
--output="$(make_absolute "$PAYLOAD")" || exit 1
132+
123133
echo "Publishing Bitbucket UI helper..."
124134
$DOTNET_ROOT/dotnet publish "$BITBUCKET_UI_SRC" \
125135
--configuration="$CONFIGURATION" \

src/osx/Installer.Mac/layout.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ SRC="$ROOT/src"
2121
OUT="$ROOT/out"
2222
INSTALLER_SRC="$SRC/osx/Installer.Mac"
2323
GCM_SRC="$SRC/shared/Git-Credential-Manager"
24+
GCM_UI_SRC="$SRC/shared/Git-Credential-Manager.UI.Avalonia"
2425
BITBUCKET_UI_SRC="$SRC/shared/Atlassian.Bitbucket.UI.Avalonia"
2526
GITHUB_UI_SRC="$SRC/shared/GitHub.UI.Avalonia"
2627
GITLAB_UI_SRC="$SRC/shared/GitLab.UI.Avalonia"
@@ -104,6 +105,16 @@ dotnet publish "$GCM_SRC" \
104105
--self-contained \
105106
--output="$(make_absolute "$PAYLOAD")" || exit 1
106107

108+
echo "Publishing core UI helper..."
109+
dotnet publish "$GCM_UI_SRC" \
110+
--no-restore \
111+
-m:1 \
112+
--configuration="$CONFIGURATION" \
113+
--framework="$FRAMEWORK" \
114+
--runtime="$RUNTIME" \
115+
--self-contained \
116+
--output="$(make_absolute "$PAYLOAD")" || exit 1
117+
107118
echo "Publishing Bitbucket UI helper..."
108119
dotnet publish "$BITBUCKET_UI_SRC" \
109120
--no-restore \

src/shared/Core.Tests/Authentication/BasicAuthenticationTests.cs

Lines changed: 47 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
24
using GitCredentialManager.Authentication;
35
using GitCredentialManager.Tests.Objects;
46
using Moq;
@@ -14,11 +16,11 @@ public void BasicAuthentication_GetCredentials_NullResource_ThrowsException()
1416
var context = new TestCommandContext();
1517
var basicAuth = new BasicAuthentication(context);
1618

17-
Assert.Throws<ArgumentNullException>(() => basicAuth.GetCredentials(null));
19+
Assert.ThrowsAsync<ArgumentNullException>(() => basicAuth.GetCredentialsAsync(null));
1820
}
1921

2022
[Fact]
21-
public void BasicAuthentication_GetCredentials_NonDesktopSession_ResourceAndUserName_PasswordPromptReturnsCredentials()
23+
public async Task BasicAuthentication_GetCredentials_NonDesktopSession_ResourceAndUserName_PasswordPromptReturnsCredentials()
2224
{
2325
const string testResource = "https://example.com";
2426
const string testUserName = "john.doe";
@@ -29,14 +31,14 @@ public void BasicAuthentication_GetCredentials_NonDesktopSession_ResourceAndUser
2931

3032
var basicAuth = new BasicAuthentication(context);
3133

32-
ICredential credential = basicAuth.GetCredentials(testResource, testUserName);
34+
ICredential credential = await basicAuth.GetCredentialsAsync(testResource, testUserName);
3335

3436
Assert.Equal(testUserName, credential.Account);
3537
Assert.Equal(testPassword, credential.Password);
3638
}
3739

3840
[Fact]
39-
public void BasicAuthentication_GetCredentials_NonDesktopSession_Resource_UserPassPromptReturnsCredentials()
41+
public async Task BasicAuthentication_GetCredentials_NonDesktopSession_Resource_UserPassPromptReturnsCredentials()
4042
{
4143
const string testResource = "https://example.com";
4244
const string testUserName = "john.doe";
@@ -48,7 +50,7 @@ public void BasicAuthentication_GetCredentials_NonDesktopSession_Resource_UserPa
4850

4951
var basicAuth = new BasicAuthentication(context);
5052

51-
ICredential credential = basicAuth.GetCredentials(testResource);
53+
ICredential credential = await basicAuth.GetCredentialsAsync(testResource);
5254

5355
Assert.Equal(testUserName, credential.Account);
5456
Assert.Equal(testPassword, credential.Password);
@@ -67,100 +69,78 @@ public void BasicAuthentication_GetCredentials_NonDesktopSession_NoTerminalPromp
6769

6870
var basicAuth = new BasicAuthentication(context);
6971

70-
Assert.Throws<InvalidOperationException>(() => basicAuth.GetCredentials(testResource));
72+
Assert.ThrowsAsync<InvalidOperationException>(() => basicAuth.GetCredentialsAsync(testResource));
7173
}
7274

73-
[PlatformFact(Platforms.Windows)]
74-
public void BasicAuthentication_GetCredentials_DesktopSession_Resource_UserPassPromptReturnsCredentials()
75+
[Fact]
76+
public async Task BasicAuthentication_GetCredentials_DesktopSession_CallsHelper()
7577
{
7678
const string testResource = "https://example.com";
7779
const string testUserName = "john.doe";
7880
const string testPassword = "letmein123"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Fake credential")]
7981

8082
var context = new TestCommandContext
8183
{
82-
SessionManager = {IsDesktopSession = true},
83-
SystemPrompts =
84-
{
85-
CredentialPrompt = (resource, userName) =>
86-
{
87-
Assert.Equal(testResource, resource);
88-
Assert.Null(userName);
89-
90-
return new GitCredential(testUserName, testPassword);
91-
}
92-
}
84+
SessionManager = {IsDesktopSession = true}
9385
};
9486

95-
var basicAuth = new BasicAuthentication(context);
96-
97-
ICredential credential = basicAuth.GetCredentials(testResource);
98-
99-
Assert.NotNull(credential);
100-
Assert.Equal(testUserName, credential.Account);
101-
Assert.Equal(testPassword, credential.Password);
102-
}
103-
104-
[PlatformFact(Platforms.Windows)]
105-
public void BasicAuthentication_GetCredentials_DesktopSession_ResourceAndUser_PassPromptReturnsCredentials()
106-
{
107-
const string testResource = "https://example.com";
108-
const string testUserName = "john.doe";
109-
const string testPassword = "letmein123"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Fake credential")]
110-
111-
var context = new TestCommandContext
112-
{
113-
SessionManager = {IsDesktopSession = true},
114-
SystemPrompts =
115-
{
116-
CredentialPrompt = (resource, userName) =>
87+
context.FileSystem.Files["/usr/local/bin/git-credential-manager-ui"] = new byte[0];
88+
context.FileSystem.Files[@"C:\Program Files\Git Credential Manager Core\git-credential-manager-ui.exe"] = new byte[0];
89+
90+
var auth = new Mock<BasicAuthentication>(MockBehavior.Strict, context);
91+
auth.Setup(x => x.InvokeHelperAsync(
92+
It.IsAny<string>(),
93+
$"basic --resource {testResource}",
94+
It.IsAny<IDictionary<string, string>>(),
95+
It.IsAny<System.Threading.CancellationToken>()))
96+
.ReturnsAsync(
97+
new Dictionary<string, string>
11798
{
118-
Assert.Equal(testResource, resource);
119-
Assert.Equal(testUserName, userName);
120-
121-
return new GitCredential(testUserName, testPassword);
99+
["username"] = testUserName,
100+
["password"] = testPassword
122101
}
123-
}
124-
};
125-
126-
var basicAuth = new BasicAuthentication(context);
102+
);
127103

128-
ICredential credential = basicAuth.GetCredentials(testResource, testUserName);
104+
ICredential credential = await auth.Object.GetCredentialsAsync(testResource);
129105

130106
Assert.NotNull(credential);
131107
Assert.Equal(testUserName, credential.Account);
132108
Assert.Equal(testPassword, credential.Password);
133109
}
134110

135-
[PlatformFact(Platforms.Windows)]
136-
public void BasicAuthentication_GetCredentials_DesktopSession_ResourceAndUser_PassPromptDiffUserReturnsCredentials()
111+
[Fact]
112+
public async Task BasicAuthentication_GetCredentials_DesktopSession_UserName_CallsHelper()
137113
{
138114
const string testResource = "https://example.com";
139115
const string testUserName = "john.doe";
140-
const string newUserName = "jane.doe";
141116
const string testPassword = "letmein123"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Fake credential")]
142117

143118
var context = new TestCommandContext
144119
{
145-
SessionManager = {IsDesktopSession = true},
146-
SystemPrompts =
147-
{
148-
CredentialPrompt = (resource, userName) =>
149-
{
150-
Assert.Equal(testResource, resource);
151-
Assert.Equal(testUserName, userName);
152-
153-
return new GitCredential(newUserName, testPassword);
154-
}
155-
}
120+
SessionManager = {IsDesktopSession = true}
156121
};
157122

158-
var basicAuth = new BasicAuthentication(context);
123+
context.FileSystem.Files["/usr/local/bin/git-credential-manager-ui"] = new byte[0];
124+
context.FileSystem.Files[@"C:\Program Files\Git Credential Manager Core\git-credential-manager-ui.exe"] = new byte[0];
125+
126+
var auth = new Mock<BasicAuthentication>(MockBehavior.Strict, context);
127+
auth.Setup(x => x.InvokeHelperAsync(
128+
It.IsAny<string>(),
129+
$"basic --resource {testResource} --username {testUserName}",
130+
It.IsAny<IDictionary<string, string>>(),
131+
It.IsAny<System.Threading.CancellationToken>()))
132+
.ReturnsAsync(
133+
new Dictionary<string, string>
134+
{
135+
["username"] = testUserName,
136+
["password"] = testPassword
137+
}
138+
);
159139

160-
ICredential credential = basicAuth.GetCredentials(testResource, testUserName);
140+
ICredential credential = await auth.Object.GetCredentialsAsync(testResource, testUserName);
161141

162142
Assert.NotNull(credential);
163-
Assert.Equal(newUserName, credential.Account);
143+
Assert.Equal(testUserName, credential.Account);
164144
Assert.Equal(testPassword, credential.Password);
165145
}
166146
}

0 commit comments

Comments
 (0)