Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 079f479

Browse files
committed
Update enterprise login dialog.
- Move URL to be first field and hide the other fields until a valid enterprise URL is entered - Don't show username/password if not supported by enterprise instance
1 parent dee143e commit 079f479

File tree

7 files changed

+121
-14
lines changed

7 files changed

+121
-14
lines changed

src/GitHub.Api/SimpleApiClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public SimpleApiClient(UriString repoUrl, IGitHubClient githubClient,
3838

3939
public IGitHubClient Client { get; }
4040

41+
public Task<Meta> GetMetadata() => Client.Miscellaneous.GetMetadata();
42+
4143
public async Task<Repository> GetRepository()
4244
{
4345
// fast path to avoid locking when the cache has already been set

src/GitHub.App/ViewModels/LoginTabViewModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,11 @@ protected async Task LoginToHostViaOAuth(HostAddress address)
200200
{
201201
oauthCancel = new CancellationTokenSource();
202202

203+
if (await ConnectionManager.GetConnection(address) != null)
204+
{
205+
await ConnectionManager.LogOut(address);
206+
}
207+
203208
try
204209
{
205210
await ConnectionManager.LogInViaOAuth(address, oauthCancel.Token);

src/GitHub.App/ViewModels/LoginToGitHubForEnterpriseViewModel.cs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
using System.Reactive;
44
using System.Reactive.Linq;
55
using System.Threading.Tasks;
6+
using GitHub.Api;
67
using GitHub.App;
78
using GitHub.Authentication;
89
using GitHub.Extensions;
910
using GitHub.Info;
1011
using GitHub.Primitives;
1112
using GitHub.Services;
1213
using GitHub.Validation;
14+
using Octokit;
1315
using ReactiveUI;
1416

1517
namespace GitHub.ViewModels
@@ -18,15 +20,25 @@ namespace GitHub.ViewModels
1820
[PartCreationPolicy(CreationPolicy.NonShared)]
1921
public class LoginToGitHubForEnterpriseViewModel : LoginTabViewModel, ILoginToGitHubForEnterpriseViewModel
2022
{
23+
readonly ISimpleApiClientFactory apiClientFactory;
24+
readonly IEnterpriseProbe enterpriseProbe;
25+
2126
[ImportingConstructor]
2227
public LoginToGitHubForEnterpriseViewModel(
2328
IConnectionManager connectionManager,
29+
ISimpleApiClientFactory apiClientFactory,
30+
IEnterpriseProbe enterpriseProbe,
2431
IVisualStudioBrowser browser)
2532
: base(connectionManager, browser)
2633
{
2734
Guard.ArgumentNotNull(connectionManager, nameof(connectionManager));
35+
Guard.ArgumentNotNull(apiClientFactory, nameof(apiClientFactory));
36+
Guard.ArgumentNotNull(enterpriseProbe, nameof(enterpriseProbe));
2837
Guard.ArgumentNotNull(browser, nameof(browser));
2938

39+
this.apiClientFactory = apiClientFactory;
40+
this.enterpriseProbe = enterpriseProbe;
41+
3042
EnterpriseUrlValidator = ReactivePropertyValidator.For(this, x => x.EnterpriseUrl)
3143
.IfNullOrEmpty(Resources.EnterpriseUrlValidatorEmpty)
3244
.IfNotUri(Resources.EnterpriseUrlValidatorInvalid)
@@ -43,11 +55,15 @@ public LoginToGitHubForEnterpriseViewModel(
4355
x => x.EnterpriseUrlValidator.ValidationResult.IsValid)
4456
.ToProperty(this, x => x.CanLogin);
4557

58+
this.WhenAnyValue(x => x.EnterpriseUrl, x => x.EnterpriseUrlValidator.ValidationResult)
59+
.Throttle(TimeSpan.FromMilliseconds(500))
60+
.ObserveOn(RxApp.MainThreadScheduler)
61+
.Subscribe(x => EnterpriseUrlChanged(x.Item1, x.Item2.IsValid));
62+
4663
NavigateLearnMore = ReactiveCommand.CreateAsyncObservable(_ =>
4764
{
4865
browser.OpenUrl(GitHubUrls.LearnMore);
4966
return Observable.Return(Unit.Default);
50-
5167
});
5268
}
5369

@@ -56,9 +72,10 @@ protected override IObservable<AuthenticationResult> LogIn(object args)
5672
return LogInToHost(HostAddress.Create(EnterpriseUrl));
5773
}
5874

59-
protected override Task<AuthenticationResult> LogInViaOAuth(object args)
75+
protected override async Task<AuthenticationResult> LogInViaOAuth(object args)
6076
{
61-
throw new NotImplementedException();
77+
await LoginToHostViaOAuth(HostAddress.Create(EnterpriseUrl));
78+
return AuthenticationResult.Success;
6279
}
6380

6481
string enterpriseUrl;
@@ -68,6 +85,20 @@ public string EnterpriseUrl
6885
set { this.RaiseAndSetIfChanged(ref enterpriseUrl, value); }
6986
}
7087

88+
bool? isEnterpriseInstance;
89+
public bool? IsEnterpriseInstance
90+
{
91+
get { return isEnterpriseInstance; }
92+
private set { this.RaiseAndSetIfChanged(ref isEnterpriseInstance, value); }
93+
}
94+
95+
bool? supportsUserNameAndPassword;
96+
public bool? SupportsUserNameAndPassword
97+
{
98+
get { return supportsUserNameAndPassword; }
99+
private set { this.RaiseAndSetIfChanged(ref supportsUserNameAndPassword, value); }
100+
}
101+
71102
public ReactivePropertyValidator EnterpriseUrlValidator { get; }
72103

73104
protected override Uri BaseUri => new Uri(EnterpriseUrl);
@@ -82,5 +113,38 @@ protected override async Task ResetValidation()
82113
EnterpriseUrl = null;
83114
await EnterpriseUrlValidator.ResetAsync();
84115
}
116+
117+
async void EnterpriseUrlChanged(string url, bool valid)
118+
{
119+
var enterpriseInstance = false;
120+
var passwordAuth = (bool?)null;
121+
122+
try
123+
{
124+
if (valid)
125+
{
126+
IsEnterpriseInstance = SupportsUserNameAndPassword = null;
127+
128+
if (await enterpriseProbe.Probe(new Uri(url)) == EnterpriseProbeResult.Ok)
129+
{
130+
var client = await apiClientFactory.Create(new UriString(url));
131+
var meta = await client.GetMetadata();
132+
133+
enterpriseInstance = true;
134+
passwordAuth = meta.VerifiablePasswordAuthentication;
135+
}
136+
}
137+
}
138+
catch
139+
{
140+
enterpriseInstance = false;
141+
}
142+
143+
if (url == EnterpriseUrl)
144+
{
145+
IsEnterpriseInstance = enterpriseInstance;
146+
SupportsUserNameAndPassword = passwordAuth;
147+
}
148+
}
85149
}
86150
}

