Skip to content

Commit 42e494b

Browse files
hickfordMatthew John Cheetham
andcommitted
Apply suggestions from code review
Co-authored-by: Matthew John Cheetham <[email protected]>
1 parent 9262dda commit 42e494b

File tree

8 files changed

+49
-107
lines changed

8 files changed

+49
-107
lines changed

docs/environment.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ ID|Provider
169169
`auto` _(default)_|_\[automatic\]_ ([learn more](autodetect.md))
170170
`azure-repos`|Azure Repos
171171
`github`|GitHub
172+
`gitlab`|GitLab<br/>_(supports OAuth in browser, personal access token and Basic Authentication)_
172173
`generic`|Generic (any other provider not listed above)
173174

174175
Automatic provider selection is based on the remote URL.
@@ -206,6 +207,7 @@ Authority|Provider(s)
206207
`auto` _(default)_|_\[automatic\]_
207208
`msa`, `microsoft`, `microsoftaccount`,<br/>`aad`, `azure`, `azuredirectory`,</br>`live`, `liveconnect`, `liveid`|Azure Repos<br/>_(supports Microsoft Authentication)_
208209
`github`|GitHub<br/>_(supports GitHub Authentication)_
210+
`gitlab`|GitLab<br/>_(supports OAuth in browser, personal access token and Basic Authentication)_
209211
`basic`, `integrated`, `windows`, `kerberos`, `ntlm`,<br/>`tfs`, `sso`|Generic<br/>_(supports Basic and Windows Integrated Authentication)_
210212

211213
#### Example

docs/gitlab.md

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
# GitLab support
22

