Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Required] doesn't seem to have an effect #505

Closed
Richard87 opened this issue Aug 7, 2023 · 2 comments
Closed

[Required] doesn't seem to have an effect #505

Richard87 opened this issue Aug 7, 2023 · 2 comments
Labels
bug Something isn't working
Milestone

Comments

@Richard87
Copy link
Contributor

I have this code

using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using static Happydogs.Domain.Breeder.Event;
using Wolverine.Http;

namespace Happydogs.Domain.Breeder;

// ReSharper disable once ClassNeverInstantiated.Global
public record ChangeVisionCommand(string Vision, Guid BreederId);

public class ChangeBreederVisionEndpoint
{
    public static async Task<IResult> Before(ChangeVisionCommand c, ClaimsPrincipal u, IAuthorizationService auth, CancellationToken ct)
    {
        var authenticated = await auth.AuthorizeAsync(u, c.BreederId, "EditBreeder");
        return !authenticated.Succeeded ? Results.Forbid() : WolverineContinue.Result();
    }

    public static async Task<Breeder?> LoadAsync(ChangeVisionCommand c, AggregateRepository r, CancellationToken ct)
        => await r.LoadAsync<Breeder>(c.BreederId, null, ct);

    [Tags("Breeder")]
    [ProducesResponseType(204)]
    [WolverinePost("/api/breeder/change-vision")]
    public static  async Task<IResult> Post(ChangeVisionCommand c,[Required] Breeder? breeder, AggregateRepository repo, CancellationToken ct)
    {
        if (breeder is null) return Results.NotFound();

        breeder.ChangeNextPlannedLitter(c.Vision);

        await repo.StoreAsync(breeder, ct);
        return Results.NoContent();
    }
}

That produces this output:

// <auto-generated/>
#pragma warning disable
using Happydogs.Domain;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using Wolverine.Http;
using Wolverine.Runtime;

namespace Internal.Generated.WolverineHandlers
{
    // START: POST_api_breeder_change_vision
    public class POST_api_breeder_change_vision : Wolverine.Http.HttpHandler
    {
        private readonly Wolverine.Http.WolverineHttpOptions _wolverineHttpOptions;
        private readonly Happydogs.Domain.AggregateRepository _aggregateRepository;
        private readonly Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Authorization.DefaultAuthorizationService> _logger;
        private readonly Microsoft.AspNetCore.Authorization.IAuthorizationHandler _authorizationHandler949362532;
        private readonly Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions> _options1;
        private readonly Microsoft.AspNetCore.Authorization.IAuthorizationHandler _authorizationHandler_188359246;
        private readonly Wolverine.Runtime.IWolverineRuntime _wolverineRuntime;
        private readonly Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions> _options3;
        private readonly Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions> _options2;
        private readonly Microsoft.AspNetCore.Authorization.IAuthorizationHandler _authorizationHandler_44409949;

        public POST_api_breeder_change_vision(Wolverine.Http.WolverineHttpOptions wolverineHttpOptions, Happydogs.Domain.AggregateRepository aggregateRepository, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Authorization.DefaultAuthorizationService> logger, [Lamar.Named("memberAuthorizationHandler")] Microsoft.AspNetCore.Authorization.IAuthorizationHandler authorizationHandler949362532, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions> __options1, [Lamar.Named("breederAuthorizationHandler")] Microsoft.AspNetCore.Authorization.IAuthorizationHandler authorizationHandler_188359246, Wolverine.Runtime.IWolverineRuntime wolverineRuntime, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions> __options3, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions> __options2, [Lamar.Named("dogAuthorizationHandler")] Microsoft.AspNetCore.Authorization.IAuthorizationHandler authorizationHandler_44409949) : base(wolverineHttpOptions)
        {
            _wolverineHttpOptions = wolverineHttpOptions;
            _aggregateRepository = aggregateRepository;
            _logger = logger;
            _authorizationHandler949362532 = authorizationHandler949362532;
            _options1 = __options1;
            _authorizationHandler_188359246 = authorizationHandler_188359246;
            _wolverineRuntime = wolverineRuntime;
            _options3 = __options3;
            _options2 = __options2;
            _authorizationHandler_44409949 = authorizationHandler_44409949;
        }



