diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 5a82951cab..47995cefc5 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -85,12 +85,6 @@ stages: retryCountOnTaskFailure: 3 inputs: versionSpec: $(nodeVersion) - - script: npm ci --no-fund --no-audit --prefer-offline - displayName: Run npm ci (Login) - workingDirectory: src/Umbraco.Web.UI.Login - - script: npm run build - displayName: Run npm build (Login) - workingDirectory: src/Umbraco.Web.UI.Login - script: npm ci --no-fund --no-audit --prefer-offline displayName: Run npm ci (Bellissima) workingDirectory: src/Umbraco.Web.UI.Client @@ -101,9 +95,12 @@ stages: - script: npm run build:for:cms displayName: Run build (Bellissima) workingDirectory: src/Umbraco.Web.UI.Client + - script: npm ci --no-fund --no-audit --prefer-offline + displayName: Run npm ci (Login) + workingDirectory: src/Umbraco.Web.UI.Login - script: npm run build - displayName: Run Login Build (Bellissima) - workingDirectory: src/Umbraco.Web.UI.Client/apps/auth + displayName: Run npm build (Login) + workingDirectory: src/Umbraco.Web.UI.Login - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: diff --git a/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs b/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs index 2cbe67e0c8..df6d8b80f8 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs @@ -22,9 +22,10 @@ public class } [ApiExplorerSettings(IgnoreApi=true)] -[Route("/umbraco/login")] +[Route(LoginPath)] public class BackOfficeLoginController : Controller { + public const string LoginPath = "/umbraco/login"; private readonly IHostingEnvironment _hostingEnvironment; private readonly GlobalSettings _globalSettings; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs index cca2e7277f..0bc4009364 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs @@ -12,10 +12,13 @@ using OpenIddict.Abstractions; using OpenIddict.Server.AspNetCore; using Umbraco.Cms.Api.Common.Builders; using Umbraco.Cms.Api.Management.Routing; +using Umbraco.Cms.Api.Management.Security; +using Umbraco.Cms.Api.Management.ViewModels.Security; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Authorization; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; @@ -34,23 +37,27 @@ public class BackOfficeController : SecurityControllerBase private readonly IBackOfficeUserManager _backOfficeUserManager; private readonly IOptions _securitySettings; private readonly ILogger _logger; + private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions; + private readonly IUserTwoFactorLoginService _userTwoFactorLoginService; public BackOfficeController( IHttpContextAccessor httpContextAccessor, IBackOfficeSignInManager backOfficeSignInManager, IBackOfficeUserManager backOfficeUserManager, IOptions securitySettings, - ILogger logger) + ILogger logger, + IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions, + IUserTwoFactorLoginService userTwoFactorLoginService) { _httpContextAccessor = httpContextAccessor; _backOfficeSignInManager = backOfficeSignInManager; _backOfficeUserManager = backOfficeUserManager; _securitySettings = securitySettings; _logger = logger; + _backOfficeTwoFactorOptions = backOfficeTwoFactorOptions; + _userTwoFactorLoginService = userTwoFactorLoginService; } - // FIXME: this is a temporary solution to get the new backoffice auth rolling. - // once the old backoffice auth is no longer necessary, clean this up and merge with 2FA handling etc. [HttpPost("login")] [MapToApiVersion("1.0")] [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] @@ -81,10 +88,14 @@ public class BackOfficeController : SecurityControllerBase } if(result.RequiresTwoFactor) { - return StatusCode(StatusCodes.Status402PaymentRequired, new ProblemDetailsBuilder() - .WithTitle("2FA Required") - .WithDetail("The user is protected by 2FA. Please continue the login process and verify a 2FA code.") - .Build()); + string? twofactorView = _backOfficeTwoFactorOptions.GetTwoFactorView(model.Username); + BackOfficeIdentityUser? attemptingUser = await _backOfficeUserManager.FindByNameAsync(model.Username); + IEnumerable enabledProviders = (await _userTwoFactorLoginService.GetProviderNamesAsync(attemptingUser!.Key)).Result.Where(x=>x.IsEnabledOnUser).Select(x=>x.ProviderName); + return StatusCode(StatusCodes.Status402PaymentRequired, new RequiresTwoFactorResponseModel() + { + TwoFactorLoginView = twofactorView, + EnabledTwoFactorProviderNames = enabledProviders + }); } return Ok(); } @@ -133,13 +144,6 @@ public class BackOfficeController : SecurityControllerBase .Build()); } - public class LoginRequestModel - { - public required string Username { get; init; } - - public required string Password { get; init; } - } - [AllowAnonymous] [HttpGet("authorize")] [MapToApiVersion("1.0")] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs index b4791de9a9..5418b06b2e 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.Builders; +using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.Filters; using Umbraco.Cms.Api.Management.ViewModels.Security; using Umbraco.Cms.Core; @@ -15,13 +16,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.Security; public class VerifyResetPasswordTokenController : SecurityControllerBase { private readonly IUserService _userService; + private readonly IPasswordConfigurationPresentationFactory _passwordConfigurationPresentationFactory; - public VerifyResetPasswordTokenController(IUserService userService) => _userService = userService; + public VerifyResetPasswordTokenController(IUserService userService, IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory) + { + _userService = userService; + _passwordConfigurationPresentationFactory = passwordConfigurationPresentationFactory; + } [AllowAnonymous] [HttpPost("forgot-password/verify")] [MapToApiVersion("1.0")] - [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(VerifyResetPasswordResponseModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetailsBuilder), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetailsBuilder), StatusCodes.Status404NotFound)] [UserPasswordEnsureMinimumResponseTime] @@ -30,7 +36,10 @@ public class VerifyResetPasswordTokenController : SecurityControllerBase Attempt result = await _userService.VerifyPasswordResetAsync(model.User.Id, model.ResetCode); return result.Success - ? NoContent() + ? Ok(new VerifyResetPasswordResponseModel() + { + PasswordConfiguration = _passwordConfigurationPresentationFactory.CreatePasswordConfigurationResponseModel(), + }) : UserOperationStatusResult(result.Result); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs index 694d5d5735..827f4b6726 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs @@ -19,6 +19,7 @@ public class CreateInitialPasswordUserController : UserControllerBase public CreateInitialPasswordUserController(IUserService userService) => _userService = userService; + [AllowAnonymous] [HttpPost("invite/create-password")] [MapToApiVersion("1.0")] [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/VerifyInviteUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/VerifyInviteUserController.cs index 9e67cbc971..a607f896ed 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/User/VerifyInviteUserController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/VerifyInviteUserController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.User; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Services; @@ -13,13 +14,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.User; public class VerifyInviteUserController : UserControllerBase { private readonly IUserService _userService; + private readonly IPasswordConfigurationPresentationFactory _passwordConfigurationPresentationFactory; - public VerifyInviteUserController(IUserService userService) => _userService = userService; + public VerifyInviteUserController(IUserService userService, IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory) + { + _userService = userService; + _passwordConfigurationPresentationFactory = passwordConfigurationPresentationFactory; + } [AllowAnonymous] [HttpPost("invite/verify")] [MapToApiVersion("1.0")] - [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(typeof(VerifyInviteUserResponseModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] public async Task Invite(VerifyInviteUserRequestModel model) @@ -27,7 +33,10 @@ public class VerifyInviteUserController : UserControllerBase Attempt result = await _userService.VerifyInviteAsync(model.User.Id, model.Token); return result.Success - ? Ok() + ? Ok(new VerifyInviteUserResponseModel() + { + PasswordConfiguration = _passwordConfigurationPresentationFactory.CreatePasswordConfigurationResponseModel(), + }) : UserOperationStatusResult(result.Result); } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 5d1ada7cd6..cbda8a5d6b 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -25076,8 +25076,8 @@ } }, "responses": { - "204": { - "description": "No Content", + "200": { + "description": "Success", "headers": { "Umb-Notifications": { "description": "The list of notifications produced during the request.", @@ -25089,6 +25089,35 @@ "nullable": true } } + }, + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VerifyResetPasswordResponseModel" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VerifyResetPasswordResponseModel" + } + ] + } + }, + "text/plain": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VerifyResetPasswordResponseModel" + } + ] + } + } } }, "400": { @@ -33079,16 +33108,8 @@ } } } - }, - "401": { - "description": "The resource is protected and requires an authentication token" } - }, - "security": [ - { - "Backoffice User": [ ] - } - ] + } } }, "/umbraco/management/api/v1/user/invite/resend": { @@ -33279,6 +33300,35 @@ "nullable": true } } + }, + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VerifyInviteUserResponseModel" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VerifyInviteUserResponseModel" + } + ] + } + }, + "text/plain": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VerifyInviteUserResponseModel" + } + ] + } + } } }, "404": { @@ -44099,6 +44149,38 @@ }, "additionalProperties": false }, + "VerifyInviteUserResponseModel": { + "required": [ + "passwordConfiguration" + ], + "type": "object", + "properties": { + "passwordConfiguration": { + "oneOf": [ + { + "$ref": "#/components/schemas/PasswordConfigurationResponseModel" + } + ] + } + }, + "additionalProperties": false + }, + "VerifyResetPasswordResponseModel": { + "required": [ + "passwordConfiguration" + ], + "type": "object", + "properties": { + "passwordConfiguration": { + "oneOf": [ + { + "$ref": "#/components/schemas/PasswordConfigurationResponseModel" + } + ] + } + }, + "additionalProperties": false + }, "VerifyResetPasswordTokenRequestModel": { "required": [ "resetCode", diff --git a/src/Umbraco.Cms.Api.Management/Security/ForgotPasswordUriProvider.cs b/src/Umbraco.Cms.Api.Management/Security/ForgotPasswordUriProvider.cs index 0c43933014..f13d82713b 100644 --- a/src/Umbraco.Cms.Api.Management/Security/ForgotPasswordUriProvider.cs +++ b/src/Umbraco.Cms.Api.Management/Security/ForgotPasswordUriProvider.cs @@ -1,9 +1,9 @@ -using System.Net; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services.OperationStatus; @@ -13,21 +13,19 @@ namespace Umbraco.Cms.Api.Management.Security; public class ForgotPasswordUriProvider : IForgotPasswordUriProvider { - private readonly LinkGenerator _linkGenerator; + private readonly ICoreBackOfficeUserManager _userManager; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly WebRoutingSettings _webRoutingSettings; public ForgotPasswordUriProvider( - LinkGenerator linkGenerator, ICoreBackOfficeUserManager userManager, - IHttpContextAccessor httpContextAccessor, - IOptions webRoutingSettings) + IHostingEnvironment hostingEnvironment, + IHttpContextAccessor httpContextAccessor) { - _linkGenerator = linkGenerator; _userManager = userManager; + _hostingEnvironment = hostingEnvironment; _httpContextAccessor = httpContextAccessor; - _webRoutingSettings = webRoutingSettings.Value; } public async Task> CreateForgotPasswordUriAsync(IUser user) @@ -39,21 +37,21 @@ public class ForgotPasswordUriProvider : IForgotPasswordUriProvider return Attempt.FailWithStatus(tokenAttempt.Status, new Uri(string.Empty)); } - string forgotPasswordToken = $"{user.Key}{WebUtility.UrlEncode("|")}{tokenAttempt.Result.ToUrlBase64()}"; + HttpRequest? request = _httpContextAccessor.HttpContext?.Request; + if (request is null) + { + throw new NotSupportedException("Needs a HttpContext"); + } - // FIXME: This will need to change. - // string? action = _linkGenerator.GetPathByAction( - // nameof(BackOfficeController.ValidatePasswordResetCode), - // ControllerExtensions.GetControllerName(), - // new { area = Constants.Web.Mvc.BackOfficeArea, invite = forgotPasswordToken }); - string action = string.Empty; + var uriBuilder = new UriBuilder(_hostingEnvironment.ApplicationMainUrl); + uriBuilder.Path = BackOfficeLoginController.LoginPath; + uriBuilder.Query = QueryString.Create(new KeyValuePair[] + { + new ("flow", "reset-password"), + new ("userId", user.Key.ToString()), + new ("resetCode", tokenAttempt.Result.ToUrlBase64()), + }).ToUriComponent(); - Uri applicationUri = _httpContextAccessor - .GetRequiredHttpContext() - .Request - .GetApplicationUri(_webRoutingSettings); - - var forgotPasswordUri = new Uri(applicationUri, action); - return Attempt.SucceedWithStatus(UserOperationStatus.Success, forgotPasswordUri); + return Attempt.SucceedWithStatus(UserOperationStatus.Success, uriBuilder.Uri); } } diff --git a/src/Umbraco.Cms.Api.Management/Security/InviteUriProvider.cs b/src/Umbraco.Cms.Api.Management/Security/InviteUriProvider.cs index b5b9c51cab..b2d78e513e 100644 --- a/src/Umbraco.Cms.Api.Management/Security/InviteUriProvider.cs +++ b/src/Umbraco.Cms.Api.Management/Security/InviteUriProvider.cs @@ -1,9 +1,6 @@ -using System.Net; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Http; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services.OperationStatus; @@ -13,21 +10,19 @@ namespace Umbraco.Cms.Api.Management.Security; public class InviteUriProvider : IInviteUriProvider { - private readonly LinkGenerator _linkGenerator; private readonly ICoreBackOfficeUserManager _userManager; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly WebRoutingSettings _webRoutingSettings; + private readonly IHostingEnvironment _hostingEnvironment; public InviteUriProvider( - LinkGenerator linkGenerator, ICoreBackOfficeUserManager userManager, IHttpContextAccessor httpContextAccessor, - IOptions webRoutingSettings) + IHostingEnvironment hostingEnvironment) { - _linkGenerator = linkGenerator; + _userManager = userManager; _httpContextAccessor = httpContextAccessor; - _webRoutingSettings = webRoutingSettings.Value; + _hostingEnvironment = hostingEnvironment; } public async Task> CreateInviteUriAsync(IUser invitee) @@ -39,21 +34,21 @@ public class InviteUriProvider : IInviteUriProvider return Attempt.FailWithStatus(tokenAttempt.Status, new Uri(string.Empty)); } - string inviteToken = $"{invitee.Key}{WebUtility.UrlEncode("|")}{tokenAttempt.Result.ToUrlBase64()}"; + HttpRequest? request = _httpContextAccessor.HttpContext?.Request; + if (request is null) + { + throw new NotSupportedException("Needs a HttpContext"); + } - // FIXME: This will need to change. - // string? action = _linkGenerator.GetPathByAction( - // nameof(BackOfficeController.VerifyInvite), - // ControllerExtensions.GetControllerName(), - // new { area = Constants.Web.Mvc.BackOfficeArea, invite = inviteToken }); - string action = string.Empty; + var uriBuilder = new UriBuilder(_hostingEnvironment.ApplicationMainUrl); + uriBuilder.Path = BackOfficeLoginController.LoginPath; + uriBuilder.Query = QueryString.Create(new KeyValuePair[] + { + new ("flow", "invite-user"), + new ("userId", invitee.Key.ToString()), + new ("inviteCode", tokenAttempt.Result.ToUrlBase64()), + }).ToUriComponent(); - Uri applicationUri = _httpContextAccessor - .GetRequiredHttpContext() - .Request - .GetApplicationUri(_webRoutingSettings); - - var inviteUri = new Uri(applicationUri, action); - return Attempt.SucceedWithStatus(UserOperationStatus.Success, inviteUri); + return Attempt.SucceedWithStatus(UserOperationStatus.Success, uriBuilder.Uri); } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Security/LoginRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Security/LoginRequestModel.cs new file mode 100644 index 0000000000..1cae670efd --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Security/LoginRequestModel.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.Security; + +public class LoginRequestModel +{ + public required string Username { get; init; } + + public required string Password { get; init; } +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Security/RequiresTwoFactorResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Security/RequiresTwoFactorResponseModel.cs new file mode 100644 index 0000000000..5f6dc4985e --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Security/RequiresTwoFactorResponseModel.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.Security; + +public class RequiresTwoFactorResponseModel +{ + public string? TwoFactorLoginView { get; set; } + public required IEnumerable EnabledTwoFactorProviderNames { get; set; } +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Security/VerifyResetPasswordTokenResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Security/VerifyResetPasswordTokenResponseModel.cs new file mode 100644 index 0000000000..31ce3077ca --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Security/VerifyResetPasswordTokenResponseModel.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.Security; + +public class VerifyResetPasswordResponseModel +{ + public required PasswordConfigurationResponseModel PasswordConfiguration { get; set; } +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/User/VerifyInviteUserResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/User/VerifyInviteUserResponseModel.cs new file mode 100644 index 0000000000..39a407b21f --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/User/VerifyInviteUserResponseModel.cs @@ -0,0 +1,8 @@ +using Umbraco.Cms.Api.Management.ViewModels.Security; + +namespace Umbraco.Cms.Api.Management.ViewModels.User; + +public class VerifyInviteUserResponseModel +{ + public required PasswordConfigurationResponseModel PasswordConfiguration { get; set; } +} diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj index 2436754ad2..ff487ca66a 100644 --- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj +++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj @@ -25,9 +25,9 @@ - - - + + + @@ -36,7 +36,6 @@ - @@ -48,10 +47,10 @@ - - - - + + + + @@ -63,7 +62,7 @@ - + diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml index e53a5d4a39..a063d29f6c 100644 --- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml @@ -1,20 +1,102 @@ @using System.Globalization -@model Umbraco.Cms.Api.Management.BackOfficeLoginModel +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using Microsoft.Extensions.Options; +@using Umbraco.Cms.Api.Management.Extensions +@using Umbraco.Cms.Api.Management.Security +@using Umbraco.Cms.Core.Configuration.Models +@using Umbraco.Cms.Core.Hosting +@using Umbraco.Cms.Core.Logging +@using Umbraco.Cms.Core.Mail +@using Umbraco.Cms.Core.Manifest +@using Umbraco.Cms.Core.Serialization +@using Umbraco.Cms.Web.Common.Hosting +@using Umbraco.Extensions +@inject IOptions ContentSettings +@inject IOptions SecuritySettings +@inject IEmailSender EmailSender +@inject IHostingEnvironment HostingEnvironment +@inject IOptions GlobalSettings +@inject IProfilerHtml ProfilerHtml +@inject IBackOfficeExternalLoginProviders ExternalLogins +@inject IBackOfficePathGenerator BackOfficePathGenerator +@inject IPackageManifestService PackageManifestService +@inject IJsonSerializer JsonSerializer +@{ + bool.TryParse(Context.Request.Query["umbDebug"], out var isDebug); + var backOfficePath = GlobalSettings.Value.GetBackOfficePath(HostingEnvironment); + var loginLogoImage = ContentSettings.Value.LoginLogoImage; + var loginLogoImageAlternative = ContentSettings.Value.LoginLogoImageAlternative; + var loginBackgroundImage = ContentSettings.Value.LoginBackgroundImage; + var usernameIsEmail = SecuritySettings.Value.UsernameIsEmail; + var allowUserInvite = EmailSender.CanSendRequiredEmail(); + var allowPasswordReset = SecuritySettings.Value.AllowPasswordReset && EmailSender.CanSendRequiredEmail(); + var disableLocalLogin = ExternalLogins.HasDenyLocalLogin(); +} @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers - - - - - - Umbraco - - - - + + + + + + + + + + Umbraco - - - + + + + + @await Html.BackOfficeImportMapScriptAsync(JsonSerializer, BackOfficePathGenerator, PackageManifestService) + + + + + + + + + +@if (isDebug) +{ + @Html.Raw(ProfilerHtml.Render()) +} + diff --git a/src/Umbraco.Infrastructure/Security/PasswordChanger.cs b/src/Umbraco.Infrastructure/Security/PasswordChanger.cs index 03d298ffda..b3de060b42 100644 --- a/src/Umbraco.Infrastructure/Security/PasswordChanger.cs +++ b/src/Umbraco.Infrastructure/Security/PasswordChanger.cs @@ -68,7 +68,7 @@ internal class PasswordChanger : IPasswordChanger where TUser : Um // If old password is not specified we either have to change another user's password, or provide a reset password token if (changingPasswordModel.OldPassword.IsNullOrWhiteSpace()) { - if (changingPasswordModel.Id == currentUser?.Id && changingPasswordModel.ResetPasswordToken is null) + if (changingPasswordModel.Id == currentUser?.Id && changingPasswordModel.ResetPasswordToken is null && currentUser.UserState != UserState.Invited) { return Attempt.Fail(new PasswordChangedModel { diff --git a/src/Umbraco.Web.UI.Login/index.html b/src/Umbraco.Web.UI.Login/index.html index 4c0be79720..32f4e84099 100644 --- a/src/Umbraco.Web.UI.Login/index.html +++ b/src/Umbraco.Web.UI.Login/index.html @@ -1,5 +1,5 @@ - + @@ -9,14 +9,14 @@ Umbraco - + - + - - - - - - - + diff --git a/src/Umbraco.Web.UI.Login/package-lock.json b/src/Umbraco.Web.UI.Login/package-lock.json index 50e4e585ce..04109b26cc 100644 --- a/src/Umbraco.Web.UI.Login/package-lock.json +++ b/src/Umbraco.Web.UI.Login/package-lock.json @@ -6,15 +6,13 @@ "": { "name": "login", "dependencies": { - "lit": "^3.1.2", - "msw": "^2.2.0", - "rxjs": "^7.8.1" + "msw": "^2.2.0" }, "devDependencies": { - "@umbraco-ui/uui": "1.7.1", - "@umbraco-ui/uui-css": "1.7.0", + "@umbraco-ui/uui-css": "1.7.2", "typescript": "^5.3.3", - "vite": "^5.1.1" + "vite": "^5.2.2", + "vite-tsconfig-paths": "^4.3.2" }, "engines": { "node": ">=20.8", @@ -38,9 +36,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", "cpu": [ "ppc64" ], @@ -54,9 +52,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", "cpu": [ "arm" ], @@ -70,9 +68,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", "cpu": [ "arm64" ], @@ -86,9 +84,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", "cpu": [ "x64" ], @@ -102,9 +100,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", "cpu": [ "arm64" ], @@ -118,9 +116,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", "cpu": [ "x64" ], @@ -134,9 +132,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", "cpu": [ "arm64" ], @@ -150,9 +148,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", "cpu": [ "x64" ], @@ -166,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", "cpu": [ "arm" ], @@ -182,9 +180,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", "cpu": [ "arm64" ], @@ -198,9 +196,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", "cpu": [ "ia32" ], @@ -214,9 +212,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", "cpu": [ "loong64" ], @@ -230,9 +228,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", "cpu": [ "mips64el" ], @@ -246,9 +244,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", "cpu": [ "ppc64" ], @@ -262,9 +260,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", "cpu": [ "riscv64" ], @@ -278,9 +276,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", "cpu": [ "s390x" ], @@ -294,9 +292,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", "cpu": [ "x64" ], @@ -310,9 +308,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", "cpu": [ "x64" ], @@ -326,9 +324,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", "cpu": [ "x64" ], @@ -342,9 +340,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", "cpu": [ "x64" ], @@ -358,9 +356,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", "cpu": [ "arm64" ], @@ -374,9 +372,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", "cpu": [ "ia32" ], @@ -390,9 +388,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "cpu": [ "x64" ], @@ -465,12 +463,16 @@ "node_modules/@lit-labs/ssr-dom-shim": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz", - "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==" + "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==", + "dev": true, + "peer": true }, "node_modules/@lit/reactive-element": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "dev": true, + "peer": true, "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0" } @@ -519,9 +521,9 @@ "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.10.0.tgz", - "integrity": "sha512-/MeDQmcD96nVoRumKUljsYOLqfv1YFJps+0pTrb2Z9Nl/w5qNUysMaWQsrd1mvAlNT4yza1iVyIu4Q4AgF6V3A==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", "cpu": [ "arm" ], @@ -532,9 +534,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.10.0.tgz", - "integrity": "sha512-lvu0jK97mZDJdpZKDnZI93I0Om8lSDaiPx3OiCk0RXn3E8CMPJNS/wxjAvSJJzhhZpfjXsjLWL8LnS6qET4VNQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", "cpu": [ "arm64" ], @@ -545,9 +547,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.10.0.tgz", - "integrity": "sha512-uFpayx8I8tyOvDkD7X6n0PriDRWxcqEjqgtlxnUA/G9oS93ur9aZ8c8BEpzFmsed1TH5WZNG5IONB8IiW90TQg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", "cpu": [ "arm64" ], @@ -558,9 +560,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.10.0.tgz", - "integrity": "sha512-nIdCX03qFKoR/MwQegQBK+qZoSpO3LESurVAC6s6jazLA1Mpmgzo3Nj3H1vydXp/JM29bkCiuF7tDuToj4+U9Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", "cpu": [ "x64" ], @@ -571,9 +573,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.10.0.tgz", - "integrity": "sha512-Fz7a+y5sYhYZMQFRkOyCs4PLhICAnxRX/GnWYReaAoruUzuRtcf+Qnw+T0CoAWbHCuz2gBUwmWnUgQ67fb3FYw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", "cpu": [ "arm" ], @@ -584,9 +586,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.10.0.tgz", - "integrity": "sha512-yPtF9jIix88orwfTi0lJiqINnlWo6p93MtZEoaehZnmCzEmLL0eqjA3eGVeyQhMtxdV+Mlsgfwhh0+M/k1/V7Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", "cpu": [ "arm64" ], @@ -597,9 +599,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.10.0.tgz", - "integrity": "sha512-9GW9yA30ib+vfFiwjX+N7PnjTnCMiUffhWj4vkG4ukYv1kJ4T9gHNg8zw+ChsOccM27G9yXrEtMScf1LaCuoWQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", "cpu": [ "arm64" ], @@ -610,9 +612,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.10.0.tgz", - "integrity": "sha512-X1ES+V4bMq2ws5fF4zHornxebNxMXye0ZZjUrzOrf7UMx1d6wMQtfcchZ8SqUnQPPHdOyOLW6fTcUiFgHFadRA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", "cpu": [ "riscv64" ], @@ -623,9 +625,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.10.0.tgz", - "integrity": "sha512-w/5OpT2EnI/Xvypw4FIhV34jmNqU5PZjZue2l2Y3ty1Ootm3SqhI+AmfhlUYGBTd9JnpneZCDnt3uNOiOBkMyw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", "cpu": [ "x64" ], @@ -636,9 +638,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.10.0.tgz", - "integrity": "sha512-q/meftEe3QlwQiGYxD9rWwB21DoKQ9Q8wA40of/of6yGHhZuGfZO0c3WYkN9dNlopHlNT3mf5BPsUSxoPuVQaw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", "cpu": [ "x64" ], @@ -649,9 +651,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.10.0.tgz", - "integrity": "sha512-NrR6667wlUfP0BHaEIKgYM/2va+Oj+RjZSASbBMnszM9k+1AmliRjHc3lJIiOehtSSjqYiO7R6KLNrWOX+YNSQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", "cpu": [ "arm64" ], @@ -662,9 +664,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.10.0.tgz", - "integrity": "sha512-FV0Tpt84LPYDduIDcXvEC7HKtyXxdvhdAOvOeWMWbQNulxViH2O07QXkT/FffX4FqEI02jEbCJbr+YcuKdyyMg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", "cpu": [ "ia32" ], @@ -675,9 +677,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.10.0.tgz", - "integrity": "sha512-OZoJd+o5TaTSQeFFQ6WjFCiltiYVjIdsXxwu/XZ8qRpsvMQr4UsVrE5UyT9RIvsnuF47DqkJKhhVZ2Q9YW9IpQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", "cpu": [ "x64" ], @@ -722,907 +724,24 @@ "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "peer": true }, "node_modules/@types/wrap-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==" }, - "node_modules/@umbraco-ui/uui": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui/-/uui-1.7.1.tgz", - "integrity": "sha512-wHGMW8NQaWJTdbbb7r03sah2Esab4Iy8bFWaTU+UtnrOpNsZclPwxZ3kZcjHnFu32xDFFBF0+iQiCki8Uy4dkA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-action-bar": "1.7.0", - "@umbraco-ui/uui-avatar": "1.7.0", - "@umbraco-ui/uui-avatar-group": "1.7.0", - "@umbraco-ui/uui-badge": "1.7.0", - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-boolean-input": "1.7.0", - "@umbraco-ui/uui-box": "1.7.0", - "@umbraco-ui/uui-breadcrumbs": "1.7.0", - "@umbraco-ui/uui-button": "1.7.1", - "@umbraco-ui/uui-button-group": "1.7.0", - "@umbraco-ui/uui-button-inline-create": "1.7.0", - "@umbraco-ui/uui-card": "1.7.0", - "@umbraco-ui/uui-card-block-type": "1.7.0", - "@umbraco-ui/uui-card-content-node": "1.7.0", - "@umbraco-ui/uui-card-media": "1.7.0", - "@umbraco-ui/uui-card-user": "1.7.0", - "@umbraco-ui/uui-caret": "1.7.0", - "@umbraco-ui/uui-checkbox": "1.7.0", - "@umbraco-ui/uui-color-area": "1.7.0", - "@umbraco-ui/uui-color-picker": "1.7.0", - "@umbraco-ui/uui-color-slider": "1.7.0", - "@umbraco-ui/uui-color-swatch": "1.7.0", - "@umbraco-ui/uui-color-swatches": "1.7.0", - "@umbraco-ui/uui-combobox": "1.7.1", - "@umbraco-ui/uui-combobox-list": "1.7.0", - "@umbraco-ui/uui-css": "1.7.0", - "@umbraco-ui/uui-dialog": "1.7.0", - "@umbraco-ui/uui-dialog-layout": "1.7.0", - "@umbraco-ui/uui-file-dropzone": "1.7.0", - "@umbraco-ui/uui-file-preview": "1.7.0", - "@umbraco-ui/uui-form": "1.7.0", - "@umbraco-ui/uui-form-layout-item": "1.7.0", - "@umbraco-ui/uui-form-validation-message": "1.7.0", - "@umbraco-ui/uui-icon": "1.7.0", - "@umbraco-ui/uui-icon-registry": "1.7.0", - "@umbraco-ui/uui-icon-registry-essential": "1.7.0", - "@umbraco-ui/uui-input": "1.7.0", - "@umbraco-ui/uui-input-file": "1.7.1", - "@umbraco-ui/uui-input-lock": "1.7.1", - "@umbraco-ui/uui-input-password": "1.7.0", - "@umbraco-ui/uui-keyboard-shortcut": "1.7.0", - "@umbraco-ui/uui-label": "1.7.0", - "@umbraco-ui/uui-loader": "1.7.0", - "@umbraco-ui/uui-loader-bar": "1.7.0", - "@umbraco-ui/uui-loader-circle": "1.7.0", - "@umbraco-ui/uui-menu-item": "1.7.0", - "@umbraco-ui/uui-modal": "1.7.0", - "@umbraco-ui/uui-pagination": "1.7.1", - "@umbraco-ui/uui-popover": "1.7.0", - "@umbraco-ui/uui-popover-container": "1.7.0", - "@umbraco-ui/uui-progress-bar": "1.7.0", - "@umbraco-ui/uui-radio": "1.7.0", - "@umbraco-ui/uui-range-slider": "1.7.0", - "@umbraco-ui/uui-ref": "1.7.0", - "@umbraco-ui/uui-ref-list": "1.7.0", - "@umbraco-ui/uui-ref-node": "1.7.0", - "@umbraco-ui/uui-ref-node-data-type": "1.7.0", - "@umbraco-ui/uui-ref-node-document-type": "1.7.0", - "@umbraco-ui/uui-ref-node-form": "1.7.0", - "@umbraco-ui/uui-ref-node-member": "1.7.0", - "@umbraco-ui/uui-ref-node-package": "1.7.0", - "@umbraco-ui/uui-ref-node-user": "1.7.0", - "@umbraco-ui/uui-scroll-container": "1.7.0", - "@umbraco-ui/uui-select": "1.7.0", - "@umbraco-ui/uui-slider": "1.7.0", - "@umbraco-ui/uui-symbol-expand": "1.7.0", - "@umbraco-ui/uui-symbol-file": "1.7.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.7.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.7.0", - "@umbraco-ui/uui-symbol-folder": "1.7.0", - "@umbraco-ui/uui-symbol-lock": "1.7.0", - "@umbraco-ui/uui-symbol-more": "1.7.0", - "@umbraco-ui/uui-symbol-sort": "1.7.0", - "@umbraco-ui/uui-table": "1.7.0", - "@umbraco-ui/uui-tabs": "1.7.1", - "@umbraco-ui/uui-tag": "1.7.0", - "@umbraco-ui/uui-textarea": "1.7.0", - "@umbraco-ui/uui-toast-notification": "1.7.1", - "@umbraco-ui/uui-toast-notification-container": "1.7.1", - "@umbraco-ui/uui-toast-notification-layout": "1.7.0", - "@umbraco-ui/uui-toggle": "1.7.0", - "@umbraco-ui/uui-visually-hidden": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-action-bar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-action-bar/-/uui-action-bar-1.7.0.tgz", - "integrity": "sha512-Lw067iEU4DihiOsL3cg2QqE4x7B7bqjYQK0EouBbD+mhJaE2IOw5eve2UIBN1KU/iQ+7V9q4qa++is1nitvUWA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-button-group": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-avatar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar/-/uui-avatar-1.7.0.tgz", - "integrity": "sha512-cW3qTTarFqXK4Ze5xMERo9pj3pRRKTvTDB57a5uA0gQ1/70uhgPnozWSX7EK22ml4w/5pmtxXXgRKfSiU9DGtQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-avatar-group": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar-group/-/uui-avatar-group-1.7.0.tgz", - "integrity": "sha512-TFDR0Mb+ug1NzVXq9RnxMiQ9pcxBcmzfOoxpR1NWMB/sAgNs/H/pTTqjieLel0/A5Am9q//8f7f9vmhTPpybGg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-avatar": "1.7.0", - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-badge": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-badge/-/uui-badge-1.7.0.tgz", - "integrity": "sha512-cdPHjXMag8KkYLaWfyYfp9N1qqG+th2Ijx7ms8EpTHAX2gtU1L/A3ShxWox0Ck1TJ75jrW66+HrqiMwDOmbn6g==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-base": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-base/-/uui-base-1.7.0.tgz", - "integrity": "sha512-66aDdgTrq2nx4BNzM9A/lc9dZYz/fyX5OVpkQDRsrpYeOLJMN3oOnE7aChIdBNW3I9lfVNJf6fh0iL27K5JCiQ==", - "dev": true, - "peerDependencies": { - "lit": ">=2.8.0" - } - }, - "node_modules/@umbraco-ui/uui-boolean-input": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-boolean-input/-/uui-boolean-input-1.7.0.tgz", - "integrity": "sha512-Hp6wOFqFLaZU0oW63GlMJ8s4va/TG+i7Sjs0qT91//5iJhJtpvgwY3j4ARoDfk0d8rKRIapiPT+hNMo9xr1sfQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-box": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-box/-/uui-box-1.7.0.tgz", - "integrity": "sha512-1P13tsVJXPEpMiHrw1FmsM0dvCLce8DevZAcP1ArDwtqWrwdArR2eRwlhVEZYu2MJkR2tESE3XGTaSOWHyC8og==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-css": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-breadcrumbs": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-breadcrumbs/-/uui-breadcrumbs-1.7.0.tgz", - "integrity": "sha512-y0sB4UypNwCle9qPlQ7Y++a4BkmFpn9vSTeJ6WRWueVyjKT99icmCV1c8/Q47blMajp0FLG2/ajevxg/aZSO4Q==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-button": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button/-/uui-button-1.7.1.tgz", - "integrity": "sha512-z2nZccn/Hel2QvytWVejDzqjNPRLJ/jLgCmLpgHoKU2IlckEgZqy4wxKcgH2Iu2bJ+wgIwpAAmlidLK0LX0tCw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-icon-registry-essential": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-button-group": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-group/-/uui-button-group-1.7.0.tgz", - "integrity": "sha512-CM0sytzzEXiDmFfB6GXnmQw5LzCNuwSo66BC6zYI4cg1+mk2a1UBu1Z8CVpvS3tsTkzk/nGd/ZFKkoIziDVKJg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-button-inline-create": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-inline-create/-/uui-button-inline-create-1.7.0.tgz", - "integrity": "sha512-SVep/tcsTJuO8jvZIX0e3EOaY1S+sOk0ZFmq+HxUJDt6csFjXsqJO48DjIon1AKq95ATTM9Iqs/hPSDVHrqNvw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-card": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card/-/uui-card-1.7.0.tgz", - "integrity": "sha512-BBWJ62UH1dmcHvZ3H0fRCnM9c+ebQMNaZlGDDWP5lPfv+2KjXXkLRuj6tPUthHa53e5Rf6AAKjKsjRssM4dsLQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-card-block-type": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-block-type/-/uui-card-block-type-1.7.0.tgz", - "integrity": "sha512-SrLgooo2imluSV8S3bp+0kA2K7zuMDAXZTuzQJRf2hzq208In65D5rBxn8OcEJsGD3lHMp6+w8rg8Ol5NlEbXA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-card": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-card-content-node": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-content-node/-/uui-card-content-node-1.7.0.tgz", - "integrity": "sha512-wkb9BaUfuZkrMczsm1q4vuP0zSOp0gfiiiXCxFRDNmWJc3jKiL3zF619PzviEZrz10/f7WRnA7MLfDgsAmQpAQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-card": "1.7.0", - "@umbraco-ui/uui-icon": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-card-media": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-media/-/uui-card-media-1.7.0.tgz", - "integrity": "sha512-Jwli2j//U1v4zG5fvkrekduf3qCa5w0RNP28RBxeqqQKzO8B5UpWqIP86/qaV7hvlp/ZuTCYrdkeWLgUV85tBg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-card": "1.7.0", - "@umbraco-ui/uui-symbol-file": "1.7.0", - "@umbraco-ui/uui-symbol-folder": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-card-user": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-user/-/uui-card-user-1.7.0.tgz", - "integrity": "sha512-4fBXEICxi4ICAM2wn40DrUV1pPGSDFJmzacOA1PXxb1pzQjxw/hb/hnu96xqjHscX+bUAWnWHkb60RnrWmmcsg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-avatar": "1.7.0", - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-card": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-caret": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-caret/-/uui-caret-1.7.0.tgz", - "integrity": "sha512-sVWUQGaLCAwhFH5mmE83+cwDjTyZamRWHgmVakTac2L9qYkwhTwzRgIol1t4i0DQMDFd4oLZ1zq+ysWvAOCmmQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-checkbox": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-checkbox/-/uui-checkbox-1.7.0.tgz", - "integrity": "sha512-7bY8FgSEscGtMYf0EtvmU4XuchV8bdjs+gwBKCVYogAELDdKbCTxWI6/HRqR6wDUOltpP1okFYN6rISugkUKtw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-boolean-input": "1.7.0", - "@umbraco-ui/uui-icon-registry-essential": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-color-area": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-area/-/uui-color-area-1.7.0.tgz", - "integrity": "sha512-7FashEB3hoh9p833gEhseq1t2mICVzb5zRe+FJ+vKFnTI2uuIRLjwD0pqSwmVAxoFCPgb81B6V5yH/pSrrzZEQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-picker": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-picker/-/uui-color-picker-1.7.0.tgz", - "integrity": "sha512-9JYlgg6i/gxwTIYsCJ8QnSjhZzZjJBiu2HZwDJ/2rm8uy/jNmbCf5aK+WHR7RbwMGNrF4/X/58t5woBzwSMUIA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-popover-container": "1.7.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-slider": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-slider/-/uui-color-slider-1.7.0.tgz", - "integrity": "sha512-DVyYeZsBG35430Cay6Dv8oO7dvi+aow6fVAJDHA4+CXdOSet4RTLO3oc1i51JwDmBiBhtLKGfo/wflrFxyfr0w==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-color-swatch": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatch/-/uui-color-swatch-1.7.0.tgz", - "integrity": "sha512-hp4Oicv7eLMvSn6jUerjDkYY6R/8JCRxbXabfbfZOZ/YwocSLN6DBc0nxlb/W8IETy26VCEFXH+tYKvZbsAB2Q==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-icon-registry-essential": "1.7.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-swatches": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatches/-/uui-color-swatches-1.7.0.tgz", - "integrity": "sha512-XIPP4BhaRB6nL3HAt2KRsEeslq/I2hMl8eQzgbz8y9V6yf7uq8q0OCMqQy2XB6bQ48N+sOqXfjKLPIT4yTIH7A==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-color-swatch": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-combobox": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox/-/uui-combobox-1.7.1.tgz", - "integrity": "sha512-4nbsRyqJO+rifoug+1PlWA8oI1L6f3aj7P/p9UT4pgcT8mpJ5Fv70XaFXMPEaCWh8HgZLsvMKDClXNzHXlvcLA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-button": "1.7.1", - "@umbraco-ui/uui-combobox-list": "1.7.0", - "@umbraco-ui/uui-icon": "1.7.0", - "@umbraco-ui/uui-popover-container": "1.7.0", - "@umbraco-ui/uui-scroll-container": "1.7.0", - "@umbraco-ui/uui-symbol-expand": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-combobox-list": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox-list/-/uui-combobox-list-1.7.0.tgz", - "integrity": "sha512-vRMz1eDqogVqsuRlzzwq+F2SoXxUoquQ9DqBJPif1LO1LgRUZ3G/j1XyOR+CaMRiPEbu0olyNBHOt15dFbgqhA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, "node_modules/@umbraco-ui/uui-css": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.7.0.tgz", - "integrity": "sha512-//nk4+w55eB+EI3hP3O+2RWKg+gXuwKqfcIjEZiP6Nn2epA2XQUV7K5NmcUwKStPyPh9NCz2+EtSvNqJZaaKhA==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.7.2.tgz", + "integrity": "sha512-KNxz0T9D1/9GAVaE6/ms76LkzWLPyfuhmqF/dCQotnZAQxwDJ7m0XLEf5/+qULRrNTqNQtluuFX/qIlmqD1xLQ==", "dev": true, "peerDependencies": { "lit": ">=2.8.0" } }, - "node_modules/@umbraco-ui/uui-dialog": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog/-/uui-dialog-1.7.0.tgz", - "integrity": "sha512-wlvpchoIrD+HQJw5fNrxQ4UP2iSfYss+uJwcxDnoQLvLHR8KyS9jdZVCUe1ozMe5KAJ7w1Tw+qEIiXumMFTUAA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-css": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-dialog-layout": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog-layout/-/uui-dialog-layout-1.7.0.tgz", - "integrity": "sha512-xuRXkAWlqAq2eO8VthT4JfOvVwpLeDwQwPOqwz4K50lR/6QHQAZdObG0g0DJuhlvehMMXPXrRneWZrAOWeIYGw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-file-dropzone": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-dropzone/-/uui-file-dropzone-1.7.0.tgz", - "integrity": "sha512-quMmD9iKg4EqV7JKs7k3pcAnxn/RGQjlXgIMrTAUbZbMclLAtTQrowij7ydX5rAdkPgtpQAWRmRuUTcufse64g==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-file-preview": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-preview/-/uui-file-preview-1.7.0.tgz", - "integrity": "sha512-QJg36PvN5LIHcl+fmcuhMFrkrTc5FDuj5L9DRStB/8V//HMhOKwjhOPcmc6xsxXm26R+jnS/7R67r/9PyjjhsQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-symbol-file": "1.7.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.7.0", - "@umbraco-ui/uui-symbol-folder": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-form": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form/-/uui-form-1.7.0.tgz", - "integrity": "sha512-gHNCYq/kwa7hXLHLKGBYrub8jTJHup7hf+mBf3g1LjipS+8M2a9tdpoO8yWzyEauzFsS4YJo45XqN6SRC1f2MQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-form-layout-item": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-layout-item/-/uui-form-layout-item-1.7.0.tgz", - "integrity": "sha512-gorUH9jCcCPdlDUy41xD6+4PQyZEL+9H3rnnKGg4xGQRw1+RnLCgmGa7mYiLfj1ycgi8l7MU50zCsQyNvPAPgg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-form-validation-message": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-form-validation-message": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-validation-message/-/uui-form-validation-message-1.7.0.tgz", - "integrity": "sha512-CU2ykzuIA3153EYKkRsqZ0SuGDxoy1zrdYVczWZ+sVxggyIWwazLMm5EZvdoiF8s3iP0m/v2LyyUh9GkBZ66LA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-icon": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon/-/uui-icon-1.7.0.tgz", - "integrity": "sha512-PtOSZkTxWskRrppdhxf17D+d54OylvtjE7muyLb2eJEYoP7KEaWdJ8Lfei5LtaUCRJlstFwQrCh/QbtWhe8Dfw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-icon-registry": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry/-/uui-icon-registry-1.7.0.tgz", - "integrity": "sha512-hG3VlF5VLt2XaNYHRUdqs2m5F4s9FUS4WxMc/TRu9Dzhqtie3A7UZ23qtONAcTCSPUxEXW5t809JUyxFi8kpBg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-icon": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-icon-registry-essential": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry-essential/-/uui-icon-registry-essential-1.7.0.tgz", - "integrity": "sha512-zgNKwT5L8Ez1R9WUO+vFRPbaUHHoSc6ohOfLA790WCA+F2krzbc7z3hNk6fHkFTR73K4rCaMu6gRbDX/PvuD8w==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-icon-registry": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-input": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input/-/uui-input-1.7.0.tgz", - "integrity": "sha512-c99s0hoggDTWFb3cq0uVcZcHCmstK82tVFJ4yPpaTMjJsilVCg9JnXE1B4tHvT25ZyAvN/pjJ/SYvLmKtU/MZA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-input-file": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-file/-/uui-input-file-1.7.1.tgz", - "integrity": "sha512-bzakMaaE1iSR7ioIzp2TGoBbwmBM+k712+0x+sK2dnQswRlLHL/Y95e7Byp/Aq6fNPayIwP6FaotB72JADDTig==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-action-bar": "1.7.0", - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-button": "1.7.1", - "@umbraco-ui/uui-file-dropzone": "1.7.0", - "@umbraco-ui/uui-icon": "1.7.0", - "@umbraco-ui/uui-icon-registry-essential": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-input-lock": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-lock/-/uui-input-lock-1.7.1.tgz", - "integrity": "sha512-kfHYiX9844/yE2yIwgk/e73BXiFYi5qn/aCJiy9T3lO6DEFaaHOJUccMyWsNIvSiPHYRX/11Mm0sP30jotjgGQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-button": "1.7.1", - "@umbraco-ui/uui-icon": "1.7.0", - "@umbraco-ui/uui-input": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-input-password": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-password/-/uui-input-password-1.7.0.tgz", - "integrity": "sha512-IU7/obNqFaHfuAyga8/wXC26+nqUEaovw67SeA83+2VUSyE7FeNTwW+AV7WFU7ZxeMYUvdJyxIpY43fClFg97A==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-icon-registry-essential": "1.7.0", - "@umbraco-ui/uui-input": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-keyboard-shortcut": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-keyboard-shortcut/-/uui-keyboard-shortcut-1.7.0.tgz", - "integrity": "sha512-PJmHNDCTiif89zkLUbBCdlnjY87TkqDfYQVjmhNwaO0DPxpQDh8gG2TvwD3Wp+aqdoVjR8FPIQH5pst+ulBa4g==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-label": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-label/-/uui-label-1.7.0.tgz", - "integrity": "sha512-uk1m3wux4dNb/0AqSGslODLo6yVT9aXKBYcHTsvW2P0NQI8IImiJVWw9TWmNrfuBPACJhEqo3pVvfe/PCfsWzQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-loader": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader/-/uui-loader-1.7.0.tgz", - "integrity": "sha512-RKKThaEF1jqG+iU/vwH91QfXxaRvO10hABEReUj6IJYiU0sVCHxmZJczXnJFZKbl5pyEycOznV//b66J5kUddw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-loader-bar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-bar/-/uui-loader-bar-1.7.0.tgz", - "integrity": "sha512-9lDRavgADrcQss5mbdmBrorzgSFNBr4srDA3B6PCya9pFpGdu/NgvQr/SrQzU0U2YSeW4jB88pyHwZaI6PCYug==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-loader-circle": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-circle/-/uui-loader-circle-1.7.0.tgz", - "integrity": "sha512-7/FqKCntviNUS8yzKhw4lYCWj598gYbzxBRvGJxVPinMOfAgMa8MAOGKpi7VDFHsqfHASfDCzHkqdywq0ku3nQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-menu-item": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-menu-item/-/uui-menu-item-1.7.0.tgz", - "integrity": "sha512-RTsrBmD1zjcP7XGPIGsxfBfOH+u4k3Jtw1qy/bxD1XLNH3ggOtfpQrpMzn/kxaer/wxYrUnXoDZDRjRuhHwvbg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-loader-bar": "1.7.0", - "@umbraco-ui/uui-symbol-expand": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-modal": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-modal/-/uui-modal-1.7.0.tgz", - "integrity": "sha512-/XTu5kbPAgkbMrm1MISz+hvvEh3d2guBl7bs5EhiLBsq4XqnaDQwh15joS4wub5R2lfaodvJg7Or2VvFV+v5ug==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-pagination": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-pagination/-/uui-pagination-1.7.1.tgz", - "integrity": "sha512-T6oomUkqf6xFc4ZMGX4YHmeBDBLwSfkTz/9sksqTpFpiK86XGJMQ0yOfPhlWNZ9TrC4OJZDurZ/jnY1l97OxcQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-button": "1.7.1", - "@umbraco-ui/uui-button-group": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-popover": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover/-/uui-popover-1.7.0.tgz", - "integrity": "sha512-aGG2AOXWfiRSo+0HAZkmZkWCXZTWyBB6mQ7+1XVcSHubsGLTimc6jcs+9y8c/OgMlFlm+YhDmp0bVSdmUKmYIg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-popover-container": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover-container/-/uui-popover-container-1.7.0.tgz", - "integrity": "sha512-az2Em1ZKaBLbPBKS3SePeCh6dk4NpdqsM+uRC5DFDLc95oAciKnC/gSjjZf1VtlL+hjb907R+nDQmszC9K7qfA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-progress-bar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-progress-bar/-/uui-progress-bar-1.7.0.tgz", - "integrity": "sha512-LjoK+DbO6BcNBJXr6ZKUHTfXPf4ZeChCVDEf1YfsiyLGxoKgt605YqJ8t8OWLInlO3m1rZmB7f0Uxc58nnPjxg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-radio": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-radio/-/uui-radio-1.7.0.tgz", - "integrity": "sha512-dNuBdHKNVJUaeemA87uCNTBIeN6S+dLdgxGI2ayQNzA/71EDSdBlIMrdm8FTJ0H8Un/itvgaujhu7EHbckai3w==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-range-slider": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-range-slider/-/uui-range-slider-1.7.0.tgz", - "integrity": "sha512-3LV9H0HciGSMEpX1I7zSzmPssGvF+C907bl8hWnlmaVVKGirBjrKPHmeZQW/zpqRCtvDWipFYKOcgbKQzCA2Vw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref/-/uui-ref-1.7.0.tgz", - "integrity": "sha512-/llhIEmVoJ4gb3LmOH1cfJ5zOSJry7TfJTxzruUpCxi+O68zMscgRZr+eh9DdF+Lz7zMbRxlubbVOZ58HhEPmQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-list": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-list/-/uui-ref-list-1.7.0.tgz", - "integrity": "sha512-BEb878VsSmRJuq1FCtoS9ryBvUErUfK8bQy93ErwgmesdUcuYpBJK1PfSe4x7SiLjD1vDlH9GHaWLyFiSJKfIQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node/-/uui-ref-node-1.7.0.tgz", - "integrity": "sha512-yqTS6B3uA0e8g29+nqbUnyPncyRdeYGNR4mjA4gdL4iwPumBvC49tPoWds8Nq0lEyxJg9fLNMezokPOMs2fKvw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-icon": "1.7.0", - "@umbraco-ui/uui-ref": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-data-type": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-data-type/-/uui-ref-node-data-type-1.7.0.tgz", - "integrity": "sha512-TmnpFGaG1QqUqvwlmXlXzpPZ+tCigqCxv4VVOYA9XwfUeqwoWmziQJ1jJyqdxSrHxRYkgg9Or8ZqObpKZ0HrCg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-ref-node": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-document-type": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-document-type/-/uui-ref-node-document-type-1.7.0.tgz", - "integrity": "sha512-KiZWbggePxAmHWr31yJzWOrA4DLGMbw8goMSC49zinBX4X2FOqgOTG8dl4dCXMxN114wxcTDRFvdTcWpIOHeEQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-ref-node": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-form": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-form/-/uui-ref-node-form-1.7.0.tgz", - "integrity": "sha512-FUZA7jjWOOA8HILRhD30mKO6NX0Hv+wL61gfIbWt95iGsmPwknth550Dm+i1Cc/3L63QmZD0qBQRTKRl7zfynA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-ref-node": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-member": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-member/-/uui-ref-node-member-1.7.0.tgz", - "integrity": "sha512-PFXZzlPmJaNLrvCO3p9n5ViIBXfr7nJtm+3WphuUM6KiJMMa0Fv7au1CINv6abu+TzjBh6VcmoNdt8Hu2MfS7g==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-ref-node": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-package": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-package/-/uui-ref-node-package-1.7.0.tgz", - "integrity": "sha512-OVvo+YDs0a3jqtm09XwaZdRNFwmDnSIBCTAllG+fLRbYQfwF0pCp96WOmuwQfGjlXhPrIjbhJ6YJH7R8QRUzbw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-ref-node": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-user": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-user/-/uui-ref-node-user-1.7.0.tgz", - "integrity": "sha512-Z2qF53n9O7Ft/xgexY/lzUd8xeFusCLSnz7hkqfWgTIbSvdI9FXtMiqCWqD1nWmijIPYBKaqujBfibGtx1DcSg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-ref-node": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-scroll-container": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-scroll-container/-/uui-scroll-container-1.7.0.tgz", - "integrity": "sha512-W4rETai/KAyXkDRUn6h14S6PLigswzkE45ufHAc7K2QZGUgXikpntbE8UpsEfq1QdMQRTHDmjorGn2qT+C6ULA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-select": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-select/-/uui-select-1.7.0.tgz", - "integrity": "sha512-pkPWTciiL9hPXpDO26wkkZFLze+jgL/xZkGgtrULrMRS5mJ6gan+8bB14iGtPt/ekFdgDmt6YcKozjp8g15xGg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-slider": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-slider/-/uui-slider-1.7.0.tgz", - "integrity": "sha512-kP93yvwsvRUEyS4+PhkhwXpkWZUm77sKerB6Dte0Z579WMQclSAivy6va9kkj5zKxZYPcRbJ3H498FvfhxhMkw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-expand": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-expand/-/uui-symbol-expand-1.7.0.tgz", - "integrity": "sha512-Z9bv8uYU2+tQ3UoJM2Ymdpmey73bLBNuaIKJG1AOXi1c2CB1UHaIn0C0Cvj4eHLoIEVp29UZOpQM7ri3/zb7lg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-file": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file/-/uui-symbol-file-1.7.0.tgz", - "integrity": "sha512-m/vx7WnCbYw0cNqS7TM6JeS7S/AMEQlnVUOWa2w2GDIuKNy6Jb1bk0soW1B3Fi6Hc6Pq+pMeaKgVPIM5F7F4Cg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-file-dropzone": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-dropzone/-/uui-symbol-file-dropzone-1.7.0.tgz", - "integrity": "sha512-lyhROAhwbmgK24DerwTiP5iP8GeOiAcgbgkUfHhG8X6hWMx9nV3H1nJHil5jFAeXk9nbAzDw4UfUgQWeKePLTg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-file-thumbnail": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-thumbnail/-/uui-symbol-file-thumbnail-1.7.0.tgz", - "integrity": "sha512-ZyS82vIqgqpPTx1atPaN+bw+Wr5e2lY2G9dpjTVx15PZtlI2Hp9aouiWyDRuCai8cc9Cj7n+7wF/K8QC7J8uCw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-folder": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-folder/-/uui-symbol-folder-1.7.0.tgz", - "integrity": "sha512-0EgbdXHY/aKniF0GZV6q64BWBsHK/dmar2hRNa/CpXHOGr04caY2svs44adWo4AOdGbPy9ayIglEzwSBRV+vXA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-lock": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-lock/-/uui-symbol-lock-1.7.0.tgz", - "integrity": "sha512-w+f3jvnVhkETiT3NERIsHJrYDZJC5zfihtW/KRE7isJflF8vrnEyUylv5ZJEih2kj0qCphoCswfMNQjwZbmMFQ==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-more": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-more/-/uui-symbol-more-1.7.0.tgz", - "integrity": "sha512-mYG0BKW3F8quwsBRck3mhINDJrl+bmfTzQsQRBjjCtP/BuIlqb2JSZDn0KBK1Jj7gl2MJINgZSzsL89zjyRVHg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-sort": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-sort/-/uui-symbol-sort-1.7.0.tgz", - "integrity": "sha512-gE8KNPAKZbUkAf+ZYLWe0zK4TC914sNfoCZJY4v8aEJ8xkZ/mYXJ7FxVvE+gvYuZ033VqrO5Ko5AwWEXfw1iIA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-table": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-table/-/uui-table-1.7.0.tgz", - "integrity": "sha512-9t9vdWOQ0NKg6aHTWqoIfAEK0M/DDrGkcn96FGvxxbPd+qkta4XDYCMEfOfMjGnGz+lukWoACectczEHXuI6gA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-tabs": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tabs/-/uui-tabs-1.7.1.tgz", - "integrity": "sha512-HYX5abtHKEse8UC17bUJM0LV4Kt0MNVIV4I2PtOOMIbLFx8kIVL/bdi/IO5T8VzYtecLQI8dgELc0Y2wgRSvNA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-button": "1.7.1", - "@umbraco-ui/uui-popover-container": "1.7.0", - "@umbraco-ui/uui-symbol-more": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-tag": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tag/-/uui-tag-1.7.0.tgz", - "integrity": "sha512-twrXe2U733r92ubBGXxWV9F5QP7SCJhKwYZbC2jbFOGoHpcxCtELvy36vEvgoWUF2BorPLQZSci7RHO0Hbnasw==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-textarea": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-textarea/-/uui-textarea-1.7.0.tgz", - "integrity": "sha512-rMqd4h5U/hW/wRacbr6D7/MoK8gqgiLh341Q+CFTEAnWdXNvRakHe4DNspguDIYCPUTjjRshTJowj9ZdbxHO7w==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-toast-notification": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification/-/uui-toast-notification-1.7.1.tgz", - "integrity": "sha512-SDAW0oYyboC5GvKg6GP0ZbNkr2C1qkVxSsO3gSAxI9+aUUbYuc3SijudyGCuESzdNshTbmya5OpUC3mnd5zdGA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-button": "1.7.1", - "@umbraco-ui/uui-css": "1.7.0", - "@umbraco-ui/uui-icon": "1.7.0", - "@umbraco-ui/uui-icon-registry-essential": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-toast-notification-container": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-container/-/uui-toast-notification-container-1.7.1.tgz", - "integrity": "sha512-m/B0XqBjAfEe30y2gHKJNbPxijF17zTU0VXb0sxTVa+1pb+eOtIMXVB6+DaYsr0TcsqPnq09kQruVEmvO8uWkg==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-toast-notification": "1.7.1" - } - }, - "node_modules/@umbraco-ui/uui-toast-notification-layout": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-layout/-/uui-toast-notification-layout-1.7.0.tgz", - "integrity": "sha512-5edQz3E84q3dKCvqFhZoMYY8258m9rPXak6gnqtZyGhAzwx8qZ8r9TDTcXftBnW+EB7Th9DheCUZLrphs35ZlA==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-css": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-toggle": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toggle/-/uui-toggle-1.7.0.tgz", - "integrity": "sha512-1Rz7CyBy38IF926maF1fyNjLG/my/4oWQRl0/22h/Xr6SYj/wWNE/1u4rg2bW1HGSu9mNtiel4wd7tDJ4g30Ew==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0", - "@umbraco-ui/uui-boolean-input": "1.7.0" - } - }, - "node_modules/@umbraco-ui/uui-visually-hidden": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-visually-hidden/-/uui-visually-hidden-1.7.0.tgz", - "integrity": "sha512-yPa1Z4S+ItjS+i9xgIobZ5QxfUyLRLguzqX8VARgCCxyoh5yXkoABhI9Fb0siSwc9TOtKuRaB+qQoV5rLnpu/g==", - "dev": true, - "dependencies": { - "@umbraco-ui/uui-base": "1.7.0" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1733,12 +852,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "dev": true - }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -1747,15 +860,32 @@ "node": ">= 0.6" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dev": true, "hasInstallScript": true, "bin": { @@ -1765,29 +895,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "node_modules/escalade": { @@ -1842,6 +972,12 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, "node_modules/graphql": { "version": "16.8.1", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", @@ -1880,6 +1016,8 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.2.tgz", "integrity": "sha512-VZx5iAyMtX7CV4K8iTLdCkMaYZ7ipjJZ0JcSdJ0zIdGxxyurjIn7yuuSxNBD7QmjvcNJwr0JS4cAdAtsy7gZ6w==", + "dev": true, + "peer": true, "dependencies": { "@lit/reactive-element": "^2.0.4", "lit-element": "^4.0.4", @@ -1890,6 +1028,8 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.4.tgz", "integrity": "sha512-98CvgulX6eCPs6TyAIQoJZBCQPo80rgXR+dVBs61cstJXqtI+USQZAbA4gFHh6L/mxBx9MrgPLHLsUgDUHAcCQ==", + "dev": true, + "peer": true, "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0", "@lit/reactive-element": "^2.0.4", @@ -1900,10 +1040,18 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.2.tgz", "integrity": "sha512-3OBZSUrPnAHoKJ9AMjRL/m01YJxQMf+TMHanNtTHG68ubjnZxK0RFl102DPzsw4mWnHibfZIBJm3LWCZ/LmMvg==", + "dev": true, + "peer": true, "dependencies": { "@types/trusted-types": "^2.0.2" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/msw": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/msw/-/msw-2.2.0.tgz", @@ -1990,9 +1138,9 @@ "dev": true }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.37", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.37.tgz", + "integrity": "sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==", "dev": true, "funding": [ { @@ -2011,7 +1159,7 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2026,9 +1174,9 @@ } }, "node_modules/rollup": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.10.0.tgz", - "integrity": "sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -2041,19 +1189,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.10.0", - "@rollup/rollup-android-arm64": "4.10.0", - "@rollup/rollup-darwin-arm64": "4.10.0", - "@rollup/rollup-darwin-x64": "4.10.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.10.0", - "@rollup/rollup-linux-arm64-gnu": "4.10.0", - "@rollup/rollup-linux-arm64-musl": "4.10.0", - "@rollup/rollup-linux-riscv64-gnu": "4.10.0", - "@rollup/rollup-linux-x64-gnu": "4.10.0", - "@rollup/rollup-linux-x64-musl": "4.10.0", - "@rollup/rollup-win32-arm64-msvc": "4.10.0", - "@rollup/rollup-win32-ia32-msvc": "4.10.0", - "@rollup/rollup-win32-x64-msvc": "4.10.0", + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", "fsevents": "~2.3.2" } }, @@ -2065,14 +1213,6 @@ "node": ">=0.12.0" } }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -2085,9 +1225,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2141,10 +1281,25 @@ "node": ">=8" } }, - "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "node_modules/tsconfck": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.0.3.tgz", + "integrity": "sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==", + "dev": true, + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, "node_modules/type-fest": { "version": "4.10.2", @@ -2176,14 +1331,14 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/vite": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", - "integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.2.tgz", + "integrity": "sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==", "dev": true, "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.20.1", + "postcss": "^8.4.36", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -2230,6 +1385,25 @@ } } }, + "node_modules/vite-tsconfig-paths": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz", + "integrity": "sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/src/Umbraco.Web.UI.Login/package.json b/src/Umbraco.Web.UI.Login/package.json index d461e761f6..93e559953f 100644 --- a/src/Umbraco.Web.UI.Login/package.json +++ b/src/Umbraco.Web.UI.Login/package.json @@ -1,27 +1,25 @@ { "name": "login", "private": true, - "type": "module", + "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", "watch": "tsc && vite build --watch", "preview": "vite preview" }, - "engines": { - "node": ">=20.8", - "npm": ">=10.1" - }, + "engines": { + "node": ">=20.8", + "npm": ">=10.1" + }, "dependencies": { - "lit": "^3.1.2", - "msw": "^2.2.0", - "rxjs": "^7.8.1" + "msw": "^2.2.0" }, "devDependencies": { - "@umbraco-ui/uui": "1.7.1", - "@umbraco-ui/uui-css": "1.7.0", + "@umbraco-ui/uui-css": "1.7.2", "typescript": "^5.3.3", - "vite": "^5.1.1" + "vite": "^5.2.2", + "vite-tsconfig-paths": "^4.3.2" }, "msw": { "workerDirectory": "public" diff --git a/src/Umbraco.Web.UI.Login/public/login.jpg b/src/Umbraco.Web.UI.Login/public/login.jpg index 3adc277159..6b97f2c943 100644 Binary files a/src/Umbraco.Web.UI.Login/public/login.jpg and b/src/Umbraco.Web.UI.Login/public/login.jpg differ diff --git a/src/Umbraco.Web.UI.Login/public/user-defined.css b/src/Umbraco.Web.UI.Login/public/user-defined.css new file mode 100644 index 0000000000..4d53802f53 --- /dev/null +++ b/src/Umbraco.Web.UI.Login/public/user-defined.css @@ -0,0 +1 @@ +/* This file can be overridden by placing a file with the same name in the /wwwroot/umbraco/login folder of the website */ diff --git a/src/Umbraco.Web.UI.Login/src/auth.element.ts b/src/Umbraco.Web.UI.Login/src/auth.element.ts index a22e5e840e..4ba0b8fbea 100644 --- a/src/Umbraco.Web.UI.Login/src/auth.element.ts +++ b/src/Umbraco.Web.UI.Login/src/auth.element.ts @@ -1,16 +1,18 @@ -import { html, LitElement } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; -import { ifDefined } from 'lit/directives/if-defined.js'; -import { until } from 'lit/directives/until.js'; +import { html, customElement, property, ifDefined } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element"; +import { type InputType, type UUIFormLayoutItemElement, type UUILabelElement } from "@umbraco-cms/backoffice/external/uui"; +import { umbExtensionsRegistry } from "@umbraco-cms/backoffice/extension-registry"; -import { umbAuthContext } from './context/auth.context.js'; -import { umbLocalizationContext } from './external/localization/localization-context.js'; -import { UmbLocalizeElement } from './external/localization/localize.element.js'; -import type { UmbLoginInputElement } from './components/login-input.element.js'; -import type { InputType, UUIFormLayoutItemElement, UUILabelElement } from '@umbraco-ui/uui'; +import { UMB_AUTH_CONTEXT, UmbAuthContext } from "./contexts"; +import type { UmbLoginInputElement } from "./components"; +import { UmbSlimBackofficeController } from "./controllers"; +// We import the authStyles here so that we can inline it in the shadow DOM that is created outside of the UmbAuthElement. import authStyles from './auth-styles.css?inline'; +// Import the main bundle +import { extensions } from './umbraco-package.js'; + const createInput = (opts: { id: string; type: InputType; @@ -34,10 +36,11 @@ const createInput = (opts: { return input; }; -const createLabel = (opts: { forId: string; localizeAlias: string }) => { +const createLabel = (opts: { forId: string; localizeAlias: string; localizeFallback: string; }) => { const label = document.createElement('uui-label'); - const umbLocalize = document.createElement('umb-localize') as UmbLocalizeElement; + const umbLocalize = document.createElement('umb-localize'); umbLocalize.key = opts.localizeAlias; + umbLocalize.innerHTML = opts.localizeFallback; label.for = opts.forId; label.appendChild(umbLocalize); @@ -67,19 +70,14 @@ const createForm = (elements: HTMLElement[]) => { }; @customElement('umb-auth') -export default class UmbAuthElement extends LitElement { +export default class UmbAuthElement extends UmbLitElement { /** * Disables the local login form and only allows external login providers. * * @attr disable-local-login */ @property({type: Boolean, attribute: 'disable-local-login'}) - set disableLocalLogin(value: boolean) { - umbAuthContext.disableLocalLogin = value; - } - get disableLocalLogin() { - return umbAuthContext.disableLocalLogin; - } + disableLocalLogin = false; @property({attribute: 'background-image'}) backgroundImage?: string; @@ -101,10 +99,10 @@ export default class UmbAuthElement extends LitElement { @property({attribute: 'return-url'}) set returnPath(value: string) { - umbAuthContext.returnPath = value; + this.#authContext.returnPath = value; } get returnPath() { - return umbAuthContext.returnPath; + return this.#authContext.returnPath; } /** @@ -120,6 +118,8 @@ export default class UmbAuthElement extends LitElement { _usernameLabel?: UUILabelElement; _passwordLabel?: UUILabelElement; + #authContext = new UmbAuthContext(this, UMB_AUTH_CONTEXT); + constructor() { super(); @@ -129,14 +129,20 @@ export default class UmbAuthElement extends LitElement { } this.requestUpdate(); }); + + // Bind the (slim) Backoffice controller to this element so that we can use utilities from the Backoffice app. + new UmbSlimBackofficeController(this); + + // Register the main package for Umbraco.Auth + umbExtensionsRegistry.registerMany(extensions); } - connectedCallback() { - super.connectedCallback(); - this.classList.add('uui-text'); - this.classList.add('uui-font'); - - this.#initializeForm(); + firstUpdated() { + setTimeout(() => { + requestAnimationFrame(() => { + this.#initializeForm(); + }); + }, 100); } disconnectedCallback() { @@ -156,12 +162,12 @@ export default class UmbAuthElement extends LitElement { * @see Track this intent-to-ship for Chrome https://groups.google.com/a/chromium.org/g/blink-dev/c/RY9leYMu5hI?pli=1 * @private */ - async #initializeForm() { + #initializeForm() { const labelUsername = this.usernameIsEmail - ? await umbLocalizationContext.localize('general_email', undefined, 'Email') - : await umbLocalizationContext.localize('general_username', undefined, 'Username'); - const labelPassword = await umbLocalizationContext.localize('general_password', undefined, 'Password'); - const requiredMessage = await umbLocalizationContext.localize('general_required', undefined, 'Required'); + ? this.localize.term('auth_email') + : this.localize.term('auth_username'); + const labelPassword = this.localize.term('auth_password'); + const requiredMessage = this.localize.term('auth_required'); this._usernameInput = createInput({ id: 'username-input', @@ -183,9 +189,10 @@ export default class UmbAuthElement extends LitElement { }); this._usernameLabel = createLabel({ forId: 'username-input', - localizeAlias: this.usernameIsEmail ? 'general_email' : 'general_username', + localizeAlias: this.usernameIsEmail ? 'auth_email' : 'auth_username', + localizeFallback: this.usernameIsEmail ? 'Email' : 'Username', }); - this._passwordLabel = createLabel({forId: 'password-input', localizeAlias: 'general_password'}); + this._passwordLabel = createLabel({forId: 'password-input', localizeAlias: 'auth_password', localizeFallback: 'Password'}); this._usernameLayoutItem = createFormLayoutItem(this._usernameLabel, this._usernameInput); this._passwordLayoutItem = createFormLayoutItem(this._passwordLabel, this._passwordInput); @@ -207,6 +214,14 @@ export default class UmbAuthElement extends LitElement { } private _renderFlowAndStatus() { + if (this.disableLocalLogin) { + return html` + + Unfortunately, it is not possible to log in directly. It has been disabled by a login provider. + + `; + } + const searchParams = new URLSearchParams(window.location.search); let flow = this.flow || searchParams.get('flow')?.toLowerCase(); const status = searchParams.get('status'); @@ -214,34 +229,20 @@ export default class UmbAuthElement extends LitElement { if (status === 'resetCodeExpired') { return html` + message=${this.localize.term('auth_resetCodeExpired')}> `; } if (flow === 'invite-user' && status === 'false') { return html` + message=${this.localize.term('auth_userInviteExpiredMessage')}> `; } // validate if (flow) { - if (flow === 'mfa' && !umbAuthContext.isMfaEnabled) { + if (flow === 'mfa' && !this.#authContext.isMfaEnabled) { flow = undefined; } } @@ -267,7 +268,6 @@ export default class UmbAuthElement extends LitElement { ?username-is-email=${this.usernameIsEmail}> - `; } } diff --git a/src/Umbraco.Web.UI.Login/src/components/back-to-login-button.element.ts b/src/Umbraco.Web.UI.Login/src/components/back-to-login-button.element.ts index 701be018e8..9200baa52d 100644 --- a/src/Umbraco.Web.UI.Login/src/components/back-to-login-button.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/back-to-login-button.element.ts @@ -1,8 +1,8 @@ -import { CSSResultGroup, LitElement, css, html } from 'lit'; -import { customElement } from 'lit/decorators.js'; +import { CSSResultGroup, css, html, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @customElement('umb-back-to-login-button') -export default class UmbBackToLoginButtonElement extends LitElement { +export default class UmbBackToLoginButtonElement extends UmbLitElement { render() { return html` `; } @@ -39,7 +39,7 @@ export default class UmbBackToLoginButtonElement extends LitElement { display: inline-flex; line-height: 1; font-size: 14px; - font-family: var(--uui-font-family); + font-family: var(--uui-font-family),sans-serif; } button svg { width: 1rem; diff --git a/src/Umbraco.Web.UI.Login/src/components/external-login-provider.element.ts b/src/Umbraco.Web.UI.Login/src/components/external-login-provider.element.ts deleted file mode 100644 index b48943e019..0000000000 --- a/src/Umbraco.Web.UI.Login/src/components/external-login-provider.element.ts +++ /dev/null @@ -1,240 +0,0 @@ -import type { UUIInterfaceColor, UUIInterfaceLook } from '@umbraco-ui/uui'; -import { css, CSSResultGroup, html, LitElement, nothing } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; -import { until } from 'lit/directives/until.js'; - -import { loadCustomView, renderCustomView } from '../utils/load-custom-view.function.js'; -import { umbLocalizationContext } from '../external/localization/localization-context.js'; - -type UserViewState = 'loggingIn' | 'loggedIn' | 'loggedOut' | 'timedOut'; - -type ExternalLoginCustomViewElement = HTMLElement & { - displayName?: string; - providerName?: string; - externalLoginUrl?: string; - userViewState?: UserViewState; -}; - -/** - * This elements represents a single external login provider and should be slotted into the element. - * - * @element umb-external-login-provider - */ -@customElement('umb-external-login-provider') -export class UmbExternalLoginProviderElement extends LitElement { - /** - * Gets or sets the path to the module that should be loaded as the custom view. - * The module should export a default class that extends HTMLElement. - * - * Setting this property will cause the default view to be hidden and the custom view to be loaded. - * The icon, button look and button color will be ignored. - * - * @example App_Plugins/MyPackage/MyCustomLoginView.js - * @attr custom-view - */ - @property({attribute: 'custom-view'}) - customView?: string; - - /** - * Gets or sets the display name of the provider. - * - * @attr display-name - * @example Google - */ - @property({attribute: 'display-name'}) - displayName = ''; - - /** - * Gets or sets the name of the provider (otherwise known as authentication type). - * - * @attr provider-name - * @example Umbraco.Google - */ - @property({attribute: 'provider-name'}) - providerName = ''; - - /** - * Gets or sets the view state of the user. This indicates in which state the user is in the login process, - * which can be used to determine where the external-login-provider is being shown. - * - * @attr user-view-state - * @example loggingIn - * @default loggingIn - */ - @property({attribute: 'user-view-state'}) - userViewState: UserViewState = 'loggingIn'; - - /** - * Gets or sets the url to the external login provider. - * - * @attr external-login-url - * @example /umbraco/ExternalLogin - */ - @property({attribute: 'external-login-url'}) - set externalLoginUrl(value: string) { - const tempUrl = new URL(value, window.location.origin); - const searchParams = new URLSearchParams(tempUrl.search); - tempUrl.searchParams.append('redirectUrl', decodeURIComponent(searchParams.get('returnPath') ?? '')); - this.#externalLoginUrl = tempUrl.pathname + tempUrl.search; - } - - get externalLoginUrl() { - return this.#externalLoginUrl; - } - - /** - * Gets or sets the icon to display next to the provider name. - * This should be the name of an icon in the Umbraco Backoffice icon set. - * - * @attr icon - * @example icon-google-fill - * @default icon-lock - */ - @property({attribute: 'icon'}) - icon = 'icon-lock'; - - /** - * Gets or sets the look of the underlying uui-button. - * - * @attr button-look - * @example outline - * @default outline - * @see https://uui.umbraco.com/?path=/story/uui-button--looks-and-colors - */ - @property({attribute: 'button-look'}) - buttonLook: UUIInterfaceLook = 'outline'; - - /** - * Gets or sets the color of the underlying uui-button. - * - * @attr button-color - * @example danger - * @default default - * @see https://uui.umbraco.com/?path=/story/uui-button--looks-and-colors - */ - @property({attribute: 'button-color'}) - buttonColor: UUIInterfaceColor = 'default'; - - #externalLoginUrl = ''; - - constructor() { - super(); - - const searchParams = new URLSearchParams(window.location.search); - const isLogout = searchParams.get('logout') === 'true'; - - if (isLogout) { - this.userViewState = 'loggedOut'; - } - } - - protected render() { - return this.customView - ? until(this.renderCustomView(), html` - `) - : this.renderDefaultView(); - } - - protected renderDefaultView() { - return html` -
- `${str} ${this.displayName}`))} - .look=${this.buttonLook} - .color=${this.buttonColor}> - ${this.displayName - ? html` -
- - Sign in with - ${this.displayName} -
- ` - : nothing} - -
-
- `; - } - - protected async renderCustomView() { - try { - if (!this.customView) return; - - const customView = await loadCustomView(this.customView); - - if (typeof customView === 'object') { - customView.displayName = this.displayName; - customView.providerName = this.providerName; - customView.externalLoginUrl = this.externalLoginUrl; - customView.userViewState = this.userViewState; - } - - return renderCustomView(customView); - } catch (error: unknown) { - console.group('[External login] Failed to load custom view'); - console.log('Provider name', this.providerName); - console.log('Element reference', this); - console.log('Custom view', this.customView); - console.error('Failed to load custom view:', error); - console.groupEnd(); - } - } - - static styles: CSSResultGroup = [ - css` - #defaultView uui-button { - width: 100%; - --uui-button-font-weight: 400; - } - - #defaultView uui-button div { - /* TODO: Remove this when uui-button has setting for aligning content */ - position: absolute; - top: 50%; - left: 0; - margin: auto; - transform: translateY(-50%); - text-align: left; - padding-left: 15px; - } - - #defaultView uui-icon { - opacity: 0.85; - padding-right: 2px; - } - - #defaultView button { - font-size: var(--uui-button-font-size); - border: 1px solid var(--uui-color-border); - border-radius: var(--uui-button-border-radius); - width: 100%; - padding: 9px; - text-align: left; - background-color: var(--uui-color-surface); - cursor: pointer; - display: flex; - align-items: center; - gap: var(--uui-size-space-2); - box-sizing: border-box; - - line-height: 1.1; /* makes the text vertically centered */ - color: var(--uui-color-interactive); - } - - #defaultView button:hover { - color: var(--uui-color-interactive-emphasis); - border-color: var(--uui-color-border-standalone); - } - `, - ]; -} - -declare global { - interface HTMLElementTagNameMap { - 'umb-external-login-provider': UmbExternalLoginProviderElement; - } -} diff --git a/src/Umbraco.Web.UI.Login/src/components/index.ts b/src/Umbraco.Web.UI.Login/src/components/index.ts new file mode 100644 index 0000000000..4ec1233fd5 --- /dev/null +++ b/src/Umbraco.Web.UI.Login/src/components/index.ts @@ -0,0 +1,4 @@ +export * from './layouts/index.js'; +export * from './pages/index.js'; +export * from './back-to-login-button.element.js'; +export * from './login-input.element.js'; diff --git a/src/Umbraco.Web.UI.Login/src/components/layouts/auth-layout.element.ts b/src/Umbraco.Web.UI.Login/src/components/layouts/auth-layout.element.ts index 4d9f972743..acee711254 100644 --- a/src/Umbraco.Web.UI.Login/src/components/layouts/auth-layout.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/layouts/auth-layout.element.ts @@ -1,6 +1,5 @@ -import { css, CSSResultGroup, html, LitElement, nothing, PropertyValueMap } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; -import { when } from 'lit/directives/when.js'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, CSSResultGroup, html, nothing, PropertyValueMap, customElement, property, when } from '@umbraco-cms/backoffice/external/lit'; /** * The auth layout component. @@ -27,7 +26,7 @@ import { when } from 'lit/directives/when.js'; * @cssprop --umb-login-curves-display - The display of the curves (default: inline) */ @customElement('umb-auth-layout') -export class UmbAuthLayoutElement extends LitElement { +export class UmbAuthLayoutElement extends UmbLitElement { @property({ attribute: 'background-image' }) backgroundImage?: string; diff --git a/src/Umbraco.Web.UI.Login/src/components/layouts/confirmation-layout.element.ts b/src/Umbraco.Web.UI.Login/src/components/layouts/confirmation-layout.element.ts index 488a963698..76e4b1f14e 100644 --- a/src/Umbraco.Web.UI.Login/src/components/layouts/confirmation-layout.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/layouts/confirmation-layout.element.ts @@ -1,8 +1,8 @@ -import { CSSResultGroup, LitElement, css, html } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; +import { CSSResultGroup, css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element"; @customElement('umb-confirmation-layout') -export default class UmbConfirmationLayoutElement extends LitElement { +export default class UmbConfirmationLayoutElement extends UmbLitElement { @property({ type: String }) header = ''; diff --git a/src/Umbraco.Web.UI.Login/src/components/layouts/error-layout.element.ts b/src/Umbraco.Web.UI.Login/src/components/layouts/error-layout.element.ts index 4f1da31fc0..ee53432749 100644 --- a/src/Umbraco.Web.UI.Login/src/components/layouts/error-layout.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/layouts/error-layout.element.ts @@ -1,22 +1,25 @@ -import { CSSResultGroup, LitElement, css, html } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; +import { CSSResultGroup, css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element"; @customElement('umb-error-layout') -export default class UmbErrorLayoutElement extends LitElement { +export default class UmbErrorLayoutElement extends UmbLitElement { @property({ type: String }) header = ''; @property({ type: String }) message = ''; + @property({ type: Boolean, attribute: 'no-back-link' }) + noBackLink = false; + render() { return html` - + ${!this.noBackLink ? html``: ''} `; } diff --git a/src/Umbraco.Web.UI.Login/src/components/layouts/external-login-providers-layout.element.ts b/src/Umbraco.Web.UI.Login/src/components/layouts/external-login-providers-layout.element.ts deleted file mode 100644 index 356bd4a618..0000000000 --- a/src/Umbraco.Web.UI.Login/src/components/layouts/external-login-providers-layout.element.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { css, CSSResultGroup, html, LitElement, nothing } from 'lit'; -import { customElement, property, queryAssignedElements } from 'lit/decorators.js'; -import { until } from 'lit/directives/until.js'; -import { umbLocalizationContext } from '../../external/localization/localization-context.js'; - -@customElement('umb-external-login-providers-layout') -export class UmbExternalLoginProvidersLayoutElement extends LitElement { - @property({ type: Boolean, attribute: 'divider' }) - showDivider = true; - - @queryAssignedElements({ flatten: true }) - protected slottedElements?: HTMLElement[]; - - firstUpdated() { - !!this.slottedElements?.length ? this.toggleAttribute('empty', false) : this.toggleAttribute('empty', true); - } - - render() { - return html` - ${this.showDivider - ? html` - - ` - : nothing} -
- -
- `; - } - - static styles: CSSResultGroup = [ - css` - :host { - margin-top: 16px; - display: flex; - flex-direction: column; - } - - :host([empty]) { - display: none; - } - - slot { - display: flex; - flex-direction: column; - gap: var(--uui-size-space-4); - } - - #divider { - width: calc(100% - 18px); - margin: 0 auto; - margin-bottom: 16px; - text-align: center; - z-index: 0; - overflow: hidden; - } - - #divider span { - padding-inline: 10px; - position: relative; - color: var(--uui-color-border-emphasis); - } - - #divider span::before, - #divider span::after { - content: ''; - display: block; - width: 500px; /* Arbitrary value, just be bigger than 50% of the max width of the container */ - height: 1px; - background-color: var(--uui-color-border); - position: absolute; - top: calc(50% + 1px); - } - - #divider span::before { - right: 100%; - } - - #divider span::after { - left: 100%; - } - `, - ]; -} - -declare global { - interface HTMLElementTagNameMap { - 'umb-external-login-providers-layout': UmbExternalLoginProvidersLayoutElement; - } -} diff --git a/src/Umbraco.Web.UI.Login/src/components/layouts/index.ts b/src/Umbraco.Web.UI.Login/src/components/layouts/index.ts new file mode 100644 index 0000000000..9981588d1d --- /dev/null +++ b/src/Umbraco.Web.UI.Login/src/components/layouts/index.ts @@ -0,0 +1,4 @@ +export * from './auth-layout.element.js'; +export * from './confirmation-layout.element.js'; +export * from './error-layout.element.js'; +export * from './new-password-layout.element.js'; diff --git a/src/Umbraco.Web.UI.Login/src/components/layouts/new-password-layout.element.ts b/src/Umbraco.Web.UI.Login/src/components/layouts/new-password-layout.element.ts index 3a3a56f0b1..5497de0acd 100644 --- a/src/Umbraco.Web.UI.Login/src/components/layouts/new-password-layout.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/layouts/new-password-layout.element.ts @@ -1,13 +1,11 @@ -import type {UUIButtonState, UUIInputPasswordElement} from '@umbraco-ui/uui'; -import {CSSResultGroup, LitElement, css, html, nothing} from 'lit'; -import {customElement, property, query, state} from 'lit/decorators.js'; -import {until} from 'lit/directives/until.js'; +import type { UUIButtonState, UUIInputPasswordElement } from '@umbraco-cms/backoffice/external/uui'; +import { type CSSResultGroup, css, html, nothing, customElement, property, query, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element"; -import {umbAuthContext} from '../../context/auth.context.js'; -import {umbLocalizationContext} from '../../external/localization/localization-context.js'; +import { UMB_AUTH_CONTEXT } from '../../contexts'; @customElement('umb-new-password-layout') -export default class UmbNewPasswordLayoutElement extends LitElement { +export default class UmbNewPasswordLayoutElement extends UmbLitElement { @query('#password') passwordElement!: UUIInputPasswordElement; @@ -21,30 +19,45 @@ export default class UmbNewPasswordLayoutElement extends LitElement { error: string = ''; @property() - userId: any; + userId: string = ''; - @property() - userName?: string; + @property({ type: Boolean, attribute: 'is-invite' }) + isInvite = false; @state() - passwordConfig?: { - allowManuallyChangingPassword: boolean; - minNonAlphaNumericChars: number; - minPasswordLength: number; - }; + _passwordConfiguration?: typeof UMB_AUTH_CONTEXT.TYPE['passwordConfiguration']; - protected async firstUpdated(_changedProperties: any) { - super.firstUpdated(_changedProperties); + @state() + _passwordPattern = ''; - if (this.userId) { - const response = await umbAuthContext.getPasswordConfig(this.userId); - this.passwordConfig = response.data; - } + constructor() { + super(); + + this.consumeContext(UMB_AUTH_CONTEXT, (authContext) => { + this._passwordConfiguration = authContext.passwordConfiguration; + // Build a pattern + let pattern = ''; + if (this._passwordConfiguration?.requireDigit) { + pattern += '(?=.*\\d)'; + } + if (this._passwordConfiguration?.requireLowercase) { + pattern += '(?=.*[a-z])'; + } + if (this._passwordConfiguration?.requireUppercase) { + pattern += '(?=.*[A-Z])'; + } + if (this._passwordConfiguration?.requireNonLetterOrDigit) { + pattern += '(?=.*\\W)'; + } + pattern += `.{${this._passwordConfiguration?.minimumPasswordLength ?? 10},}`; + this._passwordPattern = pattern; + }); } - async #onSubmit(event: Event) { + #onSubmit(event: Event) { event.preventDefault(); - if (!this.passwordConfig) return; + if (!this._passwordConfiguration) return; + const form = event.target as HTMLFormElement; this.passwordElement.setCustomValidity(''); @@ -59,33 +72,52 @@ export default class UmbNewPasswordLayoutElement extends LitElement { let passwordIsInvalid = false; - if (this.passwordConfig.minPasswordLength > 0 && password.length < this.passwordConfig.minPasswordLength) { + if (this._passwordConfiguration.minimumPasswordLength > 0 && password.length < this._passwordConfiguration.minimumPasswordLength) { passwordIsInvalid = true; } - if (this.passwordConfig.minNonAlphaNumericChars > 0) { - const nonAlphaNumericChars = password.replace(/[a-zA-Z0-9]/g, '').length; //TODO: How should we check for non-alphanumeric chars? - if (nonAlphaNumericChars < this.passwordConfig?.minNonAlphaNumericChars) { + if (this._passwordConfiguration.requireNonLetterOrDigit) { + const hasNonLetterOrDigit = /\W/.test(password); + if (!hasNonLetterOrDigit) { + passwordIsInvalid = true; + } + } + + if (this._passwordConfiguration.requireDigit) { + const hasDigit = /\d/.test(password); + if (!hasDigit) { + passwordIsInvalid = true; + } + } + + if (this._passwordConfiguration.requireLowercase) { + const hasLowercase = /[a-z]/.test(password); + if (!hasLowercase) { + passwordIsInvalid = true; + } + } + + if (this._passwordConfiguration.requireUppercase) { + const hasUppercase = /[A-Z]/.test(password); + if (!hasUppercase) { passwordIsInvalid = true; } } if (passwordIsInvalid) { - const passwordValidityText = await umbLocalizationContext.localize( - 'errorHandling_errorInPasswordFormat', - [this.passwordConfig.minPasswordLength, this.passwordConfig.minNonAlphaNumericChars], - "The password doesn't meet the minimum requirements!" - ); + const passwordValidityText = this.localize.term( + 'auth_errorInPasswordFormat', + this._passwordConfiguration.minimumPasswordLength, + this._passwordConfiguration.requireNonLetterOrDigit ? 1 : 0 + ) ?? 'The password does not meet the minimum requirements!'; this.passwordElement.setCustomValidity(passwordValidityText); return; } if (password !== passwordConfirm) { - const passwordValidityText = await umbLocalizationContext.localize( - 'user_passwordMismatch', - undefined, - "The confirmed password doesn't match the new password!" - ); + const passwordValidityText = this.localize.term( + 'auth_passwordMismatch' + ) ?? "The confirmed password doesn't match the new password!"; this.confirmPasswordElement.setCustomValidity(passwordValidityText); return; } @@ -94,11 +126,11 @@ export default class UmbNewPasswordLayoutElement extends LitElement { } renderHeader() { - if (this.userName) { + if (this.isInvite) { return html` -