src/GitHub.Exports.Reactive/ViewModels/ILoginToGitHubForEnterpriseViewModel.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ public interface ILoginToGitHubForEnterpriseViewModel : ILoginToHostViewModel
1515
/// </summary>
1616
string EnterpriseUrl { get; set; }
1717

18+
/// <summary>
19+
/// Gets a value indicating whether a GitHub Enterprise instance was found at
20+
/// <see cref="EnterpriseUrl"/>.
21+
/// </summary>
22+
bool? IsEnterpriseInstance { get; }
23+
24+
/// <summary>
25+
/// Gets a value indcating whether the GitHub Enterprise instance at <see cref="EnterpriseUrl"/>
26+
/// supports logging in with a username and password.
27+
/// </summary>
28+
bool? SupportsUserNameAndPassword { get; }
29+
1830
/// <summary>
1931
/// Gets the validator instance used for validating the
2032
/// <see cref="EnterpriseUrl"/> property

src/GitHub.Exports/Api/ISimpleApiClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public interface ISimpleApiClient
99
IGitHubClient Client { get; }
1010
HostAddress HostAddress { get; }
1111
UriString OriginalUrl { get; }
12+
Task<Meta> GetMetadata();
1213
Task<Repository> GetRepository();
1314
bool HasWiki();
1415
bool IsEnterprise();

src/GitHub.VisualStudio/UI/Views/Controls/LoginControl.xaml

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,39 @@
148148
<StackPanel
149149
x:Name="enterpriseloginControlsPanel"
150150
Style="{StaticResource FormFieldStackPanel}">
151-
152-
<ui:PromptTextBox
153-
x:Name="enterpriseUserNameOrEmail"
154-
PromptText="{x:Static prop:Resources.UserNameOrEmailPromptText}"
155-
Margin="0,0,0,10"
156-
AutomationProperties.AutomationId="{x:Static automation:AutomationIDs.EnterpriseUsernameEmailTextBox}" />
157151

