Search code examples
c#integration-testingrazor-pagesasp.net-core-3.1xunit.net

How Can I remove AuthorizeAreaFolder option from service in integration test


I have a integration test for my razor web application with custom WebApplicationFactory:

public class CustomWebApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            var descriptor = services.SingleOrDefault(
                d => d.ServiceType ==
                    typeof(DbContextOptions<GenericDbContext>));

            services.Remove(descriptor);


            services.AddDbContext<GenericDbContext>(options =>
            {
                options.UseInMemoryDatabase("InMemoryDbForTesting");
            });

            var sp = services.BuildServiceProvider();

            using var scope = sp.CreateScope();

            var scopedServices = scope.ServiceProvider;
            var db = scopedServices.GetRequiredService<GenericDbContext>();
            var logger = scopedServices
                .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

            db.Database.EnsureCreated();

            try
            {
                Utilities.InitializeDbForTests(db);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "An error occurred seeding the " +
                    "database with test messages. Error: {Message}", ex.Message);
            }
        });
    }
}

In the TStartup I am passing this startup configure services:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    //AzureADB2C
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    
    .AddAzureAdB2C(options => Configuration.Bind("Authentication:AzureAdB2C", options))
    .AddCookie(options =>
    {
        options.Cookie.SameSite = SameSiteMode.None;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.IsEssential = true;
        //options.ExpireTimeSpan = TimeSpan.FromDays(365);
    });
    //AzureADB2C

    //Simple
    //services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
    //    .AddAzureADB2C(options => Configuration.Bind("Authentication:AzureAdB2C", options));


    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => false; // = context => true; //When set to true, TempData will not work unless the user opts in.
        options.MinimumSameSitePolicy = SameSiteMode.Unspecified; //If we dont do the hack for chrome/safari, this should be set to 'None'
        options.Secure = CookieSecurePolicy.Always;

        //Hack for Chrome/Safari. May not be needed in future
        options.OnAppendCookie = cookieContext =>
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        options.OnDeleteCookie = cookieContext =>
           CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    });

    services.AddRouting(options => {
        options.LowercaseUrls = true;
        //options.AppendTrailingSlash = false;
        });


 
    //Response Caching
    services.AddResponseCaching();

    //With conditional runtime compilation
    IMvcBuilder builder = services.AddRazorPages()
        .AddRazorPagesOptions(options =>
        {
            //Sitemap
            options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");



            options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
            options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
            options.Conventions.AuthorizeAreaFolder("xxxxxxx", "/", "xxxxx");
            //options.Conventions.AuthorizeAreaFolder("xxxx", "/");
            options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
            options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
            options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
            //options.Conventions.AuthorizeAreaFolder("xxxxx", "/");
            options.Conventions.AuthorizeAreaPage("xxxx", "/xxxxx");


            //password reset
            options.Conventions.AllowAnonymousToAreaPage("xxxxx", "/xxxxx");
            options.Conventions.AllowAnonymousToAreaPage("xxxxx", "/xxxx");

            //Checkout and 3DS pages
            options.Conventions.AllowAnonymousToAreaPage("xxxxx", "/xxxxx");


        });

    services.AddMemoryCache();

    //CACHE for tokens
    // Adds a default in-memory implementation of IDistributedCache.
    services.AddDistributedMemoryCache();
    //services.AddSession();

    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromHours(1);
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Unspecified;// SameSiteMode.None;
        //options.IdleTimeout = TimeSpan.FromDays(365);
    });


    services.AddDbContext<IGenericDbContext, GenericDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("GenericDbConnection")));
}

Like I am removing dbContextOption in the custom WebApplicationFactory, I want to remove AuthorizeAreaFolder or to override "services.AddRazorPages().AddRazorPagesOptions()" to allow integration test to get or post a page without faking authorization.


Solution

  • AddRazorPagesOptions essentially just configures the RazorPagesOptions on the service collection, so it is equivalent to the following:

    services.Configure<RazorPagesOptions>(options =>
    {
        options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
        // …
    });
    

    That also means that in order to change the conventions, you can reconfigure the RazorPagesOptions and modify the conventions collection directly. Unfortunately, the IPageConvention types are private, so you cannot look for the entries that are added by AuthorizeAreaFolder directly and remove them with the RemoveType method. However, you could clear the whole collection to remove all conventions completely:

    services.Configure<RazorPagesOptions>(options =>
    {
        options.Conventions.Clear();
    });