Skip to content

Commit

Permalink
Merge pull request #871 from mjcheetham/generic-ui
Browse files Browse the repository at this point in the history
Add generic username/password UI to all platforms
  • Loading branch information
mjcheetham committed Sep 28, 2022
2 parents 4a73bc7 + feb02ce commit 00244fa
Show file tree
Hide file tree
Showing 37 changed files with 886 additions and 165 deletions.
32 changes: 32 additions & 0 deletions Git-Credential-Manager.sln
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitLab.UI.Avalonia", "src\s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitLab.UI.Windows", "src\windows\GitLab.UI.Windows\GitLab.UI.Windows.csproj", "{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}"
EndProject
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}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -469,6 +473,32 @@ Global
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacDebug|Any CPU.Build.0 = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.Release|Any CPU.Build.0 = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxRelease|Any CPU.ActiveCfg = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.LinuxRelease|Any CPU.Build.0 = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.MacRelease|Any CPU.Build.0 = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
{35659127-8859-4DB9-8DD6-A08C1952632E}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsRelease|Any CPU.ActiveCfg = Debug|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C}.LinuxRelease|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -505,6 +535,8 @@ Global
{9AFD88E2-7E2C-46DA-9D38-4342086426D3} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
{47186A50-8889-4FC7-8A05-F9FCE7F8F4AE} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
{83EAC1F9-8E1F-41FC-8FC9-2C452452D64E} = {66722747-1B61-40E4-A89B-1AC8E6D62EA9}
{35659127-8859-4DB9-8DD6-A08C1952632E} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
{01BF56EC-AAC1-4BCA-8204-EE51D968DF5C} = {66722747-1B61-40E4-A89B-1AC8E6D62EA9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0EF9FC65-E6BA-45D4-A455-262A9EA4366B}
Expand Down
10 changes: 10 additions & 0 deletions src/linux/Packaging.Linux/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )"
SRC="$ROOT/src"
OUT="$ROOT/out"
GCM_SRC="$SRC/shared/Git-Credential-Manager"
GCM_UI_SRC="$SRC/shared/Git-Credential-Manager.UI.Avalonia"
BITBUCKET_UI_SRC="$SRC/shared/Atlassian.Bitbucket.UI.Avalonia"
GITHUB_UI_SRC="$SRC/shared/GitHub.UI.Avalonia"
GITLAB_UI_SRC="$SRC/shared/GitLab.UI.Avalonia"
Expand Down Expand Up @@ -120,6 +121,15 @@ $DOTNET_ROOT/dotnet publish "$GCM_SRC" \
-p:PublishSingleFile=true \
--output="$(make_absolute "$PAYLOAD")" || exit 1

echo "Publishing core UI helper..."
$DOTNET_ROOT/dotnet publish "$GCM_UI_SRC" \
--configuration="$CONFIGURATION" \
--framework="$FRAMEWORK" \
--runtime="$RUNTIME" \
--self-contained=true \
-p:PublishSingleFile=true \
--output="$(make_absolute "$PAYLOAD")" || exit 1

echo "Publishing Bitbucket UI helper..."
$DOTNET_ROOT/dotnet publish "$BITBUCKET_UI_SRC" \
--configuration="$CONFIGURATION" \
Expand Down
11 changes: 11 additions & 0 deletions src/osx/Installer.Mac/layout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ SRC="$ROOT/src"
OUT="$ROOT/out"
INSTALLER_SRC="$SRC/osx/Installer.Mac"
GCM_SRC="$SRC/shared/Git-Credential-Manager"
GCM_UI_SRC="$SRC/shared/Git-Credential-Manager.UI.Avalonia"
BITBUCKET_UI_SRC="$SRC/shared/Atlassian.Bitbucket.UI.Avalonia"
GITHUB_UI_SRC="$SRC/shared/GitHub.UI.Avalonia"
GITLAB_UI_SRC="$SRC/shared/GitLab.UI.Avalonia"
Expand Down Expand Up @@ -104,6 +105,16 @@ dotnet publish "$GCM_SRC" \
--self-contained \
--output="$(make_absolute "$PAYLOAD")" || exit 1

