Allows for customizing the invite link, had to update umb-button to support external href, streamlines external login provider configs

This commit is contained in:
Shannon
2020-06-16 14:48:43 +10:00
parent 4385929628
commit 5ac02431cf
13 changed files with 139 additions and 61 deletions

View File

@@ -56,8 +56,7 @@ namespace Umbraco.Core
public const string AllowedApplicationsClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/allowedapp";
public const string SessionIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/sessionid";
public const string BackOfficeExternalAuthenticationDenyLocalLoginProperty = "UmbracoBackOffice_DenyLocalLogin";
public const string BackOfficeExternalAuthenticationAutoLoginRedirectProperty = "UmbracoBackOffice_AutoLoginRedirect";
public const string BackOfficeExternalLoginOptionsProperty = "UmbracoBackOfficeExternalLoginOptions";
}
}

View File

@@ -56,7 +56,7 @@ Use this directive to render an umbraco button. The directive can be used to gen
@param {callback} action The button action which should be performed when the button is clicked.
@param {string=} href Url/Path to navigato to.
@param {string=} type Set the button type ("button" or "submit").
@param {string=} type Set the button type ("button" or "submit" or "link").
@param {string=} buttonStyle Set the style of the button. The directive uses the default bootstrap styles ("primary", "info", "success", "warning", "danger", "inverse", "link", "block"). Pass in array to add multple styles [success,block].
@param {string=} state Set a progress state on the button ("init", "busy", "success", "error").
@param {string=} shortcut Set a keyboard shortcut for the button ("ctrl+c").
@@ -86,6 +86,7 @@ Use this directive to render an umbraco button. The directive can be used to gen
bindings: {
action: "&?",
href: "@?",
hrefTarget: "@?",
type: "@",
buttonStyle: "@?",
state: "<?",
@@ -123,6 +124,10 @@ Use this directive to render an umbraco button. The directive can be used to gen
vm.innerState = "init";
vm.generalActions = vm.labelKey === "general_actions";
if (!vm.type) {
vm.type = "button"; // set the default
}
vm.buttonLabel = vm.label;
// is this a primary button style (i.e. anything but an 'info' button)?
vm.isPrimaryButtonStyle = vm.buttonStyle && vm.buttonStyle !== 'info';

View File

@@ -11,10 +11,10 @@ function externalLoginInfoService(externalLoginInfo) {
*/
function hasDenyLocalLogin(provider) {
if (!provider) {
return _.some(externalLoginInfo.providers, x => x.properties.UmbracoBackOffice_DenyLocalLogin === true);
return _.some(externalLoginInfo.providers, x => x.properties.UmbracoBackOfficeExternalLoginOptions.DenyLocalLogin === true);
}
else {
return provider.properties.UmbracoBackOffice_DenyLocalLogin;
return provider.properties.UmbracoBackOfficeExternalLoginOptions.DenyLocalLogin;
}
}
@@ -24,16 +24,32 @@ function externalLoginInfoService(externalLoginInfo) {
*/
function getLoginProviders(excludeDenyLocalLogin) {
if (excludeDenyLocalLogin) {
return _.filter(externalLoginInfo.providers, x => !x.properties.UmbracoBackOffice_DenyLocalLogin);
return _.filter(externalLoginInfo.providers, x => !x.properties.UmbracoBackOfficeExternalLoginOptions.DenyLocalLogin);
}
else {
return externalLoginInfo.providers;
}
}
/**
* If there is any external login providers with deny local login, check if any have a custom invite link and return it
* @param {any} provider optional to get the invite link directly from the provider, else will return the first one found (if any)
*/
function getUserInviteLink(provider) {
if (!provider) {
var denyLocalLoginProviders = _.filter(externalLoginInfo.providers, x => x.properties.UmbracoBackOfficeExternalLoginOptions.DenyLocalLogin);
var withInviteLink = _.filter(denyLocalLoginProviders, x => x.properties.UmbracoBackOfficeExternalLoginOptions.CustomUserInviteLink);
return withInviteLink.length > 0 ? withInviteLink[0].properties.UmbracoBackOfficeExternalLoginOptions.CustomUserInviteLink : null;
}
else {
return provider.properties.UmbracoBackOfficeExternalLoginOptions.CustomUserInviteLink;
}
}
return {
hasDenyLocalLogin: hasDenyLocalLogin,
getLoginProviders: getLoginProviders
getLoginProviders: getLoginProviders,
getUserInviteLink: getUserInviteLink
};
}
angular.module('umbraco.services').factory('externalLoginInfoService', externalLoginInfoService);

View File

@@ -50,7 +50,7 @@
<div ng-repeat="login in externalLoginProviders">
<div ng-if="!login.properties.UmbracoBackOffice_DenyLocalLogin">
<div ng-if="!login.properties.UmbracoBackOfficeExternalLoginOptions.DenyLocalLogin">
<form ng-submit="linkProvider($event)" ng-if="login.linkedProviderKey == undefined" method="POST" action="{{externalLinkLoginFormAction}}" name="oauthloginform" id="oauthloginform-{{login.authType}}">
<input type="hidden" name="provider" value="{{login.authType}}" />
<button class="btn btn-block btn-social"

View File

@@ -3,8 +3,10 @@
<umb-button
ng-if="defaultButton"
alias="{{defaultButton.alias ? defaultButton.alias : 'groupPrimary' }}"
type="button"
type="{{defaultButton.type}}"
action="defaultButton.handler()"
href="{{defaultButton.href}}"
href-target="{{defaultButton.hrefTarget}}"
button-style="{{buttonStyle}}"
state="state"
label="{{defaultButton.labelKey}}"

View File

@@ -9,6 +9,7 @@
<a ng-if="vm.type === 'link'"
ng-href="{{vm.href}}"
ng-attr-target="{{(vm.hrefTarget) ? vm.hrefTarget : undefined}}"
class="btn umb-button__button {{vm.style}} umb-button--{{vm.size}} umb-outline"
ng-click="vm.clickButton($event)"
hotkey="{{vm.shortcut}}"

View File

@@ -71,7 +71,7 @@
vm.denyLocalLogin = externalLoginInfoService.hasDenyLocalLogin();
// No buttons with denyLocalLogin
// No default buttons with denyLocalLogin
// Don't show the invite button if no email is configured
if (!vm.denyLocalLogin) {
if (Umbraco.Sys.ServerVariables.umbracoSettings.showUserInvite) {
@@ -99,6 +99,19 @@
};
}
}
else {
// if deny local login then check if there's an invite link in the config
var customInviteLink = externalLoginInfoService.getUserInviteLink();
if (customInviteLink) {
vm.defaultButton = {
type: "link",
labelKey: "user_inviteUser",
href: customInviteLink,
hrefTarget: "_blank"
};
}
}
vm.toggleFilter = toggleFilter;
vm.setUsersViewState = setUsersViewState;

View File

@@ -400,7 +400,7 @@ namespace Umbraco.Web.Editors
}
else
{
autoLinkOptions = authType.GetExternalAuthenticationOptions();
autoLinkOptions = authType.GetExternalSignInAutoLinkOptions();
}
// Sign in the user with this external login provider if the user already has a login