        public override async System.Threading.Tasks.Task Handle(Microsoft.AspNetCore.Http.HttpContext httpContext)
        {
            var defaultAuthorizationPolicyProvider = new Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider(_options1);
            var passThroughAuthorizationHandler = new Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler(_options2);
            var authorizationHandlerList = new System.Collections.Generic.List<Microsoft.AspNetCore.Authorization.IAuthorizationHandler>{passThroughAuthorizationHandler, _authorizationHandler949362532, _authorizationHandler_188359246, _authorizationHandler_44409949};
            var defaultAuthorizationHandlerProvider = new Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider(authorizationHandlerList);
            var defaultAuthorizationHandlerContextFactory = new Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory();
            var defaultAuthorizationEvaluator = new Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator();
            var defaultAuthorizationService = new Microsoft.AspNetCore.Authorization.DefaultAuthorizationService(defaultAuthorizationPolicyProvider, defaultAuthorizationHandlerProvider, _logger, defaultAuthorizationHandlerContextFactory, defaultAuthorizationEvaluator, _options3);
            var messageContext = new Wolverine.Runtime.MessageContext(_wolverineRuntime);
            Wolverine.Http.Runtime.RequestIdMiddleware.Apply(httpContext, messageContext);
            var (c, jsonContinue) = await ReadJsonAsync<Happydogs.Domain.Breeder.ChangeVisionCommand>(httpContext);
            if (jsonContinue == Wolverine.HandlerContinuation.Stop) return;
            var result1 = await Happydogs.Domain.Breeder.ChangeBreederVisionEndpoint.Before(c, httpContext.User, defaultAuthorizationService, httpContext.RequestAborted).ConfigureAwait(false);
            if (!(result1 is Wolverine.Http.WolverineContinue))
            {
                await result1.ExecuteAsync(httpContext).ConfigureAwait(false);
                return;
            }

            var breeder = await Happydogs.Domain.Breeder.ChangeBreederVisionEndpoint.LoadAsync(c, _aggregateRepository, httpContext.RequestAborted).ConfigureAwait(false);
            var result = await Happydogs.Domain.Breeder.ChangeBreederVisionEndpoint.Post(c, breeder, _aggregateRepository, httpContext.RequestAborted).ConfigureAwait(false);
            await result.ExecuteAsync(httpContext).ConfigureAwait(false);
        }

    }

    // END: POST_api_breeder_change_vision
    
    
}
@Richard87
Copy link
Contributor Author

Some extra context :)

Program.cs

using System.Net.Http.Headers;
using Authentication;
using Happydogs.Domain;
using Happydogs.ReadModels;
using Marten;
using Oakton;
using Oakton.Resources;
using Wolverine;
using Wolverine.Http;
using Wolverine.Marten;
using Wolverine.Postgresql;
using OpenTelemetry.Trace;
using Marten.Events.Daemon.Resiliency;
using Marten.Events.Projections;
using Marten.Services.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Net.Http.Headers;
using Npgsql;
using OpenTelemetry.Resources;
using Svix;
using Utility.Api;
using Web.Configure;
using Wolverine.FluentValidation;
using Wolverine.Http.FluentValidation;

var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.local.json", optional: true).AddEnvironmentVariables();
string clerkApiKey = builder.Configuration["Clerk:ApiKey"] ?? throw new Exception("No Clerk API key found");
string clerkAuthority = builder.Configuration["Clerk:ClerkAPI"] ?? throw new Exception("No Clerk API found");
string sendgridApiKey = builder.Configuration["SendGrid:ApiKey"] ?? throw new Exception("No SendGrid API key found");
string connectionString = builder.Configuration["ConnectionStrings:postgres"] ?? throw new Exception("No connection string found");
string clerkWebhookSecret = builder.Configuration["Clerk:WebhookSecret"] ?? throw new Exception("No Clerk webhook secret found");
string notionApiKey = builder.Configuration["Notion:ApiKey"] ?? throw new Exception("No Notion API Key found");

builder.Host.ApplyOaktonExtensions();
builder.Host.UseResourceSetupOnStartup();
// builder.Host.UseSerilog();

builder.Services.AddSingleton<Webhook>(_ => new Webhook(clerkWebhookSecret));
builder.Services.AddNotionClient(options => { options.AuthToken = notionApiKey; });

builder.Services.AddHttpClient("SendGrid", (provider, client) =>
{
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", sendgridApiKey);
    client.BaseAddress = new Uri("https://api.sendgrid.com");
});
builder.Services.AddHttpClient<ClerkClient>((provider, client) =>
{
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", clerkApiKey);
    client.BaseAddress = new Uri("https://api.clerk.com");
});
builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "_happydogsServers",
        policy  =>
        {
            policy.WithOrigins("https://happydogs.no", "http://localhost:3000")
                .WithHeaders(HeaderNames.Authorization, HeaderNames.ContentType, HeaderNames.Accept);
        });
});

