diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs index 1336805045..cca2e7277f 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs @@ -2,6 +2,7 @@ using Asp.Versioning; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -15,6 +16,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Web.Common.Authorization; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; using IdentitySignInResult = Microsoft.AspNetCore.Identity.SignInResult; @@ -49,9 +51,9 @@ public class BackOfficeController : SecurityControllerBase // 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. - // [AllowAnonymous] // This is handled implicitly by the NewDenyLocalLoginIfConfigured policy on the . Keep it here for now and check FIXME in . [HttpPost("login")] [MapToApiVersion("1.0")] + [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public async Task Login(LoginRequestModel model) { var validated = await _backOfficeUserManager.ValidateCredentialsAsync(model.Username, model.Password); @@ -87,6 +89,7 @@ public class BackOfficeController : SecurityControllerBase return Ok(); } + [AllowAnonymous] [HttpPost("verify-2fa")] [MapToApiVersion("1.0")] public async Task Verify2FACode(Verify2FACodeModel model) @@ -137,7 +140,7 @@ public class BackOfficeController : SecurityControllerBase public required string Password { get; init; } } - // [AllowAnonymous] // This is handled implicitly by the NewDenyLocalLoginIfConfigured policy on the . Keep it here for now and check FIXME in . + [AllowAnonymous] [HttpGet("authorize")] [MapToApiVersion("1.0")] public async Task Authorize() @@ -160,6 +163,7 @@ public class BackOfficeController : SecurityControllerBase : await AuthorizeExternal(request); } + [AllowAnonymous] [HttpGet("signout")] [MapToApiVersion("1.0")] public async Task Signout() diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs index 92823e5dc6..cd9a1faf05 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs @@ -1,15 +1,12 @@ using Asp.Versioning; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.Security; -using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers.Security; [ApiVersion("1.0")] -[Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] // FIXME: Add requiring password reset token policy when its implemented public class ConfigurationSecurityController : SecurityControllerBase { diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordController.cs index 87ee99ec1f..a068f5d33d 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordController.cs @@ -7,11 +7,13 @@ using Umbraco.Cms.Api.Management.ViewModels.Security; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.OperationStatus; +using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers.Security; [ApiVersion("1.0")] // FIXME: Add requiring password reset token policy when its implemented +[Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public class ResetPasswordController : SecurityControllerBase { private readonly IUserService _userService; @@ -21,7 +23,6 @@ public class ResetPasswordController : SecurityControllerBase [HttpPost("forgot-password")] [MapToApiVersion("1.0")] - [AllowAnonymous] // This is handled implicitly by the NewDenyLocalLoginIfConfigured policy on the . Keep it here for now and check FIXME in . [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [UserPasswordEnsureMinimumResponseTime] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordTokenController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordTokenController.cs index 57a733adc3..54d5e2dae6 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordTokenController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/ResetPasswordTokenController.cs @@ -1,4 +1,5 @@ using Asp.Versioning; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.Builders; @@ -8,10 +9,12 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.OperationStatus; +using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers.Security; [ApiVersion("1.0")] +[Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public class ResetPasswordTokenController : SecurityControllerBase { private readonly IUserService _userService; @@ -20,7 +23,6 @@ public class ResetPasswordTokenController : SecurityControllerBase [HttpPost("forgot-password/reset")] [MapToApiVersion("1.0")] - // [AllowAnonymous] // This is handled implicitly by the NewDenyLocalLoginIfConfigured policy on the . Keep it here for now and check FIXME in . [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ProblemDetailsBuilder), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetailsBuilder), StatusCodes.Status404NotFound)] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs index 5e447a424a..09b493c484 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs @@ -1,16 +1,13 @@ -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Routing; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Services.OperationStatus; -using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers.Security; [VersionedApiBackOfficeRoute("security")] [ApiExplorerSettings(GroupName = "Security")] -[Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public abstract class SecurityControllerBase : ManagementApiControllerBase { protected IActionResult UserOperationStatusResult(UserOperationStatus status, ErrorMessageResult? errorMessageResult = null) => diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs index 0c432134e8..b4791de9a9 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/VerifyResetPasswordTokenController.cs @@ -1,4 +1,5 @@ using Asp.Versioning; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.Builders; @@ -17,9 +18,9 @@ public class VerifyResetPasswordTokenController : SecurityControllerBase public VerifyResetPasswordTokenController(IUserService userService) => _userService = userService; + [AllowAnonymous] [HttpPost("forgot-password/verify")] [MapToApiVersion("1.0")] - // [AllowAnonymous] // This is handled implicitly by the NewDenyLocalLoginIfConfigured policy on the . Keep it here for now and check FIXME in . [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ProblemDetailsBuilder), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetailsBuilder), StatusCodes.Status404NotFound)] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs index e017b0c6a3..694d5d5735 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/CreateInitialPasswordUserController.cs @@ -19,7 +19,6 @@ public class CreateInitialPasswordUserController : UserControllerBase public CreateInitialPasswordUserController(IUserService userService) => _userService = userService; - // [AllowAnonymous] // This is handled implicitly by the NewDenyLocalLoginIfConfigured policy on the . Keep it here for now and check FIXME in . [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 0252712f4c..9e67cbc971 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/User/VerifyInviteUserController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/VerifyInviteUserController.cs @@ -6,19 +6,17 @@ using Umbraco.Cms.Api.Management.ViewModels.User; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.OperationStatus; -using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers.User; [ApiVersion("1.0")] -[Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public class VerifyInviteUserController : UserControllerBase { private readonly IUserService _userService; public VerifyInviteUserController(IUserService userService) => _userService = userService; - // [AllowAnonymous] // This is handled implicitly by the NewDenyLocalLoginIfConfigured policy. Keep it here for now and check FIXME in . + [AllowAnonymous] [HttpPost("invite/verify")] [MapToApiVersion("1.0")] [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/src/Umbraco.Cms.Api.Management/Security/Authorization/DenyLocalLogin/DenyLocalLoginHandler.cs b/src/Umbraco.Cms.Api.Management/Security/Authorization/DenyLocalLogin/DenyLocalLoginHandler.cs index e4969d9960..e57eb6c742 100644 --- a/src/Umbraco.Cms.Api.Management/Security/Authorization/DenyLocalLogin/DenyLocalLoginHandler.cs +++ b/src/Umbraco.Cms.Api.Management/Security/Authorization/DenyLocalLogin/DenyLocalLoginHandler.cs @@ -9,32 +9,25 @@ namespace Umbraco.Cms.Api.Management.Security.Authorization.DenyLocalLogin; /// public class DenyLocalLoginHandler : MustSatisfyRequirementAuthorizationHandler { - // private readonly IBackOfficeExternalLoginProviders _externalLogins; - // - // /// - // /// Initializes a new instance of the class. - // /// - // /// Provides access to instances. - // public DenyLocalLoginHandler(IBackOfficeExternalLoginProviders externalLogins) - // => _externalLogins = externalLogins; - // - // /// - // protected override Task IsAuthorized(AuthorizationHandlerContext context, DenyLocalLoginRequirement requirement) - // => Task.FromResult(!_externalLogins.HasDenyLocalLogin()); + private readonly IBackOfficeExternalLoginProviders _externalLogins; + + /// + /// Initializes a new instance of the class. + /// + /// Provides access to instances. + public DenyLocalLoginHandler(IBackOfficeExternalLoginProviders externalLogins) + => _externalLogins = externalLogins; - // FIXME: Replace the value of isDenied with above implementation, once we have IBackOfficeExternalLoginProviders and related classes - // moved from Umbraco.Web.Backoffice - // FIXME: Remove [AllowAnonymous] from implementers of and in when we have the proper implementation protected override Task IsAuthorized(AuthorizationHandlerContext context, DenyLocalLoginRequirement requirement) { - // Some logic here - for now we will always authorize successfully - var isDenied = false; + var isDenied = _externalLogins.HasDenyLocalLogin(); if (isDenied is false) { - // Now allow anonymous (RequireAuthenticatedUser() adds this requirement) - necessary for some of the endpoints (BackOfficeController.Login()) - var denyAnonymousUserRequirements = context.PendingRequirements.OfType(); - foreach (var denyAnonymousUserRequirement in denyAnonymousUserRequirements) + // AuthorizationPolicies.BackOfficeAccess policy adds this requirement by policy.RequireAuthenticatedUser() + // Since we want to "allow anonymous" for some endpoints (i.e. BackOfficeController.Login()), it is necessary to succeed this requirement + IEnumerable denyAnonymousUserRequirements = context.PendingRequirements.OfType(); + foreach (DenyAnonymousAuthorizationRequirement denyAnonymousUserRequirement in denyAnonymousUserRequirements) { context.Succeed(denyAnonymousUserRequirement); } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 744e02f001..93ab2f63cb 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -382,12 +382,11 @@ namespace Umbraco.Cms.Core.DependencyInjection // Authorizers Services.AddSingleton(); Services.AddSingleton(); + Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); - Services.AddSingleton(); - } } }