From d6357e8fde738af42e82f893a7de1e288e7ee158 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 27 Nov 2020 13:34:32 +0100 Subject: [PATCH] Revert "Revert "Gets oauth working (with google) now need to test others and debug why the styles aren't working"" Signed-off-by: Bjarke Berg --- .../Controllers/AuthenticationController.cs | 2 +- .../Controllers/BackOfficeController.cs | 6 +- .../Controllers/BackOfficeServerVariables.cs | 13 +- .../BackOfficeExternalLoginProviderOptions.cs | 8 +- .../Security/BackOfficeSignInManager.cs | 1 - .../Security/ExternalSignInAutoLinkOptions.cs | 2 - .../IBackOfficeExternalLoginProviders.cs | 131 ++++++++++++++---- .../services/externallogininfo.service.js | 2 +- .../src/views/common/overlays/user/user.html | 6 +- .../components/application/umb-login.html | 2 +- 10 files changed, 126 insertions(+), 47 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 8189641c3a..9d76e58982 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -180,7 +180,7 @@ namespace Umbraco.Web.BackOffice.Controllers } else { - if (!opt.AutoLinkOptions.AllowManualLinking) + if (!opt.Options.AutoLinkOptions.AllowManualLinking) { // If AllowManualLinking is disabled for this provider we cannot unlink return BadRequest(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 2a0bba8c1f..c905876f51 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -414,7 +414,7 @@ namespace Umbraco.Web.BackOffice.Controllers } else { - autoLinkOptions = _externalLogins.Get(authType.Name)?.AutoLinkOptions; + autoLinkOptions = _externalLogins.Get(authType.Name)?.Options?.AutoLinkOptions; } // Sign in the user with this external login provider if the user already has a login @@ -445,7 +445,7 @@ namespace Umbraco.Web.BackOffice.Controllers ViewData.SetExternalSignInProviderErrors( new BackOfficeExternalLoginProviderErrors( loginInfo.LoginProvider, - new[] { "The requested provider (" + loginInfo.LoginProvider + ") has not been linked to an account" })); + new[] { "The requested provider (" + loginInfo.LoginProvider + ") has not been linked to an account, the provider must be linked from the back office." })); } //Remove the cookie otherwise this message will keep appearing @@ -462,7 +462,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (autoLinkOptions.AutoLinkExternalAccount == false) { - return true; // TODO: This seems weird to return true, but it was like that before so must be a reason? + return false; } var email = loginInfo.Principal.FindFirstValue(ClaimTypes.Email); diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index 2fa3f62cb8..470e62c2c2 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -135,7 +135,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the server variables for authenticated users /// /// - internal async Task> GetServerVariablesAsync() + internal Task> GetServerVariablesAsync() { var globalSettings = _globalSettings; var backOfficeControllerName = ControllerExtensions.GetControllerName(); @@ -149,8 +149,8 @@ namespace Umbraco.Web.BackOffice.Controllers // having each url defined here explicitly - we can do that in v8! for now // for umbraco services we'll stick to explicitly defining the endpoints. - // {"externalLoginsUrl", _linkGenerator.GetPathByAction(nameof(BackOfficeController.ExternalLogin), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })}, - // {"externalLinkLoginsUrl", _linkGenerator.GetPathByAction(nameof(BackOfficeController.LinkLogin), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })}, + {"externalLoginsUrl", _linkGenerator.GetPathByAction(nameof(BackOfficeController.ExternalLogin), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })}, + {"externalLinkLoginsUrl", _linkGenerator.GetPathByAction(nameof(BackOfficeController.LinkLogin), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })}, {"gridConfig", _linkGenerator.GetPathByAction(nameof(BackOfficeController.GetGridConfig), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })}, // TODO: This is ultra confusing! this same key is used for different things, when returning the full app when authenticated it is this URL but when not auth'd it's actually the ServerVariables address {"serverVarsJs", _linkGenerator.GetPathByAction(nameof(BackOfficeController.Application), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })}, @@ -418,10 +418,13 @@ namespace Umbraco.Web.BackOffice.Controllers "externalLogins", new Dictionary { { + // 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() .Select(p => new { - authType = p.AuthenticationType, caption = p.Name, + authType = p.AuthenticationType, + caption = p.Name, properties = p.Options }) .ToArray() @@ -441,7 +444,7 @@ namespace Umbraco.Web.BackOffice.Controllers } } }; - return defaultVals; + return Task.FromResult(defaultVals); } [DataContract] diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs index 58ec79e51b..a3ce87e404 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs @@ -4,7 +4,6 @@ using System.Runtime.Serialization; namespace Umbraco.Web.BackOffice.Security { - // TODO: This is only for the back office, does it need to be in common? /// /// Options used to configure back office external login providers @@ -12,13 +11,13 @@ namespace Umbraco.Web.BackOffice.Security public class BackOfficeExternalLoginProviderOptions { public BackOfficeExternalLoginProviderOptions( - string style, string icon, string callbackPath, + string buttonStyle, string icon, string callbackPath, ExternalSignInAutoLinkOptions autoLinkOptions = null, bool denyLocalLogin = false, bool autoRedirectLoginToExternalProvider = false, string customBackOfficeView = null) { - Style = style; + ButtonStyle = buttonStyle; Icon = icon; CallbackPath = callbackPath; AutoLinkOptions = autoLinkOptions ?? new ExternalSignInAutoLinkOptions(); @@ -27,11 +26,10 @@ namespace Umbraco.Web.BackOffice.Security CustomBackOfficeView = customBackOfficeView; } - public string Style { get; } + public string ButtonStyle { get; } public string Icon { get; } public string CallbackPath { get; } - /// /// Options used to control how users can be auto-linked/created/updated based on the external login provider /// diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs index 0cbcd85923..6f34a85c79 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs @@ -14,7 +14,6 @@ using Umbraco.Extensions; namespace Umbraco.Web.Common.Security { - // TODO: This is only for the back office, does it need to be in common? using Constants = Umbraco.Core.Constants; diff --git a/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs b/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs index 2098d90773..8d9f57945b 100644 --- a/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs @@ -7,8 +7,6 @@ using SecurityConstants = Umbraco.Core.Constants.Security; namespace Umbraco.Web.BackOffice.Security { - // TODO: This is only for the back office, does it need to be in common? - /// /// Options used to configure auto-linking external OAuth providers /// diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs index b631227470..6d0b64e84f 100644 --- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs +++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs @@ -1,27 +1,109 @@ -using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; - +using Umbraco.Core; +using Umbraco.Core.Builder; namespace Umbraco.Web.BackOffice.Security { - // TODO: This is only for the back office, does it need to be in common? + /// + /// Custom used to associate external logins with umbraco external login options + /// + public class BackOfficeAuthenticationBuilder : AuthenticationBuilder + { + private readonly BackOfficeExternalLoginProviderOptions _loginProviderOptions; + + public BackOfficeAuthenticationBuilder(IServiceCollection services, BackOfficeExternalLoginProviderOptions loginProviderOptions) + : base(services) + { + _loginProviderOptions = loginProviderOptions; + } + + /// + /// Overridden to track the final authenticationScheme being registered for the external login + /// + /// + /// + /// + /// + /// + /// + public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string displayName, Action configureOptions) + { + //Ensure the prefix is set + if (!authenticationScheme.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix)) + { + authenticationScheme = Constants.Security.BackOfficeExternalAuthenticationTypePrefix + authenticationScheme; + } + + // add our login provider to the container along with a custom options configuration + Services.AddSingleton(x => new BackOfficeExternalLoginProvider(displayName, authenticationScheme, _loginProviderOptions)); + Services.TryAddEnumerable(ServiceDescriptor.Singleton, EnsureBackOfficeScheme>()); + + return base.AddRemoteScheme(authenticationScheme, displayName, configureOptions); + } + + // TODO: We could override and throw NotImplementedException for other methods? + + // Ensures that the sign in scheme is always the Umbraco back office external type + private class EnsureBackOfficeScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions + { + public void PostConfigure(string name, TOptions options) + { + options.SignInScheme = Constants.Security.BackOfficeExternalAuthenticationType; + } + } + } + + /// + /// Used to add back office login providers + /// + public class BackOfficeExternalLoginsBuilder + { + public BackOfficeExternalLoginsBuilder(IServiceCollection services) + { + _services = services; + } + + private readonly IServiceCollection _services; + + /// + /// Add a back office login provider with options + /// + /// + /// + /// + public BackOfficeExternalLoginsBuilder AddBackOfficeLogin( + BackOfficeExternalLoginProviderOptions loginProviderOptions, + Action build) + { + build(new BackOfficeAuthenticationBuilder(_services, loginProviderOptions)); + return this; + } + } + + public static class AuthenticationBuilderExtensions + { + public static IUmbracoBuilder AddBackOfficeExternalLogins(this IUmbracoBuilder umbracoBuilder, Action builder) + { + builder(new BackOfficeExternalLoginsBuilder(umbracoBuilder.Services)); + return umbracoBuilder; + } + } // TODO: We need to implement this and extend it to support the back office external login options // basically migrate things from AuthenticationManagerExtensions & AuthenticationOptionsExtensions // and use this to get the back office external login infos public interface IBackOfficeExternalLoginProviders { - /// - /// Register a login provider for the back office - /// - /// - void Register(BackOfficeExternalLoginProvider provider); - - BackOfficeExternalLoginProviderOptions Get(string authenticationType); + BackOfficeExternalLoginProvider Get(string authenticationType); IEnumerable GetBackOfficeProviders(); @@ -32,42 +114,41 @@ namespace Umbraco.Web.BackOffice.Security /// string GetAutoLoginProvider(); + /// + /// Returns true if there is any external provider that has the Deny Local Login option configured + /// + /// bool HasDenyLocalLogin(); } - // TODO: This class is just a placeholder for later public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProviders { - private ConcurrentDictionary _providers = new ConcurrentDictionary(); - - public void Register(BackOfficeExternalLoginProvider provider) + public BackOfficeExternalLoginProviders(IEnumerable externalLogins) { - _providers.TryAdd(provider.AuthenticationType, provider); - - // TODO: we need to be able to set things like we were doing in ForUmbracoBackOffice. - // Ok, most is done but we'll also need to take into account the callback path to ignore when we - // do front-end routing + _externalLogins = externalLogins; } - public BackOfficeExternalLoginProviderOptions Get(string authenticationType) + private readonly IEnumerable _externalLogins; + + public BackOfficeExternalLoginProvider Get(string authenticationType) { - return _providers.TryGetValue(authenticationType, out var opt) ? opt.Options : null; + return _externalLogins.FirstOrDefault(x => x.AuthenticationType == authenticationType); } public string GetAutoLoginProvider() { - var found = _providers.Where(x => x.Value.Options.AutoRedirectLoginToExternalProvider).ToList(); - return found.Count > 0 ? found[0].Key : null; + var found = _externalLogins.Where(x => x.Options.AutoRedirectLoginToExternalProvider).ToList(); + return found.Count > 0 ? found[0].AuthenticationType : null; } public IEnumerable GetBackOfficeProviders() { - return _providers.Values; + return _externalLogins; } public bool HasDenyLocalLogin() { - var found = _providers.Where(x => x.Value.Options.DenyLocalLogin).ToList(); + var found = _externalLogins.Where(x => x.Options.DenyLocalLogin).ToList(); return found.Count > 0; } } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js b/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js index 31914f4e58..f1dbb0f651 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js @@ -50,7 +50,7 @@ function externalLoginInfoService(externalLoginInfo, umbRequestHelper) { return true; } else { - return x.properties.ExternalSignInAutoLinkOptions.AllowManualLinking; + return x.properties.AutoLinkOptions.AllowManualLinking; } }); return providers; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html index fdd2671200..330a57ab7d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html @@ -52,11 +52,11 @@
-
+