View File

@@ -54,14 +54,21 @@ namespace Umbraco.Web.Security
public static string GetAutoLoginProvider(this IAuthenticationManager manager)
{
var found = manager.GetExternalAuthenticationTypes()
.LastOrDefault(p => p.Properties.ContainsKey(Constants.Security.BackOfficeAuthenticationType) && p.Properties.ContainsKey(Constants.Security.BackOfficeExternalAuthenticationAutoLoginRedirectProperty));
.LastOrDefault(p => p.Properties.ContainsKey(Constants.Security.BackOfficeAuthenticationType)
&& p.Properties.TryGetValue(Constants.Security.BackOfficeExternalLoginOptionsProperty, out var options)
&& options is BackOfficeExternalLoginProviderOptions externalLoginProviderOptions
&& externalLoginProviderOptions.AutoRedirectLoginToExternalProvider);
return found?.AuthenticationType;
}
public static bool HasDenyLocalLogin(this IAuthenticationManager manager)
{
return manager.GetExternalAuthenticationTypes().Any(p => p.Properties.ContainsKey(Constants.Security.BackOfficeExternalAuthenticationDenyLocalLoginProperty));
return manager.GetExternalAuthenticationTypes()
.Any(p => p.Properties.ContainsKey(Constants.Security.BackOfficeAuthenticationType)
&& p.Properties.TryGetValue(Constants.Security.BackOfficeExternalLoginOptionsProperty, out var options)
&& options is BackOfficeExternalLoginProviderOptions externalLoginProviderOptions
&& externalLoginProviderOptions.DenyLocalLogin);
}
/// <summary>

View File

