Revert "Cleans up BackofficeSecurity, fixes up AuthenticationController for endpoints that aren't authorized (and simplifies)"

Signed-off-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Bjarke Berg
2020-11-27 13:16:22 +01:00
parent 348f69734b
commit 96ef2fd9b7
8 changed files with 78 additions and 32 deletions

View File

@@ -17,8 +17,6 @@ namespace Umbraco.Extensions
/// <returns></returns>
public static UmbracoBackOfficeIdentity GetUmbracoIdentity(this IPrincipal user)
{
// TODO: It would be nice to get rid of this and only rely on Claims, not a strongly typed identity instance
//If it's already a UmbracoBackOfficeIdentity
if (user.Identity is UmbracoBackOfficeIdentity backOfficeIdentity) return backOfficeIdentity;
@@ -55,10 +53,10 @@ namespace Umbraco.Extensions
/// <returns></returns>
public static double GetRemainingAuthSeconds(this IPrincipal user, DateTimeOffset now)
{
var claimsPrincipal = user as ClaimsPrincipal;
if (claimsPrincipal == null) return 0;
var umbIdentity = user.GetUmbracoIdentity();
if (umbIdentity == null) return 0;
var ticketExpires = claimsPrincipal.FindFirst(Constants.Security.TicketExpiresClaimType)?.Value;
var ticketExpires = umbIdentity.FindFirstValue(Constants.Security.TicketExpiresClaimType);
if (ticketExpires.IsNullOrWhiteSpace()) return 0;
var utcExpired = DateTimeOffset.Parse(ticketExpires, null, DateTimeStyles.RoundtripKind);

View File

@@ -32,6 +32,13 @@ namespace Umbraco.Core.Security
/// <returns></returns>
ValidateRequestAttempt ValidateCurrentUser(bool throwExceptions, bool requiresApproval = true);
/// <summary>
/// Authorizes the full request, checks for SSL and validates the current user
/// </summary>
/// <param name="throwExceptions">set to true if you want exceptions to be thrown if failed</param>
/// <returns></returns>
ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false);
/// <summary>
/// Checks if the specified user as access to the app
/// </summary>

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
@@ -207,26 +206,18 @@ namespace Umbraco.Web.BackOffice.Controllers
}
[HttpGet]
public async Task<double> GetRemainingTimeoutSeconds()
public double GetRemainingTimeoutSeconds()
{
// force authentication to occur since this is not an authorized endpoint
var result = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType);
if (!result.Succeeded)
{
return 0;
}
var backOfficeIdentity = HttpContext.User.GetUmbracoIdentity();
var remainingSeconds = HttpContext.User.GetRemainingAuthSeconds();
if (remainingSeconds <= 30)
if (remainingSeconds <= 30 && backOfficeIdentity != null)
{
var username = result.Principal.FindFirst(ClaimTypes.Name)?.Value;
//NOTE: We are using 30 seconds because that is what is coded into angular to force logout to give some headway in
// the timeout process.
_logger.LogInformation(
"User logged will be logged out due to timeout: {Username}, IP Address: {IPAddress}",
username ?? "unknown",
backOfficeIdentity.Name,
_ipResolver.GetCurrentRequestIpAddress());
}
@@ -238,11 +229,14 @@ namespace Umbraco.Web.BackOffice.Controllers
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<bool> IsAuthenticated()
public bool IsAuthenticated()
{
// force authentication to occur since this is not an authorized endpoint
var result = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType);
return result.Succeeded;
var attempt = _backofficeSecurityAccessor.BackOfficeSecurity.AuthorizeRequest();
if (attempt == ValidateRequestAttempt.Success)
{
return true;
}
return false;
}
/// <summary>
@@ -592,6 +586,7 @@ namespace Umbraco.Web.BackOffice.Controllers
}
/// <summary>
/// Return the <see cref="UserDetail"/> for the given <see cref="IUser"/>
/// </summary>

View File

@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Umbraco.Web.Common.Filters;
namespace Umbraco.Web.Common.ApplicationModels
{
/// <summary>
/// Ensures all requests with this convention are authenticated with the back office scheme
/// </summary>
public class AuthenticateAsBackOfficeSchemeConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
action.Filters.Add(new EnsureUmbracoBackOfficeAuthentication());
}
}
}

View File

@@ -17,7 +17,8 @@ namespace Umbraco.Web.Common.ApplicationModels
{
ActionModelConventions = new List<IActionModelConvention>()
{
new BackOfficeIdentityCultureConvention()
new BackOfficeIdentityCultureConvention(),
new AuthenticateAsBackOfficeSchemeConvention()
};
}

View File

@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Umbraco.Web.Common.ApplicationModels;
using Umbraco.Web.Common.Attributes;
namespace Umbraco.Web.Common.Filters
{
/// <summary>
/// Assigned as part of the umbraco back office application model <see cref="UmbracoApiBehaviorApplicationModelProvider"/>
/// to always ensure that back office authentication occurs for all controller/actions with
/// <see cref="IsBackOfficeAttribute"/> applied.
/// </summary>
public class EnsureUmbracoBackOfficeAuthentication : IAuthorizationFilter, IAuthorizeData
{
// Implements IAuthorizeData only to return the back office scheme
public string AuthenticationSchemes { get; set; } = Umbraco.Core.Constants.Security.BackOfficeAuthenticationType;
public string Policy { get; set; }
public string Roles { get; set; }
public void OnAuthorization(AuthorizationFilterContext context)
{
}
}
}

View File

@@ -6,15 +6,8 @@ namespace Umbraco.Web.Common.Filters
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
public class UmbracoBackOfficeAuthorizeAttribute : TypeFilterAttribute, IAuthorizeData
public class UmbracoBackOfficeAuthorizeAttribute : TypeFilterAttribute
{
// Implements IAuthorizeData to return the back office scheme so that all requests with this attributes
// get authenticated with this scheme.
// TODO: We'll have to refactor this as part of the authz policy changes.
public string AuthenticationSchemes { get; set; } = Umbraco.Core.Constants.Security.BackOfficeAuthenticationType;
public string Policy { get; set; }
public string Roles { get; set; }
/// <summary>
/// Default constructor
/// </summary>

View File

@@ -52,6 +52,18 @@ namespace Umbraco.Web.Common.Security
}
}
/// <inheritdoc />
public ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false)
{
// check for secure connection
if (_globalSettings.UseHttps && !_httpContextAccessor.GetRequiredHttpContext().Request.IsHttps)
{
if (throwExceptions) throw new SecurityException("This installation requires a secure connection (via SSL). Please update the URL to include https://");
return ValidateRequestAttempt.FailedNoSsl;
}
return ValidateCurrentUser(throwExceptions);
}
/// <inheritdoc />
public Attempt<int> GetUserId()
{
@@ -94,7 +106,6 @@ namespace Umbraco.Web.Common.Security
var user = CurrentUser;
// TODO: All of this is done as part of identity/backofficesigninmanager
// Check for console access
if (user == null || (requiresApproval && user.IsApproved == false) || (user.IsLockedOut && RequestIsInUmbracoApplication(_httpContextAccessor, _globalSettings, _hostingEnvironment)))
{