echo "Publishing core UI helper..."
dotnet publish "$GCM_UI_SRC" \
--no-restore \
-m:1 \
--configuration="$CONFIGURATION" \
--framework="$FRAMEWORK" \
--runtime="$RUNTIME" \
--self-contained \
--output="$(make_absolute "$PAYLOAD")" || exit 1

echo "Publishing Bitbucket UI helper..."
dotnet publish "$BITBUCKET_UI_SRC" \
--no-restore \
Expand Down
114 changes: 47 additions & 67 deletions src/shared/Core.Tests/Authentication/BasicAuthenticationTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using GitCredentialManager.Authentication;
using GitCredentialManager.Tests.Objects;
using Moq;
Expand All @@ -14,11 +16,11 @@ public void BasicAuthentication_GetCredentials_NullResource_ThrowsException()
var context = new TestCommandContext();
var basicAuth = new BasicAuthentication(context);

Assert.Throws<ArgumentNullException>(() => basicAuth.GetCredentials(null));
Assert.ThrowsAsync<ArgumentNullException>(() => basicAuth.GetCredentialsAsync(null));
}

[Fact]
public void BasicAuthentication_GetCredentials_NonDesktopSession_ResourceAndUserName_PasswordPromptReturnsCredentials()
public async Task BasicAuthentication_GetCredentials_NonDesktopSession_ResourceAndUserName_PasswordPromptReturnsCredentials()
{
const string testResource = "https://example.com";
const string testUserName = "john.doe";
Expand All @@ -29,14 +31,14 @@ public void BasicAuthentication_GetCredentials_NonDesktopSession_ResourceAndUser

var basicAuth = new BasicAuthentication(context);

ICredential credential = basicAuth.GetCredentials(testResource, testUserName);
ICredential credential = await basicAuth.GetCredentialsAsync(testResource, testUserName);

Assert.Equal(testUserName, credential.Account);
Assert.Equal(testPassword, credential.Password);
}

[Fact]
public void BasicAuthentication_GetCredentials_NonDesktopSession_Resource_UserPassPromptReturnsCredentials()
public async Task BasicAuthentication_GetCredentials_NonDesktopSession_Resource_UserPassPromptReturnsCredentials()
{
const string testResource = "https://example.com";
const string testUserName = "john.doe";
Expand All @@ -48,7 +50,7 @@ public void BasicAuthentication_GetCredentials_NonDesktopSession_Resource_UserPa

var basicAuth = new BasicAuthentication(context);

ICredential credential = basicAuth.GetCredentials(testResource);
ICredential credential = await basicAuth.GetCredentialsAsync(testResource);

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

var basicAuth = new BasicAuthentication(context);

Assert.Throws<InvalidOperationException>(() => basicAuth.GetCredentials(testResource));
Assert.ThrowsAsync<InvalidOperationException>(() => basicAuth.GetCredentialsAsync(testResource));
}

[PlatformFact(Platforms.Windows)]
public void BasicAuthentication_GetCredentials_DesktopSession_Resource_UserPassPromptReturnsCredentials()
[Fact]
public async Task BasicAuthentication_GetCredentials_DesktopSession_CallsHelper()
{
const string testResource = "https://example.com";
const string testUserName = "john.doe";
const string testPassword = "letmein123"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Fake credential")]

var context = new TestCommandContext
{
SessionManager = {IsDesktopSession = true},
SystemPrompts =
{
CredentialPrompt = (resource, userName) =>
{
Assert.Equal(testResource, resource);
Assert.Null(userName);
return new GitCredential(testUserName, testPassword);
}
}
SessionManager = {IsDesktopSession = true}
};

var basicAuth = new BasicAuthentication(context);

ICredential credential = basicAuth.GetCredentials(testResource);

Assert.NotNull(credential);
Assert.Equal(testUserName, credential.Account);
Assert.Equal(testPassword, credential.Password);
}

