Revert "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:35:43 +01:00
parent a098b956a6
commit 26dc9219c4
8 changed files with 33 additions and 79 deletions

View File

@@ -17,6 +17,8 @@ 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;
@@ -53,10 +55,10 @@ namespace Umbraco.Extensions
/// <returns></returns>
public static double GetRemainingAuthSeconds(this IPrincipal user, DateTimeOffset now)
{
var umbIdentity = user.GetUmbracoIdentity();
if (umbIdentity == null) return 0;
var claimsPrincipal = user as ClaimsPrincipal;
if (claimsPrincipal == null) return 0;
var ticketExpires = umbIdentity.FindFirstValue(Constants.Security.TicketExpiresClaimType);
var ticketExpires = claimsPrincipal.FindFirst(Constants.Security.TicketExpiresClaimType)?.Value;
if (ticketExpires.IsNullOrWhiteSpace()) return 0;
var utcExpired = DateTimeOffset.Parse(ticketExpires, null, DateTimeStyles.RoundtripKind);

View File

@@ -32,13 +32,6 @@ 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,6 +2,7 @@
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;
@@ -206,18 +207,26 @@ namespace Umbraco.Web.BackOffice.Controllers
}
[HttpGet]
public double GetRemainingTimeoutSeconds()
public async Task<double> GetRemainingTimeoutSeconds()
{
var backOfficeIdentity = HttpContext.User.GetUmbracoIdentity();
var remainingSeconds = HttpContext.User.GetRemainingAuthSeconds();
if (remainingSeconds <= 30 && backOfficeIdentity != null)
// 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 remainingSeconds = HttpContext.User.GetRemainingAuthSeconds();
if (remainingSeconds <= 30)
{
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}",
backOfficeIdentity.Name,
username ?? "unknown",
_ipResolver.GetCurrentRequestIpAddress());
}
@@ -229,14 +238,11 @@ namespace Umbraco.Web.BackOffice.Controllers
/// </summary>
/// <returns></returns>
[HttpGet]
public bool IsAuthenticated()
public async Task<bool> IsAuthenticated()
{
var attempt = _backofficeSecurityAccessor.BackOfficeSecurity.AuthorizeRequest();
if (attempt == ValidateRequestAttempt.Success)
{
return true;
}
return false;
// force authentication to occur since this is not an authorized endpoint
var result = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType);
return result.Succeeded;
}
/// <summary>
@@ -586,7 +592,6 @@ namespace Umbraco.Web.BackOffice.Controllers
}
/// <summary>
/// Return the <see cref="UserDetail"/> for the given <see cref="IUser"/>
/// </summary>

View File

@@ -1,16 +0,0 @@
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,8 +17,7 @@ namespace Umbraco.Web.Common.ApplicationModels
{
ActionModelConventions = new List<IActionModelConvention>()
{
new BackOfficeIdentityCultureConvention(),
new AuthenticateAsBackOfficeSchemeConvention()
new BackOfficeIdentityCultureConvention()
};
}

View File

@@ -1,25 +0,0 @@
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,8 +6,15 @@ namespace Umbraco.Web.Common.Filters
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
public class UmbracoBackOfficeAuthorizeAttribute : TypeFilterAttribute
public class UmbracoBackOfficeAuthorizeAttribute : TypeFilterAttribute, IAuthorizeData
{
// 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,18 +52,6 @@ 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()
{
@@ -106,6 +94,7 @@ 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)))
{