diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index ae7776dfaf..cf7a7dd729 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -181,14 +181,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } else { - var opt = _externalAuthenticationOptions.Get(authType.Name); + BackOfficeExternaLoginProviderScheme opt = await _externalAuthenticationOptions.GetAsync(authType.Name); if (opt == null) { return BadRequest($"Could not find external authentication options registered for provider {unlinkLoginModel.LoginProvider}"); } else { - if (!opt.Options.AutoLinkOptions.AllowManualLinking) + if (!opt.ExternalLoginProvider.Options.AutoLinkOptions.AllowManualLinking) { // If AllowManualLinking is disabled for this provider we cannot unlink return BadRequest(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index c3fb203ec1..61c1660dd0 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -144,7 +144,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Returns the server variables for authenticated users /// /// - internal Task> GetServerVariablesAsync() + internal async Task> GetServerVariablesAsync() { var globalSettings = _globalSettings; var backOfficeControllerName = ControllerExtensions.GetControllerName(); @@ -432,12 +432,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { // TODO: It would be nicer to not have to manually translate these properties // but then needs to be changed in quite a few places in angular - "providers", _externalLogins.GetBackOfficeProviders() + "providers", (await _externalLogins.GetBackOfficeProvidersAsync()) .Select(p => new { - authType = p.AuthenticationType, - caption = p.Name, - properties = p.Options + authType = p.ExternalLoginProvider.AuthenticationType, + caption = p.AuthenticationScheme.DisplayName, + properties = p.ExternalLoginProvider.Options }) .ToArray() } @@ -456,7 +456,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } } }; - return Task.FromResult(defaultVals); + + return defaultVals; } [DataContract] diff --git a/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs index 3340988714..fee75f6eae 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -54,18 +54,18 @@ namespace Umbraco.Extensions /// /// /// - public static Task AngularValueExternalLoginInfoScriptAsync(this IHtmlHelper html, + public static async Task AngularValueExternalLoginInfoScriptAsync(this IHtmlHelper html, IBackOfficeExternalLoginProviders externalLogins, BackOfficeExternalLoginProviderErrors externalLoginErrors) { - var providers = externalLogins.GetBackOfficeProviders(); + var providers = await externalLogins.GetBackOfficeProvidersAsync(); var loginProviders = providers .Select(p => new { - authType = p.AuthenticationType, - caption = p.Name, - properties = p.Options + authType = p.ExternalLoginProvider.AuthenticationType, + caption = p.AuthenticationScheme.DisplayName, + properties = p.ExternalLoginProvider.Options }) .ToArray(); @@ -89,7 +89,7 @@ namespace Umbraco.Extensions sb.AppendLine(JsonConvert.SerializeObject(loginProviders)); sb.AppendLine(@"});"); - return Task.FromResult(html.Raw(sb.ToString())); + return html.Raw(sb.ToString()); } /// diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs index ac19a5f195..bc9f64129f 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -45,7 +46,6 @@ namespace Umbraco.Cms.Web.BackOffice.Security base.Services.AddSingleton(services => { return new BackOfficeExternalLoginProvider( - displayName, authenticationScheme, services.GetRequiredService>()); }); diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs new file mode 100644 index 0000000000..2732338426 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.AspNetCore.Authentication; + +namespace Umbraco.Cms.Web.BackOffice.Security +{ + public class BackOfficeExternaLoginProviderScheme + { + public BackOfficeExternaLoginProviderScheme( + BackOfficeExternalLoginProvider externalLoginProvider, + AuthenticationScheme authenticationScheme) + { + ExternalLoginProvider = externalLoginProvider ?? throw new ArgumentNullException(nameof(externalLoginProvider)); + AuthenticationScheme = authenticationScheme ?? throw new ArgumentNullException(nameof(authenticationScheme)); + } + + public BackOfficeExternalLoginProvider ExternalLoginProvider { get; } + public AuthenticationScheme AuthenticationScheme { get; } + } + +} diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs index fa942bddaa..9e78917087 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs @@ -9,7 +9,6 @@ namespace Umbraco.Cms.Web.BackOffice.Security public class BackOfficeExternalLoginProvider : IEquatable { public BackOfficeExternalLoginProvider( - string name, string authenticationType, IOptionsMonitor properties) { @@ -18,31 +17,20 @@ namespace Umbraco.Cms.Web.BackOffice.Security throw new ArgumentNullException(nameof(properties)); } - Name = name ?? throw new ArgumentNullException(nameof(name)); AuthenticationType = authenticationType ?? throw new ArgumentNullException(nameof(authenticationType)); Options = properties.Get(authenticationType); } - public string Name { get; } + /// + /// The authentication "Scheme" + /// public string AuthenticationType { get; } + public BackOfficeExternalLoginProviderOptions Options { get; } - public override bool Equals(object obj) - { - return Equals(obj as BackOfficeExternalLoginProvider); - } - - public bool Equals(BackOfficeExternalLoginProvider other) - { - return other != null && - Name == other.Name && - AuthenticationType == other.AuthenticationType; - } - - public override int GetHashCode() - { - return HashCode.Combine(Name, AuthenticationType); - } + public override bool Equals(object obj) => Equals(obj as BackOfficeExternalLoginProvider); + public bool Equals(BackOfficeExternalLoginProvider other) => other != null && AuthenticationType == other.AuthenticationType; + public override int GetHashCode() => HashCode.Combine(AuthenticationType); } } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs index 7ecb4e2829..4c9799b9a4 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs @@ -1,41 +1,71 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; namespace Umbraco.Cms.Web.BackOffice.Security { + /// public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProviders { - public BackOfficeExternalLoginProviders(IEnumerable externalLogins) + private readonly Dictionary _externalLogins; + private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider; + + public BackOfficeExternalLoginProviders( + IEnumerable externalLogins, + IAuthenticationSchemeProvider authenticationSchemeProvider) { - _externalLogins = externalLogins; + _externalLogins = externalLogins.ToDictionary(x => x.AuthenticationType); + _authenticationSchemeProvider = authenticationSchemeProvider; } - private readonly IEnumerable _externalLogins; - /// - public BackOfficeExternalLoginProvider Get(string authenticationType) + public async Task GetAsync(string authenticationType) { - return _externalLogins.FirstOrDefault(x => x.AuthenticationType == authenticationType); + if (!_externalLogins.TryGetValue(authenticationType, out BackOfficeExternalLoginProvider provider)) + { + return null; + } + + // get the associated scheme + AuthenticationScheme associatedScheme = await _authenticationSchemeProvider.GetSchemeAsync(provider.AuthenticationType); + + if (associatedScheme == null) + { + throw new InvalidOperationException("No authentication scheme registered for " + provider.AuthenticationType); + } + + return new BackOfficeExternaLoginProviderScheme(provider, associatedScheme); } /// public string GetAutoLoginProvider() { - var found = _externalLogins.Where(x => x.Options.AutoRedirectLoginToExternalProvider).ToList(); + var found = _externalLogins.Values.Where(x => x.Options.AutoRedirectLoginToExternalProvider).ToList(); return found.Count > 0 ? found[0].AuthenticationType : null; } /// - public IEnumerable GetBackOfficeProviders() + public async Task> GetBackOfficeProvidersAsync() { - return _externalLogins; + var providersWithSchemes = new List(); + foreach (BackOfficeExternalLoginProvider login in _externalLogins.Values) + { + // get the associated scheme + AuthenticationScheme associatedScheme = await _authenticationSchemeProvider.GetSchemeAsync(login.AuthenticationType); + + providersWithSchemes.Add(new BackOfficeExternaLoginProviderScheme(login, associatedScheme)); + } + + return providersWithSchemes; } /// public bool HasDenyLocalLogin() { - var found = _externalLogins.Where(x => x.Options.DenyLocalLogin).ToList(); + var found = _externalLogins.Values.Where(x => x.Options.DenyLocalLogin).ToList(); return found.Count > 0; } } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs index 3e921ba0f9..4b970e4b72 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs // to be able to deal with auto-linking and reduce duplicate lookups - var autoLinkOptions = _externalLogins.Get(loginInfo.LoginProvider)?.Options?.AutoLinkOptions; + var autoLinkOptions = (await _externalLogins.GetAsync(loginInfo.LoginProvider))?.ExternalLoginProvider?.Options?.AutoLinkOptions; var user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); if (user == null) { diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs index d47873f3cd..2426cfcf4d 100644 --- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs +++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Umbraco.Cms.Web.BackOffice.Security { @@ -13,13 +14,13 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - BackOfficeExternalLoginProvider Get(string authenticationType); + Task GetAsync(string authenticationType); /// /// Get all registered /// /// - IEnumerable GetBackOfficeProviders(); + Task> GetBackOfficeProvidersAsync(); /// /// Returns the authentication type for the last registered external login (oauth) provider that specifies an auto-login redirect option