[PlatformFact(Platforms.Windows)]
public void BasicAuthentication_GetCredentials_DesktopSession_ResourceAndUser_PassPromptReturnsCredentials()
{
const string testResource = "https://example.com";
const string testUserName = "john.doe";
const string testPassword = "letmein123"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Fake credential")]

var context = new TestCommandContext
{
SessionManager = {IsDesktopSession = true},
SystemPrompts =
{
CredentialPrompt = (resource, userName) =>
context.FileSystem.Files["/usr/local/bin/git-credential-manager-ui"] = new byte[0];
context.FileSystem.Files[@"C:\Program Files\Git Credential Manager Core\git-credential-manager-ui.exe"] = new byte[0];

var auth = new Mock<BasicAuthentication>(MockBehavior.Strict, context);
auth.Setup(x => x.InvokeHelperAsync(
It.IsAny<string>(),
$"basic --resource {testResource}",
It.IsAny<IDictionary<string, string>>(),
It.IsAny<System.Threading.CancellationToken>()))
.ReturnsAsync(
new Dictionary<string, string>
{
Assert.Equal(testResource, resource);
Assert.Equal(testUserName, userName);
return new GitCredential(testUserName, testPassword);
["username"] = testUserName,
["password"] = testPassword
}
}
};

var basicAuth = new BasicAuthentication(context);
);

ICredential credential = basicAuth.GetCredentials(testResource, testUserName);
ICredential credential = await auth.Object.GetCredentialsAsync(testResource);

Assert.NotNull(credential);
Assert.Equal(testUserName, credential.Account);
Assert.Equal(testPassword, credential.Password);
}

[PlatformFact(Platforms.Windows)]
public void BasicAuthentication_GetCredentials_DesktopSession_ResourceAndUser_PassPromptDiffUserReturnsCredentials()
[Fact]
public async Task BasicAuthentication_GetCredentials_DesktopSession_UserName_CallsHelper()
{
const string testResource = "https://example.com";
const string testUserName = "john.doe";
const string newUserName = "jane.doe";
const string testPassword = "letmein123"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Fake credential")]

var context = new TestCommandContext
{
SessionManager = {IsDesktopSession = true},
SystemPrompts =
{
CredentialPrompt = (resource, userName) =>
{
Assert.Equal(testResource, resource);
Assert.Equal(testUserName, userName);
return new GitCredential(newUserName, testPassword);
}
}
SessionManager = {IsDesktopSession = true}
};

var basicAuth = new BasicAuthentication(context);
context.FileSystem.Files["/usr/local/bin/git-credential-manager-ui"] = new byte[0];
context.FileSystem.Files[@"C:\Program Files\Git Credential Manager Core\git-credential-manager-ui.exe"] = new byte[0];

var auth = new Mock<BasicAuthentication>(MockBehavior.Strict, context);
auth.Setup(x => x.InvokeHelperAsync(
It.IsAny<string>(),
$"basic --resource {testResource} --username {testUserName}",
It.IsAny<IDictionary<string, string>>(),
It.IsAny<System.Threading.CancellationToken>()))
.ReturnsAsync(
new Dictionary<string, string>
{
["username"] = testUserName,
["password"] = testPassword
}
);

ICredential credential = basicAuth.GetCredentials(testResource, testUserName);
ICredential credential = await auth.Object.GetCredentialsAsync(testResource, testUserName);

Assert.NotNull(credential);
Assert.Equal(newUserName, credential.Account);
Assert.Equal(testUserName, credential.Account);
Assert.Equal(testPassword, credential.Password);
}
}
Expand Down
Loading

0 comments on commit 00244fa

Please sign in to comment.