builder.Services.AddClerkAuthentication(clerkAuthority);
builder.Services.AddSvixAuthentication(clerkWebhookSecret);
builder.Services.Configure<CloudflareOptions>(o =>
{
    o.AccessKey = builder.Configuration["Cloudflare:AccessKey"] ?? throw new Exception("No Cloudflare access key found");
    o.SecretKey = builder.Configuration["Cloudflare:SecretKey"] ?? throw new Exception("No Cloudflare secret key found");
    o.AccountId = builder.Configuration["Cloudflare:AccountId"] ?? throw new Exception("No Cloudflare account ID found");
    o.Bucket = builder.Configuration["Cloudflare:Bucket"] ?? throw new Exception("No Cloudflare bucket found");
});
builder.Services.AddScoped<Cloudflare>();

var happydogsAssembly = typeof(Happydogs.Domain.AggregateBase).Assembly;
builder.Services.AddControllers()
    .AddApplicationPart(happydogsAssembly)
    .AddControllersAsServices();


// Add services to the container
builder.Services.AddMarten(opts =>
    {
        opts.Connection(connectionString);
        opts.UseDefaultSerialization(serializerType: SerializerType.SystemTextJson );

        opts.Events.MetadataConfig.HeadersEnabled = true;
        opts.Events.MetadataConfig.CausationIdEnabled = true;
        opts.Events.MetadataConfig.CorrelationIdEnabled = true;

        opts.Events.AddEventTypes(
            typeof(EventBase)
                .Assembly
                .GetTypes()
                .Where(typeof(EventBase).IsAssignableFrom));

        opts.Projections.Add<MembersProjection>(ProjectionLifecycle.Async);
        opts.Projections.Add<MemberBreederProjection>(ProjectionLifecycle.Async);
        opts.Projections.Add<DogMemberProjection>(ProjectionLifecycle.Async);
        opts.Projections.Add<DogsProjection>(ProjectionLifecycle.Async);
        opts.Projections.Add<BreedersProjection>(ProjectionLifecycle.Async);
    })
    .IntegrateWithWolverine()
    .UseLightweightSessions()
    .AddAsyncDaemon(DaemonMode.Solo)
    .ApplyAllDatabaseChangesOnStartup()
    .InitializeWith();

builder.Host.UseWolverine(opts =>
{
    opts.PersistMessagesWithPostgresql(connectionString);
    // This middleware will apply to the HTTP
    // endpoints as well
    opts.Policies.AutoApplyTransactions();

    // Setting up the outbox on all locally handled
    // background tasks
    opts.Policies.UseDurableLocalQueues();
    opts.Discovery.IncludeAssembly(happydogsAssembly);


    opts.UseFluentValidation();
});
builder.Services.AddOpenTelemetry()
    .ConfigureResource(b => b.AddService(serviceName: "HappyDogs"))
    .WithTracing(b => b
        .AddAspNetCoreInstrumentation()
        .AddOtlpExporter()
        .AddNpgsql()
        .AddSource("Wolverine")
    );
builder.Services.AddSingleton<AggregateRepository>();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddHappyDogsSwagger();

var app = builder.Build();
app.UseCors("_happydogsServers");
// app.UseSvixAuthMiddleware();
app.UseAuthentication();
app.UseAuthorization();


// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapGet("/hello", [Authorize](HttpContext context) => "Hello World!");

// Let's add in Wolverine HTTP endpoints to the routing tree
app.MapWolverineEndpoints(opts =>
{
    opts.UseFluentValidationProblemDetailMiddleware();
});

return await app.RunOaktonCommands(args);

My AgregateRepositorys LoadAsync:

    public async Task<T?> LoadAsync<T>(
        Guid id,
        int? version = null,
        CancellationToken ct = default
    ) where T : AggregateBase
    {
        await using var session = await _store.LightweightSerializableSessionAsync(token: ct);
        var aggregate = await session.Events.AggregateStreamAsync<T>(id, version ?? 0, token: ct);

        if (aggregate is null || aggregate.Version == 0)
        {
            return null;
        }

        return aggregate;
    }

@jeremydmiller jeremydmiller added this to the 1.6.0 milestone Aug 10, 2023
@jeremydmiller jeremydmiller added the bug Something isn't working label Aug 11, 2023
@jeremydmiller
Copy link
Member

@Richard87 It didn't catch on that because Wolverine had been coded to only care about [Required] if the route had any parameters. I guess I don't care enough about whether that was true or not, so I widened the applicability to "any method argument w/ [Required]" to fix this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants