Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

HttpsRequirementAttribute.cs

using System.Net;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Hosting;
using Nop.Core;
using Nop.Core.Http.Extensions;
using Nop.Data;

namespace Nop.Web.Framework.Mvc.Filters;

/// 
/// Represents a filter attribute that checks whether current connection is secured and properly redirect if necessary
/// 
public sealed class HttpsRequirementAttribute : TypeFilterAttribute
{
    #region Ctor

    /// 
    /// Create instance of the filter attribute
    /// 
    /// Whether to ignore the execution of filter actions
    public HttpsRequirementAttribute(bool ignore = false) : base(typeof(HttpsRequirementFilter))
    {
        IgnoreFilter = ignore;
        Arguments = [ignore];
    }

    #endregion

    #region Properties

    /// 
    /// Gets a value indicating whether to ignore the execution of filter actions
    /// 
    public bool IgnoreFilter { get; }

    #endregion

    #region Nested filter

    /// 
    /// Represents a filter confirming that checks whether current connection is secured and properly redirect if necessary
    /// 
    private class HttpsRequirementFilter : IAsyncAuthorizationFilter
    {
        #region Fields

        protected readonly bool _ignoreFilter;
        protected readonly IStoreContext _storeContext;
        protected readonly IWebHelper _webHelper;
        protected readonly IWebHostEnvironment _webHostEnvironment;

        #endregion

        #region Ctor

        public HttpsRequirementFilter(bool ignoreFilter, IStoreContext storeContext, IWebHelper webHelper, IWebHostEnvironment webHostEnvironment)
        {
            _ignoreFilter = ignoreFilter;
            _storeContext = storeContext;
            _webHelper = webHelper;
            _webHostEnvironment = webHostEnvironment;
        }

        #endregion

        #region Utilities

        /// 
        /// Called early in the filter pipeline to confirm request is authorized
        /// 
        /// Authorization filter context
        /// A task that represents the asynchronous operation
        private async Task CheckHttpsRequirementAsync(AuthorizationFilterContext context)
        {
            ArgumentNullException.ThrowIfNull(context);

            //only in GET requests, otherwise the browser might not propagate the verb and request body correctly
            if (!context.HttpContext.Request.IsGetRequest())
                return;

            if (!DataSettingsManager.IsDatabaseInstalled())
                return;

            //check whether this filter has been overridden for the action
            var actionFilter = context.ActionDescriptor.FilterDescriptors
                .Where(filterDescriptor => filterDescriptor.Scope == FilterScope.Action)
                .Select(filterDescriptor => filterDescriptor.Filter)
                .OfType()
                .FirstOrDefault();

            if (actionFilter?.IgnoreFilter ?? _ignoreFilter)
                return;

            var store = await _storeContext.GetCurrentStoreAsync();

            //whether current connection is secured
            var currentConnectionSecured = _webHelper.IsCurrentConnectionSecured();

            //link caching can cause unstable behavior in development environments, when we use permanent redirects
            var isPermanent = !_webHostEnvironment.IsDevelopment();

            //page should be secured, so redirect (permanent) to HTTPS version of page
            if (store.SslEnabled && !currentConnectionSecured)
                context.Result = new RedirectResult(_webHelper.GetThisPageUrl(true, true), isPermanent);

            //page shouldn't be secured, so redirect (permanent) to HTTP version of page
            if (!store.SslEnabled && currentConnectionSecured)
                context.Result = new RedirectResult(_webHelper.GetThisPageUrl(true, false), isPermanent);
        }

        #endregion

        #region Methods

        /// 
        /// Called early in the filter pipeline to confirm request is authorized
        /// 
        /// Authorization filter context
        /// A task that represents the asynchronous operation
        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            await CheckHttpsRequirementAsync(context);
        }

        #endregion
    }

    #endregion
}