web-dev-qa-db-fra.com

L'autorisation MVC dans MapWhen () est appliquée à tous les contrôleurs

Dans ASP.Net Core 3.0 Preview 7, j'ai essayé d'écrire du code comme suit:

  public void Configure(IApplicationBuilder app) {

            app.MapWhen(context =>
                context.Request.Path.StartsWithSegments(
                      new PathString("/UnsecureLog")),
                a => {
                    a.UseRouting();
                    a.UseEndpoints(endpoints => {
                        endpoints.MapControllers();
                    });
                }
            );
            app.UseAuthentication();
            app.UseAuthorization();

            app.MapWhen(context =>
                context.Request.Path.StartsWithSegments(
                   new PathString("/SecureLog")),
                a => {
                    a.UseRouting();
                    a.UseEndpoints(endpoints => {
                        endpoints.MapControllers()
                            .RequireAuthorization("MustBeReader");
                    });
                }
            );
        }

Mon objectif était de permettre à certains contrôleurs d'être gérés dans le middleware sans authentification, et je pensais que MapWhen () était le moyen de le retirer.

Au lieu de cela, je vois cette erreur lorsque je frappe le point de terminaison /UnsecureLog:

System.InvalidOperationException: Endpoint ... contains authorization metadata,
but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization()
inside the call to Configure(..) in the application startup code.

Traduction: "Comment allez-vous et implémentez des fonctionnalités de sécurité pour ce point de terminaison que vous ne vouliez pas sécuriser".

Ma conclusion est que tout appel à RequireAuthorization("MustBeReader") dans any MapWhen () la logique de contrôleur de gestion des blocs sera en fait appliqué à tous les routes du contrôleur MVC.

Ma solution de contournement actuelle consiste à supprimer l'appel .RequireAuthorization("MustBeReader") dans le deuxième bloc de code MapWhen () et à le réappliquer en tant qu'attribut ([RequireAuthorization("MustBeReader")]) aux points de terminaison que je souhaite sécuriser. Cela fonctionne sans erreur et produit le comportement souhaité.

Mais ce n'était pas le but, n'est-ce pas?

Je préfère gérer des groupes entiers de contrôleurs avec des politiques similaires, tout en épargnant les autres de la sécurité, et gérer tout cela à partir de Configure (). Au lieu de cela, je dois appliquer les exigences d'autorisation souhaitées à chaque contrôleur, au coup par coup.

J'espère qu'il existe une meilleure façon de mettre en œuvre le routage qui évite le problème noté ici. Peut-être que je fais quelque chose de mal.

Pensées, quelqu'un?

6
Wellspring

Je m'attribue le mérite de la réponse complète, après avoir consacré du temps aujourd'hui à cela et affiché le code. Merci à mwilson de m'avoir donné le Nudge pour réessayer.

Avant ma nouvelle approche, à chaque exécution du code, cet avertissement était affiché ...

Startup.cs(189,13): warning ASP0001: The call to UseAuthorization
should appear between app.UseRouting() and app.UseEndpoints(..)
for authorization to be correctly evaluated.

La solution n'est donc pas simplement de reconnaître cet avertissement et de le respecter, mais aussi de trouver un moyen d'apaiser les dieux du compilateur. (Et vous verrez ci-dessous que je n'ai toujours pas encore compris comment faire cela.)

Voici ce que j'ai découvert aujourd'hui. J'ai placé les appels UseAuthentication et UseAuthorization à deux endroits différents. Cela marche. Quelque peu.

Dans ce projet d'API, je peux désormais exécuter des points de terminaison anonymes non sécurisés, des points de terminaison sécurisés et, pour les points bonus, des points de terminaison GraphQL sécurisés également.

FWIW, code ci-dessous:

        public void Configure(...)
        {
            ...
            ...
            app.UseStaticFiles();
            app.UseRouting();

            app.MapWhen(
                context => (context.Request.Path
                    .StartsWithSegments(new PathString("/api"))
                ),
                a =>
                {
                    a.UseRouting();
                    a.UseAuthentication();
                    a.UseAuthorization();
                    a.UseEndpoints(endpoints =>
                    {
                        endpoints
                            .MapControllers()
                            .RequireAuthorization(...);
                    });
                }
            );

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseWebSockets();
            app.UseGraphQLWebSockets<...>(...);
            app.UseGraphQL<...>(...);
        }

Cela fonctionne, mais je reçois toujours l'avertissement du compilateur. De plus, si je deviens trop intelligent de moitié et que j'essaie d'utiliser le contrôleur suivant ...


    [Route("vapi/[controller]")]
    //[AllowAnonymous]
    public class VersionController : Controller
    { ...

avec ce code Startup.cs supplémentaire ...

            app.MapWhen(
                context => (context.Request.Path
                    .StartsWithSegments(new PathString("/vapi"))
                ),
                a =>
                {
                    a.UseRouting();
                    // a.UseAuthentication(); <-- look, Ma, no authentication!
                    // a.UseAuthorization(); <-- look, Ma, no authorization!
                    a.UseEndpoints(endpoints =>
                    {
                        endpoints
                            .MapControllers()
                            .RequireAuthorization(...);
                    });
                }
            );

Je reçois également l'erreur notée dans mon OP. (Tout me revient, comme un vieux cauchemar ...)

Endpoint ....Controllers.VersionController.Version (...) contains authorization
metadata, but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization() ...

Donc, où je laisse la question est la suivante: je ne peux toujours pas faire ce que je voulais faire, qui était de sécuriser uniquement certains chemins de contrôleur. Ce que je peux faire (sans ajouter ce code supportant le chemin "vapi" au démarrage) est le suivant:

    [Route("api/[controller]")]
    [AllowAnonymous]
    public class VersionController : Controller
    { ...

Alors ... je vais appeler cela la réponse, jusqu'à ce que quelqu'un publie un ensemble de code utilisable qui l'améliore.

0
Wellspring

Il est important de noter que app.UseAuthorization Doit apparaître entre app.UseRouting() et app.UseEndpoints(...);

Donc, cela devrait ressembler à ceci:

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });
1
mwilson

J'utilise l'attribut Authorize - sur les classes/contrôleurs pour forcer le système à exiger un utilisateur authentifié. Si vous spécifiez un paramètre sur cet attribut, l'utilisateur doit avoir cette revendication. Pour tout contrôleur ou méthode qui devrait être accessible sans authentification, nous définissons l'attribut AllowAnonymous - sur le contrôleur ou la méthode spécifique. Cela devrait annuler toutes les exigences d'autorisation.

0
Mark Elissen