Hi, ${this.userName}

+

Hi!

- + Welcome to Umbraco! Just need to get your password setup and then you're good to go @@ -106,10 +138,10 @@ export default class UmbNewPasswordLayoutElement extends LitElement { } else { return html`

- New password + New password

- Please provide a new password. + Please provide a new password. `; } @@ -122,43 +154,44 @@ export default class UmbNewPasswordLayoutElement extends LitElement { - New password + New password + required-message=${this.localize.term('auth_passwordIsBlank')}> + - Confirm new password + Confirm new password + required-message=${this.localize.term('auth_required')}> ${this.#renderErrorMessage()} diff --git a/src/Umbraco.Web.UI.Login/src/components/login-input.element.ts b/src/Umbraco.Web.UI.Login/src/components/login-input.element.ts index d3e6262172..fcd1953525 100644 --- a/src/Umbraco.Web.UI.Login/src/components/login-input.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/login-input.element.ts @@ -1,5 +1,5 @@ -import { UUIInputElement } from '@umbraco-ui/uui'; -import { customElement } from 'lit/decorators.js'; +import { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; +import { customElement } from '@umbraco-cms/backoffice/external/lit'; /** * This is a custom element based on UUIInputElement that is used in the login page. diff --git a/src/Umbraco.Web.UI.Login/src/components/pages/index.ts b/src/Umbraco.Web.UI.Login/src/components/pages/index.ts new file mode 100644 index 0000000000..dadd3e7ef8 --- /dev/null +++ b/src/Umbraco.Web.UI.Login/src/components/pages/index.ts @@ -0,0 +1,5 @@ +export * from './invite.page.element.js'; +export * from './login.page.element.js'; +export * from './mfa.page.element.js'; +export * from './new-password.page.element.js'; +export * from './reset-password.page.element.js'; diff --git a/src/Umbraco.Web.UI.Login/src/components/pages/invite.page.element.ts b/src/Umbraco.Web.UI.Login/src/components/pages/invite.page.element.ts index 9c5a8b215a..b393842eb5 100644 --- a/src/Umbraco.Web.UI.Login/src/components/pages/invite.page.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/pages/invite.page.element.ts @@ -1,13 +1,13 @@ -import type { UUIButtonState } from '@umbraco-ui/uui'; -import { LitElement, html } from 'lit'; -import { customElement, state } from 'lit/decorators.js'; -import { until } from 'lit/directives/until.js'; - -import { umbAuthContext } from '../../context/auth.context.js'; -import { umbLocalizationContext } from '../../external/localization/localization-context.js'; +import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; +import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UMB_AUTH_CONTEXT } from "../../contexts"; @customElement('umb-invite-page') -export default class UmbInvitePageElement extends LitElement { +export default class UmbInvitePageElement extends UmbLitElement { + #token = ''; + #userId = ''; + @state() state: UUIButtonState = undefined; @@ -15,20 +15,51 @@ export default class UmbInvitePageElement extends LitElement { error = ''; @state() - invitedUser?: any; + loading = true; - protected async firstUpdated(_changedProperties: any) { - super.firstUpdated(_changedProperties); + #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; - const response = await umbAuthContext.getInvitedUser(); + constructor() { + super(); - if (!response.user?.id) { - // The login page should already have redirected the user to an error page. They should never get here. - this.error = 'No invited user found'; + this.consumeContext(UMB_AUTH_CONTEXT, (authContext) => { + this.#authContext = authContext; + this.#init(); + }); + } + + async #init() { + const urlParams = new URLSearchParams(window.location.search); + const token = urlParams.get('inviteCode'); + const userId = urlParams.get('userId'); + + if (!token || !userId) { + this.error = 'The invite has expired or is invalid'; + this.loading = false; return; } - this.invitedUser = response.user; + if (!this.#authContext) return; + + this.#token = token; + this.#userId = userId; + + const response = await this.#authContext.validateInviteCode(this.#token, this.#userId); + + if (response.error) { + this.error = response.error; + this.loading = false; + return; + } + + if (!response.passwordConfiguration) { + this.error = 'There is no password configuration for the invite code. Please contact the administrator.'; + this.loading = false; + return; + } + + this.#authContext.passwordConfiguration = response.passwordConfiguration; + this.loading = false; } async #onSubmit(event: CustomEvent) { @@ -37,8 +68,10 @@ export default class UmbInvitePageElement extends LitElement { if (!password) return; + if (!this.#authContext) return; + this.state = 'waiting'; - const response = await umbAuthContext.newInvitedUserPassword(password); + const response = await this.#authContext.newInvitedUserPassword(password, this.#token, this.#userId); if (response.error) { this.error = response.error; @@ -47,30 +80,25 @@ export default class UmbInvitePageElement extends LitElement { } this.state = 'success'; - window.location.href = umbAuthContext.returnPath; + window.location.href = this.#authContext.returnPath; } render() { - return this.invitedUser - ? html` - ` - : this.error + return this.loading ? html`` : ( + this.error ? html` ` - : html` - - `; + header=${this.localize.term('auth_error')} + message=${this.error ?? this.localize.term('auth_defaultError')}> + ` + : html` + ` + ); } } diff --git a/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts b/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts index 196db95d26..7dc14a00b4 100644 --- a/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts @@ -1,14 +1,11 @@ -import type {UUIButtonState} from '@umbraco-ui/uui'; -import {css, CSSResultGroup, html, LitElement, nothing} from 'lit'; -import {customElement, property, queryAssignedElements, state} from 'lit/decorators.js'; -import {when} from 'lit/directives/when.js'; -import {until} from 'lit/directives/until.js'; +import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, type CSSResultGroup, html, nothing, when, customElement, property, queryAssignedElements, state } from '@umbraco-cms/backoffice/external/lit'; -import {umbAuthContext} from '../../context/auth.context.js'; -import {umbLocalizationContext} from '../../external/localization/localization-context.js'; +import { UMB_AUTH_CONTEXT } from '../../contexts'; @customElement('umb-login-page') -export default class UmbLoginPageElement extends LitElement { +export default class UmbLoginPageElement extends UmbLitElement { @property({type: Boolean, attribute: 'username-is-email'}) usernameIsEmail = false; @@ -25,12 +22,21 @@ export default class UmbLoginPageElement extends LitElement { private _loginError = ''; @state() - private get disableLocalLogin() { - return umbAuthContext.disableLocalLogin; - } + supportPersistLogin = false; #formElement?: HTMLFormElement; + #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; + + constructor() { + super(); + + this.consumeContext(UMB_AUTH_CONTEXT, (authContext) => { + this.#authContext = authContext; + this.supportPersistLogin = authContext.supportsPersistLogin; + }); + } + async #onSlotChanged() { this.#formElement = this.slottedElements?.find((el) => el.id === 'umb-login-form'); @@ -42,6 +48,8 @@ export default class UmbLoginPageElement extends LitElement { #handleSubmit = async (e: SubmitEvent) => { e.preventDefault(); + if (!this.#authContext) return; + const form = e.target as HTMLFormElement; if (!form) return; @@ -55,7 +63,7 @@ export default class UmbLoginPageElement extends LitElement { this._loginState = 'waiting'; - const response = await umbAuthContext.login({ + const response = await this.#authContext.login({ username, password, persist, @@ -66,9 +74,12 @@ export default class UmbLoginPageElement extends LitElement { // Check for 402 status code indicating that MFA is required if (response.status === 402) { - umbAuthContext.isMfaEnabled = true; + this.#authContext.isMfaEnabled = true; if (response.twoFactorView) { - umbAuthContext.twoFactorView = response.twoFactorView; + this.#authContext.twoFactorView = response.twoFactorView; + } + if (response.twoFactorProviders) { + this.#authContext.mfaProviders = response.twoFactorProviders; } this.dispatchEvent(new CustomEvent('umb-login-flow', {composed: true, detail: {flow: 'mfa'}})); @@ -76,11 +87,11 @@ export default class UmbLoginPageElement extends LitElement { } if (response.error) { - this.dispatchEvent(new CustomEvent('umb-login-failed', {bubbles: true, composed: true, detail: response})); + this.dispatchEvent(new CustomEvent('umb-login-failed', {bubbles: true, composed: true})); return; } - const returnPath = umbAuthContext.returnPath; + const returnPath = this.#authContext.returnPath; if (returnPath) { location.href = returnPath; @@ -91,13 +102,13 @@ export default class UmbLoginPageElement extends LitElement { get #greetingLocalizationKey() { return [ - 'login_greeting0', - 'login_greeting1', - 'login_greeting2', - 'login_greeting3', - 'login_greeting4', - 'login_greeting5', - 'login_greeting6', + 'auth_greeting0', + 'auth_greeting1', + 'auth_greeting2', + 'auth_greeting3', + 'auth_greeting4', + 'auth_greeting5', + 'auth_greeting6', ][new Date().getDay()]; } @@ -109,49 +120,42 @@ export default class UmbLoginPageElement extends LitElement { return html` - ${this.disableLocalLogin - ? nothing - : html` - -
- ${when( - umbAuthContext.supportsPersistLogin, - () => html` - - - Remember me - - ` - )} - ${when( - this.allowPasswordReset, - () => - html` - ` - )} -
- + +
+ ${when( + this.supportPersistLogin, + () => html` + + + Remember me + + ` + )} + ${when( + this.allowPasswordReset, + () => + html` + ` + )} +
+ - ${this.#renderErrorMessage()} - `} - - - + ${this.#renderErrorMessage()} `; } @@ -210,7 +214,7 @@ export default class UmbLoginPageElement extends LitElement { display: inline-flex; line-height: 1; font-size: 14px; - font-family: var(--uui-font-family); + font-family: var(--uui-font-family),sans-serif; margin-left: auto; margin-bottom: var(--uui-size-space-3); } diff --git a/src/Umbraco.Web.UI.Login/src/components/pages/mfa.page.element.ts b/src/Umbraco.Web.UI.Login/src/components/pages/mfa.page.element.ts index f24d383ca7..e047f7868f 100644 --- a/src/Umbraco.Web.UI.Login/src/components/pages/mfa.page.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/pages/mfa.page.element.ts @@ -1,10 +1,9 @@ -import type {UUIButtonState, UUIInputElement} from '@umbraco-ui/uui'; -import {LitElement, css, html, nothing} from 'lit'; -import {customElement, state} from 'lit/decorators.js'; -import {until} from 'lit/directives/until.js'; -import {umbAuthContext} from '../../context/auth.context.js'; -import {umbLocalizationContext} from '../../external/localization/localization-context.js'; -import {loadCustomView, renderCustomView} from '../../utils/load-custom-view.function.js'; +import type {UUIButtonState, UUIInputElement} from '@umbraco-cms/backoffice/external/uui'; +import {css, html, nothing, customElement, state, until} from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element"; + +import { loadCustomView, renderCustomView } from '../../utils/load-custom-view.function.js'; +import { UMB_AUTH_CONTEXT } from "../../contexts"; type MfaCustomViewElement = HTMLElement & { providers?: string[]; @@ -12,56 +11,47 @@ type MfaCustomViewElement = HTMLElement & { }; @customElement('umb-mfa-page') -export default class UmbMfaPageElement extends LitElement { +export default class UmbMfaPageElement extends UmbLitElement { @state() protected providers: Array<{ name: string; value: string; selected: boolean }> = []; - @state() - private loading = true; - @state() private buttonState?: UUIButtonState; @state() private error: string | null = null; - connectedCallback() { - super.connectedCallback(); - this.#loadProviders(); + #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; + + constructor() { + super(); + this.consumeContext(UMB_AUTH_CONTEXT, authContext => { + this.#authContext = authContext; + this.#loadProviders(); + }); } - async #loadProviders() { - try { - const response = await umbAuthContext.getMfaProviders(); - this.providers = response.providers.map((provider) => ({name: provider, value: provider, selected: false})); + #loadProviders() { + this.providers = this.#authContext?.mfaProviders.map((provider) => ({name: provider, value: provider, selected: false})) ?? []; - if (this.providers.length) { - this.providers[0].selected = true; - } - - if (response.error) { - this.error = response.error; - } - } catch (e) { - if (e instanceof Error) { - this.error = e.message ?? 'Unknown error'; - } else { - this.error = 'Unknown error'; - } - this.providers = []; + if (this.providers.length) { + this.providers[0].selected = true; + } else { + this.error = 'Error: No providers available'; } - this.loading = false; } - private async handleSubmit(e: SubmitEvent) { + async #handleSubmit(e: SubmitEvent) { e.preventDefault(); + if (!this.#authContext) return; + this.error = null; const form = e.target as HTMLFormElement; if (!form) return; - const codeInput = form.elements.namedItem('2facode') as UUIInputElement; + const codeInput = form.elements.namedItem('mfacode') as UUIInputElement; if (codeInput) { codeInput.error = false; @@ -76,6 +66,12 @@ export default class UmbMfaPageElement extends LitElement { // If no provider given, use the first one (there probably is only one anyway) if (!provider) { + // If there are no providers, we can't continue + if (!this.providers.length) { + this.error = 'No providers available'; + return; + } + provider = this.providers[0].value; } @@ -89,7 +85,7 @@ export default class UmbMfaPageElement extends LitElement { this.buttonState = 'waiting'; try { - const response = await umbAuthContext.validateMfaCode(code, provider); + const response = await this.#authContext.validateMfaCode(code, provider); if (response.error) { if (codeInput) { codeInput.error = true; @@ -103,13 +99,13 @@ export default class UmbMfaPageElement extends LitElement { this.buttonState = 'success'; - const returnPath = umbAuthContext.returnPath; + const returnPath = this.#authContext.returnPath; if (returnPath) { location.href = returnPath; } this.dispatchEvent( - new CustomEvent('umb-login-success', {bubbles: true, composed: true, detail: response.data}) + new CustomEvent('umb-login-success', {bubbles: true, composed: true}) ); } catch (e) { if (e instanceof Error) { @@ -118,21 +114,21 @@ export default class UmbMfaPageElement extends LitElement { this.error = 'Unknown error'; } this.buttonState = 'failed'; - this.dispatchEvent(new CustomEvent('umb-login-failed', {bubbles: true, composed: true, detail: e})); + this.dispatchEvent(new CustomEvent('umb-login-failed', {bubbles: true, composed: true})); } } protected renderDefaultView() { return html` -
+