158-
<ui:SecurePasswordBox x:Name="enterprisePassword" PromptText="{x:Static prop:Resources.PasswordPrompt}" Margin="0,0,0,10" AutomationProperties.AutomationId="{x:Static automation:AutomationIDs.EnterprisePasswordTextBox}" />
152+
<ui:PromptTextBox x:Name="enterpriseUrl"
153+
PromptText="{x:Static prop:Resources.enterpriseUrlPromptText}"
154+
Margin="0,0,0,10"
155+
AutomationProperties.AutomationId="{x:Static automation:AutomationIDs.EnterpriseServerAddressTextBox}" />
156+
157+
<StackPanel Name="enterpriseValidUrlPanel" Style="{StaticResource FormFieldStackPanel}">
158+
<StackPanel Name="enterpriseUsernamePasswordPanel">
159+
<ui:PromptTextBox
160+
x:Name="enterpriseUserNameOrEmail"
161+
PromptText="{x:Static prop:Resources.UserNameOrEmailPromptText}"
162+
Margin="0,0,0,10"
163+
AutomationProperties.AutomationId="{x:Static automation:AutomationIDs.EnterpriseUsernameEmailTextBox}" />
164+
165+
<ui:SecurePasswordBox x:Name="enterprisePassword" PromptText="{x:Static prop:Resources.PasswordPrompt}" AutomationProperties.AutomationId="{x:Static automation:AutomationIDs.EnterprisePasswordTextBox}" />
166+
167+
<Grid Margin="0 8">
168+
<Grid.ColumnDefinitions>
169+
<ColumnDefinition Width="*"/>
170+
<ColumnDefinition Width="Auto"/>
171+
<ColumnDefinition Width="*"/>
172+
</Grid.ColumnDefinitions>
173+
<Rectangle Grid.Column="0" Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" Height="1"/>
174+
<TextBlock Grid.Column="1" Margin="8,0,8,4">or</TextBlock>
175+
<Rectangle Grid.Column="2" Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" Height="1"/>
176+
</Grid>
177+
</StackPanel>
159178

160-
<ui:PromptTextBox x:Name="enterpriseUrl" PromptText="{x:Static prop:Resources.enterpriseUrlPromptText}" AutomationProperties.AutomationId="{x:Static automation:AutomationIDs.EnterpriseServerAddressTextBox}" />
179+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
180+
<ui:GitHubActionLink x:Name="enterpriseSsaLogInButton">Sign in with your browser</ui:GitHubActionLink>
181+
<ui:OcticonImage Icon="link_external" Margin="0 1 0 0"/>
182+
</StackPanel>
183+
</StackPanel>
161184

162185
<uirx:ValidationMessage
163186
x:Name="enterpriseUserNameOrEmailValidationMessage"
@@ -174,8 +197,6 @@
174197

175198
<uirx:UserErrorMessages x:Name="enterpriseErrorMessage" Margin="0,10">
176199
</uirx:UserErrorMessages>
177-
178-
<ui:GitHubActionLink x:Name="enterpriseSsaLogInButton">Login with your browser</ui:GitHubActionLink>
179200
</StackPanel>
180201
</DockPanel>
181202
</TabItem>

src/GitHub.VisualStudio/UI/Views/Controls/LoginControl.xaml.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ void SetupDotComBindings(Action<IDisposable> d)
8989
void SetupEnterpriseBindings(Action<IDisposable> d)
9090
{
9191
d(this.OneWayBind(ViewModel, vm => vm.EnterpriseLogin.IsLoggingIn, x => x.enterpriseloginControlsPanel.IsEnabled, x => x == false));
92-
92+
d(this.OneWayBind(ViewModel, vm => vm.EnterpriseLogin.IsEnterpriseInstance, x => x.enterpriseValidUrlPanel.Visibility, x => x == true ? Visibility.Visible : Visibility.Collapsed));
93+
d(this.OneWayBind(ViewModel, vm => vm.EnterpriseLogin.SupportsUserNameAndPassword, x => x.enterpriseUsernamePasswordPanel.Visibility, x => x == true ? Visibility.Visible : Visibility.Collapsed));
94+
9395
d(this.Bind(ViewModel, vm => vm.EnterpriseLogin.UsernameOrEmail, x => x.enterpriseUserNameOrEmail.Text));
9496
d(this.OneWayBind(ViewModel, vm => vm.EnterpriseLogin.UsernameOrEmailValidator, v => v.enterpriseUserNameOrEmailValidationMessage.ReactiveValidator));
9597

0 commit comments

Comments
 (0)