3-
git-credential-manager supports the following public GitLab instances out the box:
3+
Git Credential Manager supports [gitlab.com](https://gitlab.com) out the box.
44

5-
* https://gitlab.com
6-
* https://gitlab.freedesktop.org
7-
8-
If you'd like support for another puublic instance, please [open an issue](https://github.com/GitCredentialManager/git-credential-manager/issues).
9-
10-
## Using on a custom instance
5+
## Using on a another instance
116

127
To use on another instance, eg. `https://gitlab.example.com` requires setup and configuration:
138

14-
1. [Create an OAuth application](https://docs.gitlab.com/ee/integration/oauth_provider.html). This can be at the user, group or instance level. Specify name `git-credential-manager` and redirect URI `http://127.0.0.1/`. Select options 'Confidential', 'Expire access tokens' and scope 'write_repository'.
9+
1. [Create an OAuth application](https://docs.gitlab.com/ee/integration/oauth_provider.html). This can be at the user, group or instance level. Specify a name and use a redirect URI of `http://127.0.0.1/`. _Unselect_ the 'Confidential' option, and ensure the 'Expire access tokens' option is selected. Set the scope to 'write_repository'.
1510
2. Copy the application ID and configure `git config --global credential.https://gitlab.example.com.GitLabDevClientId <APPLICATION_ID>`
1611
3. Copy the application secret and configure `git config --global credential.https://gitlab.example.com.GitLabDevClientSecret <APPLICATION_SECRET>`
17-
4. For good measure, configure `git config --global credential.https://gitlab.example.com.provider gitlab`
18-
5. Verify the config is as expected `git config --global --get-urlmatch credential https://gitlab.example.com`
12+
4. Configure authentication modes to include 'browser' `git config --global credential.https://gitlab.example.com.gitLabAuthModes browser`
13+
5. For good measure, configure `git config --global credential.https://gitlab.example.com.provider gitlab`
14+
6. Verify the config is as expected `git config --global --get-urlmatch credential https://gitlab.example.com`
1915

2016
### Clearing config
2117

@@ -35,7 +31,6 @@ Select an authentication method for 'https://gitlab.com/':
3531
option (enter for default):
3632
```
3733

38-
3934
If you have a preferred authentication mode, you can specify [credential.gitLabAuthModes](configuration.md#credential.gitLabAuthModes):
4035

4136
`git config --global credential.gitlabauthmodes browser`

src/shared/GitLab.Tests/GitLabHostProviderTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ public class GitLabHostProviderTests
1414
[Theory]
1515
[InlineData("https", "gitlab.com", true)]
1616
[InlineData("http", "gitlab.com", true)]
17-
[InlineData("https", "gitlab.freedesktop.org", true)]
18-
[InlineData("https", "gitlab.gnome.org", true)]
17+
[InlineData("https", "gitlab.example.com", true)]
1918
[InlineData("https", "github.com", false)]
19+
[InlineData("https", "github.example.com", false)]
2020
public void GitLabHostProvider_IsSupported(string protocol, string host, bool expected)
2121
{
2222
var input = new InputArguments(new Dictionary<string, string>

src/shared/GitLab/GitLabAuthentication.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public AuthenticationPromptResult GetAuthentication(Uri targetUri, string userNa
6969
switch (modes)
7070
{
7171
case AuthenticationModes.Basic:
72+
case AuthenticationModes.Pat:
7273
ThrowIfUserInteractionDisabled();
7374
ThrowIfTerminalPromptsDisabled();
7475
Context.Terminal.WriteLine("Enter GitLab credentials for '{0}'...", targetUri);
@@ -82,22 +83,12 @@ public AuthenticationPromptResult GetAuthentication(Uri targetUri, string userNa
8283
Context.Terminal.WriteLine("Username: {0}", userName);
8384
}
8485

85-
string password = Context.Terminal.PromptSecret("Password");
86-
87-
return new AuthenticationPromptResult(
88-
AuthenticationModes.Basic, new GitCredential(userName, password));
86+
string password_or_token = Context.Terminal.PromptSecret(modes == AuthenticationModes.Basic ? "Password" : "Personal access token");
87+
return new AuthenticationPromptResult(modes, new GitCredential(userName, password_or_token));
8988

9089
case AuthenticationModes.Browser:
9190
return new AuthenticationPromptResult(AuthenticationModes.Browser);
9291

93-
case AuthenticationModes.Pat:
94-
ThrowIfUserInteractionDisabled();
95-
ThrowIfTerminalPromptsDisabled();
96-
Context.Terminal.WriteLine("Enter GitLab personal access token for '{0}'...", targetUri);
97-
string pat = Context.Terminal.PromptSecret("Token");
98-
return new AuthenticationPromptResult(
99-
AuthenticationModes.Pat, new GitCredential(userName, pat));
100-
10192
case AuthenticationModes.None:
10293
throw new ArgumentOutOfRangeException(nameof(modes), @$"At least one {nameof(AuthenticationModes)} must be supplied");
10394

src/shared/GitLab/GitLabConstants.cs

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,11 @@
44

55
namespace GitLab
66
{
7-
public record GitLabApplication
8-
{
9-
public GitLabApplication(string host, string oAuthClientId, string oAuthClientSecret)
10-
{
11-
Host = host;
12-
OAuthClientId = oAuthClientId;
13-
OAuthClientSecret = oAuthClientSecret;
14-
}
15-
16-
public string Host { get; }
17-
public string OAuthClientId { get; }
18-
public string OAuthClientSecret { get; }
19-
}
20-
21-
227
public static class GitLabConstants
238
{
24-
// To add an instance, follow https://docs.gitlab.com/ee/integration/oauth_provider.html
25-
// specify callback/redirect URI http://127.0.0.1/ and check 'confidential', 'expire access tokens', 'write_repository'
26-
private static readonly GitLabApplication[] GitLabApplications =
27-
{
28-
new GitLabApplication(host: "gitlab.com",
29-
// https://gitlab.com/oauth/applications/207177/edit owned by hickford
30-
oAuthClientId: "d8a14250a4d1beacaad67dd6fabaab1e0408b581ca73ae4a76cc7170d3f8afd1",
31-
oAuthClientSecret : "58b5f5e0c99a5be9ac13f4ba15992cc72c5594386e82aecac94da964147d3151"
32-
),
33-
new GitLabApplication(host: "gitlab.freedesktop.org",
34-
// https://gitlab.freedesktop.org/oauth/applications/52 owned by hickford
35-
oAuthClientId: "6503d8c5a27187628440d44e0352833a2b49bce540c546c22a3378c8f5b74d45",
36-
oAuthClientSecret: "2ae9343a034ff1baadaef1e7ce3197776b00746a02ddf0323bb34aca8bff6dc1")
37-
};
38-
public static readonly IDictionary<string, GitLabApplication> GitLabApplicationsByHost = GitLabApplications.ToDictionary(x => x.Host, StringComparer.InvariantCultureIgnoreCase);
9+
// owned by https://gitlab.com/gitcredentialmanager
10+
public const string OAuthClientId = "172b9f227872b5dde33f4d9b1db06a6a5515ae79508e7a00c973c85ce490671e";
11+
public const string OAuthClientSecret = "7da92770d1447508601e4ba026bc5eb655c8268e818cd609889cc9bae2023f39";
3912

4013
public static readonly Uri OAuthRedirectUri = new Uri("http://127.0.0.1/");
4114
// https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
@@ -57,11 +30,13 @@ public static class GitConfiguration
5730
{
5831
public static class Credential
5932
{
60-
public const string AuthenticationModes = "GitLabAuthModes";
61-
public const string DevOAuthClientId = "GitLabDevClientId";
62-
public const string DevOAuthClientSecret = "GitLabDevClientSecret";
63-
public const string DevOAuthRedirectUri = "GitLabDevRedirectUri";
33+
public const string AuthenticationModes = "gitLabAuthModes";
34+
public const string DevOAuthClientId = "gitLabDevClientId";
35+
public const string DevOAuthClientSecret = "gitLabDevClientSecret";
36+
public const string DevOAuthRedirectUri = "gitLabDevRedirectUri";
6437
}
6538
}
39+
40+
public static bool IsGitLabDotCom(Uri uri) => StringComparer.OrdinalIgnoreCase.Equals(uri.Host, "gitlab.com");
6641
}
6742
}

src/shared/GitLab/GitLabHostProvider.cs

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@ public class GitLabHostProvider : HostProvider
1515
"write_repository",
1616
};
1717

18-
private readonly IGitLabAuthentication _GitLabAuth;
18+
private readonly IGitLabAuthentication _gitLabAuth;
1919

2020
public GitLabHostProvider(ICommandContext context)
2121
: this(context, new GitLabAuthentication(context)) { }
2222

23-
public GitLabHostProvider(ICommandContext context, IGitLabAuthentication GitLabAuth)
23+
public GitLabHostProvider(ICommandContext context, IGitLabAuthentication gitLabAuth)
2424
: base(context)
2525
{
26-
EnsureArgument.NotNull(GitLabAuth, nameof(GitLabAuth));
26+
EnsureArgument.NotNull(gitLabAuth, nameof(gitLabAuth));
2727

28-
_GitLabAuth = GitLabAuth;
28+
_gitLabAuth = gitLabAuth;
2929
}
3030

31-
public override string Id => "GitLab";
31+
public override string Id => "gitlab";
3232

3333
public override string Name => "GitLab";
3434

@@ -48,15 +48,15 @@ public override bool IsSupported(InputArguments input)
4848
return false;
4949
}
5050

51-
// Split port number and hostname from host input argument
52-
if (!input.TryGetHostAndPort(out string hostName, out _))
51+
if (GitLabConstants.IsGitLabDotCom(input.GetRemoteUri()))
5352
{
54-
return false;
53+
return true;
5554
}
5655

57-
if (GitLabConstants.GitLabApplicationsByHost.ContainsKey(hostName))
56+
// Split port number and hostname from host input argument
57+
if (!input.TryGetHostAndPort(out string hostName, out _))
5858
{
59-
return true;
59+
return false;
6060
}
6161

6262
string[] domains = hostName.Split(new char[] { '.' });
@@ -78,6 +78,7 @@ public override bool IsSupported(HttpResponseMessage response)
7878
return false;
7979
}
8080

81+
// as seen at eg. https://salsa.debian.org/apt-team/apt.git
8182
// not always present https://gitlab.com/gitlab-org/gitlab/-/issues/349464
8283
return response.Headers.Contains("X-Gitlab-Feature-Category");
8384
}
@@ -106,24 +107,17 @@ public override async Task<ICredential> GenerateCredentialAsync(InputArguments i
106107

107108
AuthenticationModes authModes = GetSupportedAuthenticationModes(remoteUri);
108109

109-
AuthenticationPromptResult promptResult = _GitLabAuth.GetAuthentication(remoteUri, input.UserName, authModes);
110+
AuthenticationPromptResult promptResult = _gitLabAuth.GetAuthentication(remoteUri, input.UserName, authModes);
110111

111112
switch (promptResult.AuthenticationMode)
112113
{
113114
case AuthenticationModes.Basic:
115+
case AuthenticationModes.Pat:
114116
return promptResult.Credential;
115117

116118
case AuthenticationModes.Browser:
117119
return await GenerateOAuthCredentialAsync(remoteUri);
118120

119-
case AuthenticationModes.Pat:
120-
string token = promptResult.Credential.Password;
121-
122-
// GitLab accepts any username https://gitlab.com/gitlab-org/gitlab/-/issues/212953
123-
string userName = promptResult.Credential.Account ?? "pat";
124-
125-
return new GitCredential(userName, token);
126-
127121
default:
128122
throw new ArgumentOutOfRangeException(nameof(promptResult));
129123
}
@@ -148,32 +142,27 @@ internal AuthenticationModes GetSupportedAuthenticationModes(Uri targetUri)
148142
}
149143
}
150144

151-
AuthenticationModes modes = AuthenticationModes.Basic | AuthenticationModes.Pat;
152-
153-
try
154-
{
155-
GitLabOAuth2Client.GetClientId(Context.Settings, targetUri);
156-
}
157-
catch (Exception e)
145+
if (GitLabConstants.IsGitLabDotCom(targetUri))
158146
{
159-
Context.Streams.Error.WriteLine(e.Message);
160-
return modes;
147+
return AuthenticationModes.All;
161148
}
162-
modes |= AuthenticationModes.Browser;
163-
return modes;
149+
150+
Context.Streams.Error.WriteLine($"Missing OAuth configuration for {targetUri.Host}, see https://github.com/GitCredentialManager/git-credential-manager/blob/main/docs/gitlab.md.");
151+
// Would like to query password_authentication_enabled_for_git, but can't unless logged in https://gitlab.com/gitlab-org/gitlab/-/issues/349463
152+
return AuthenticationModes.Basic | AuthenticationModes.Pat;
164153
}
165154

166155
private async Task<GitCredential> GenerateOAuthCredentialAsync(Uri targetUri)
167156
{
168-
OAuth2TokenResult result = await _GitLabAuth.GetOAuthTokenViaBrowserAsync(targetUri, GitLabOAuthScopes);
157+
OAuth2TokenResult result = await _gitLabAuth.GetOAuthTokenViaBrowserAsync(targetUri, GitLabOAuthScopes);
169158

170159
// username oauth2 https://gitlab.com/gitlab-org/gitlab/-/issues/349461
171160
return new GitCredential("oauth2", result.AccessToken);
172161
}
173162

174163
protected override void ReleaseManagedResources()
175164
{
176-
_GitLabAuth.Dispose();
165+
_gitLabAuth.Dispose();
177166
base.ReleaseManagedResources();
178167
}
179168
}

src/shared/GitLab/GitLabOAuth2Client.cs

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class GitLabOAuth2Client : OAuth2Client
99
{
1010
public GitLabOAuth2Client(HttpClient httpClient, ISettings settings, Uri baseUri)
1111
: base(httpClient, CreateEndpoints(baseUri),
12-
GetClientId(settings, baseUri), GetRedirectUri(settings), GetClientSecret(settings, baseUri))
12+
GetClientId(settings), GetRedirectUri(settings), GetClientSecret(settings))
1313
{ }
1414

1515
private static OAuth2ServerEndpoints CreateEndpoints(Uri baseUri)
@@ -34,7 +34,7 @@ private static Uri GetRedirectUri(ISettings settings)
3434
return GitLabConstants.OAuthRedirectUri;
3535
}
3636

37-
internal static string GetClientId(ISettings settings, Uri baseUri)
37+
internal static string GetClientId(ISettings settings)
3838
{
3939
// Check for developer override value
4040
if (settings.TryGetSetting(
@@ -45,15 +45,10 @@ internal static string GetClientId(ISettings settings, Uri baseUri)
4545
return clientId;
4646
}
4747

48-
GitLabApplication instance;
49-
if (GitLabConstants.GitLabApplicationsByHost.TryGetValue(baseUri.Host, out instance))
50-
{
51-
return instance.OAuthClientId;
52-
}
53-
throw new ArgumentException($"Missing OAuth configuration for {baseUri.Host}, see https://github.com/GitCredentialManager/git-credential-manager/blob/main/docs/gitlab.md.");
48+
return GitLabConstants.OAuthClientId;
5449
}
5550

56-
private static string GetClientSecret(ISettings settings, Uri baseUri)
51+
private static string GetClientSecret(ISettings settings)
5752
{
5853
// Check for developer override value
5954
if (settings.TryGetSetting(
@@ -64,12 +59,7 @@ private static string GetClientSecret(ISettings settings, Uri baseUri)
6459
return clientSecret;
6560
}
6661

67-
GitLabApplication instance;
68-
if (GitLabConstants.GitLabApplicationsByHost.TryGetValue(baseUri.Host, out instance))
69-
{
70-
return instance.OAuthClientSecret;
71-
}
72-
throw new ArgumentException($"Missing OAuth configuration for {baseUri.Host}, see https://github.com/GitCredentialManager/git-credential-manager/blob/main/docs/gitlab.md.");
62+
return GitLabConstants.OAuthClientSecret;
7363
}
7464
}
7565
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
using System.Runtime.CompilerServices;
22

3-
[assembly:InternalsVisibleTo("GitLab.Tests")]
3+
[assembly: InternalsVisibleTo("GitLab.Tests")]

0 commit comments

Comments
 (0)