@@ -9,81 +9,62 @@ namespace Umbraco.Web.Security
{
public static class AuthenticationOptionsExtensions
{
// these are used for backwards compat
private const string ExternalSignInAutoLinkOptionsProperty = "ExternalSignInAutoLinkOptions";
private const string ChallengeResultCallbackProperty = "ChallengeResultCallback";
/// <summary>
/// When trying to implement an Azure AD B2C provider or other OAuth provider that requires a customized Challenge Result in order to work then
/// this must be used.
/// Used to specify all back office external login options
/// </summary>
/// <param name="authOptions"></param>
/// <param name="authProperties"></param>
/// <remarks>
/// See: http://issues.umbraco.org/issue/U4-7353
/// </remarks>
/// <param name="externalLoginProviderOptions"></param>
public static void SetBackOfficeExternalLoginProviderOptions(this AuthenticationOptions authOptions,
BackOfficeExternalLoginProviderOptions externalLoginProviderOptions)
{
authOptions.Description.Properties[Constants.Security.BackOfficeExternalLoginOptionsProperty] = externalLoginProviderOptions;
// for backwards compat, we need to add these:
if (externalLoginProviderOptions.AutoLinkOptions != null)
authOptions.Description.Properties[ExternalSignInAutoLinkOptionsProperty] = externalLoginProviderOptions.AutoLinkOptions;
if (externalLoginProviderOptions.OnChallenge != null)
authOptions.Description.Properties[ChallengeResultCallbackProperty] = externalLoginProviderOptions.OnChallenge;
}
[Obsolete("Use SetBackOfficeExternalLoginProviderOptions instead")]
public static void SetSignInChallengeResultCallback(
this AuthenticationOptions authOptions,
Func<IOwinContext, AuthenticationProperties> authProperties)
{
authOptions.Description.Properties["ChallengeResultCallback"] = authProperties;
authOptions.Description.Properties[ChallengeResultCallbackProperty] = authProperties;
}
public static AuthenticationProperties GetSignInChallengeResult(this AuthenticationDescription authenticationDescription, IOwinContext ctx)
{
if (authenticationDescription.Properties.ContainsKey("ChallengeResultCallback") == false) return null;
var cb = authenticationDescription.Properties["ChallengeResultCallback"] as Func<IOwinContext, AuthenticationProperties>;
if (authenticationDescription.Properties.ContainsKey(ChallengeResultCallbackProperty) == false) return null;
var cb = authenticationDescription.Properties[ChallengeResultCallbackProperty] as Func<IOwinContext, AuthenticationProperties>;
if (cb == null) return null;
return cb(ctx);
}
/// <summary>
/// Used during the External authentication process to assign external sign-in options
/// that are used by the Umbraco authentication process.
/// </summary>
/// <param name="authOptions"></param>
/// <param name="options"></param>
[Obsolete("Use SetBackOfficeExternalLoginProviderOptions instead")]
public static void SetExternalSignInAutoLinkOptions(
this AuthenticationOptions authOptions,
ExternalSignInAutoLinkOptions options)
{
authOptions.Description.Properties["ExternalSignInAutoLinkOptions"] = options;
authOptions.Description.Properties[ExternalSignInAutoLinkOptionsProperty] = options;
}
/// <summary>
/// Used during the External authentication process to retrieve external sign-in options
/// that have been set with SetExternalAuthenticationOptions
/// </summary>
/// <param name="authenticationDescription"></param>
[Obsolete("Use GetExternalSignInAutoLinkOptions instead")]
public static ExternalSignInAutoLinkOptions GetExternalAuthenticationOptions(this AuthenticationDescription authenticationDescription)
=> authenticationDescription.GetExternalSignInAutoLinkOptions();
public static ExternalSignInAutoLinkOptions GetExternalSignInAutoLinkOptions(this AuthenticationDescription authenticationDescription)
{
if (authenticationDescription.Properties.ContainsKey("ExternalSignInAutoLinkOptions") == false) return null;
var options = authenticationDescription.Properties["ExternalSignInAutoLinkOptions"] as ExternalSignInAutoLinkOptions;
if (authenticationDescription.Properties.ContainsKey(ExternalSignInAutoLinkOptionsProperty) == false) return null;
var options = authenticationDescription.Properties[ExternalSignInAutoLinkOptionsProperty] as ExternalSignInAutoLinkOptions;
return options;
}
/// <summary>
/// When set this will disable all local login ability within Umbraco
/// </summary>
/// <param name="options"></param>
/// <remarks>
/// Even if there are multiple OAuth providers installed if any of these specifies this option then all local login ability is disabled.
/// </remarks>
public static void DenyLocalLogin(this AuthenticationOptions options)
{
options.Description.Properties[Constants.Security.BackOfficeExternalAuthenticationDenyLocalLoginProperty] = true;
}
/// <summary>
/// When specified this will automatically redirect to the OAuth login provider instead of prompting the user to click on the OAuth button first.
/// </summary>
/// <param name="options"></param>
/// <remarks>
/// This is generally used in conjunction with <see cref="DenyLocalLogin(AuthenticationOptions)"/>. If more than one OAuth provider specifies this, the last registered
/// provider's redirect settings will win.
/// </remarks>
public static void AutoLoginRedirect(this AuthenticationOptions options)
{
options.Description.Properties[Constants.Security.BackOfficeExternalAuthenticationAutoLoginRedirectProperty] = true;
}
/// <summary>
/// Configures the properties of the authentication description instance for use with Umbraco back office
/// </summary>

View File

@@ -0,0 +1,49 @@
using Microsoft.Owin;
using Microsoft.Owin.Security;
using System;
using System.Runtime.Serialization;
namespace Umbraco.Web.Security
{
/// <summary>
/// Options used to configure back office external login providers
/// </summary>
public class BackOfficeExternalLoginProviderOptions
{
/// <summary>
/// When specified this will be called to retrieve the <see cref="AuthenticationProperties"/> used during the authentication Challenge response.
/// </summary>
/// <remarks>
/// For example, when trying to implement an Azure AD B2C provider or other OAuth provider that requires a customized Challenge Result in order to work then
/// this must be used.
/// See: http://issues.umbraco.org/issue/U4-7353
/// </remarks>
[IgnoreDataMember]
public Func<IOwinContext, AuthenticationProperties> OnChallenge { get; set; }
/// <summary>
/// Options used to control how users can be auto-linked/created/updated based on the external login provider
/// </summary>
[IgnoreDataMember] // we are ignoring this one from serialization for backwards compat since these options are manually incuded in the response separately
public ExternalSignInAutoLinkOptions AutoLinkOptions { get; set; }
/// <summary>
/// When set to true will disable all local user login functionality
/// </summary>
public bool DenyLocalLogin { get; set; }
/// <summary>
/// If specified and <see cref="DenyLocalLogin"/> is true then the user invite button in the back office will link through to this custom URL
/// </summary>
public string CustomUserInviteLink { get; set; }
/// <summary>
/// When specified this will automatically redirect to the OAuth login provider instead of prompting the user to click on the OAuth button first.
/// </summary>
/// <remarks>
/// This is generally used in conjunction with <see cref="DenyLocalLogin"/>. If more than one OAuth provider specifies this, the last registered
/// provider's redirect settings will win.
/// </remarks>
public bool AutoRedirectLoginToExternalProvider { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Runtime.Serialization;
using Microsoft.AspNet.Identity.Owin;
using Umbraco.Core;
using Umbraco.Core.Composing;
@@ -7,6 +8,7 @@ using Umbraco.Core.Models.Identity;
namespace Umbraco.Web.Security
{
/// <summary>
/// Options used to configure auto-linking external OAuth providers
/// </summary>
@@ -33,12 +35,14 @@ namespace Umbraco.Web.Security
/// <summary>
/// A callback executed during account auto-linking and before the user is persisted
/// </summary>
[IgnoreDataMember]
public Action<BackOfficeIdentityUser, ExternalLoginInfo> OnAutoLinking { get; set; }
/// <summary>
/// A callback executed during every time a user authenticates using an external login.
/// returns a boolean indicating if sign in should continue or not.
/// </summary>
[IgnoreDataMember]
public Func<BackOfficeIdentityUser, ExternalLoginInfo, bool> OnExternalLogin { get; set; }

View File

@@ -257,6 +257,7 @@
<Compile Include="Search\ExamineUserComponent.cs" />
<Compile Include="Search\IUmbracoTreeSearcherFields.cs" />
<Compile Include="Search\UmbracoTreeSearcherFields.cs" />
<Compile Include="Security\BackOfficeExternalLoginProviderOptions.cs" />
<Compile Include="Services\DashboardService.cs" />
<Compile Include="Services\IDashboardService.cs" />
<Compile Include="Models\Link.cs" />