class

AspnetAuthTagger

Inherits FrameworkTagger < FileHelper < Tagger < Reference < Object

Constants

ACTION_AUTH_PATTERNS = [{/User\.Identity\.IsAuthenticated/, "ASP.NET User.Identity.IsAuthenticated"}, {/User\.IsInRole\s*\(/, "ASP.NET User.IsInRole check"}, {/HttpContext\.User/, "ASP.NET HttpContext.User check"}]

ASP.NET Core middleware auth in action body

ACTION_REFUSAL_RE = /\breturn\s+(?:(?:base|this)\.)?(?:Challenge|Forbid|Unauthorized)\s*\(|\breturn\s+new\s+(?:ChallengeResult|ForbidResult|UnauthorizedResult)\b|\breturn\s+StatusCode\s*\(\s*(?:40[13]\b|StatusCodes\.Status40[13]|\(int\)\s*HttpStatusCode\.(?:Unauthorized|Forbidden))/

return Challenge(); / Forbid() / Unauthorized() / a 401/403 status.

ALLOW_ANONYMOUS_PATTERN = /\[(?:[^\]]*,\s*)?AllowAnonymous\s*[(,\]]/

Public override markers

ATTRIBUTE_BASE_ROOTS = {"AuthorizeAttribute" => :authorize, "IAllowAnonymous" => :anonymous, "AllowAnonymousAttribute" => :anonymous}
AUTHORIZE_KINDS = {"Roles" => "ASP.NET [Authorize(Roles)]", "Policy" => "ASP.NET [Authorize(Policy)]", "AuthenticationSchemes" => "ASP.NET [Authorize(AuthenticationSchemes)]"}
AUTHORIZE_PATTERNS = [{/\[Authorize\]/, "ASP.NET [Authorize]"}, {/\[Authorize\s*\(\s*Roles\s*=/, "ASP.NET [Authorize(Roles)]"}, {/\[Authorize\s*\(\s*Policy\s*=/, "ASP.NET [Authorize(Policy)]"}, {/\[Authorize\s*\(\s*AuthenticationSchemes\s*=/, "ASP.NET [Authorize(AuthenticationSchemes)]"}, {/\[(?:[^\]]*,\s*)?Authorize\s*[(,\]]/, "ASP.NET [Authorize]"}]

[Authorize] on a minimal API handler lambda, read off the route statement's masked text (attributes on declarations are read as nodes).

CALLER_CALL = /\bGetCurrent(?:User|Customer|Account|Member|Principal)\w*\s*\(|\w+\s*\(\s*(?:(?:\w+\.)*HttpContext\.)?User\s*\)/

A call naming the current caller: an accessor for the signed-in user/customer, or a lookup handed the ClaimsPrincipal.

CLASS_DECL_NAME_RE = /\bclass\s+([A-Z]\w*)/
CONTROLLER_TECHS = ["cs_aspnet_mvc", "cs_aspnet_core_mvc"] of ::String
CONTROLLERS_GLOBAL = /MapControllers\s*\(\s*\)\s*\.RequireAuthorization\s*\(|Filters\.Add\s*(?:<\s*AuthorizeFilter\s*>|\(\s*new\s+AuthorizeFilter\b)|filters\.Add\s*\(\s*new\s+(?:System\.Web\.Mvc\.)?AuthorizeAttribute\b/
CSPROJ = /\.csproj\z/
FALLBACK_POLICY = /FallbackPolicy\s*=[^;]*RequireAuthenticatedUser\s*\(/

App-wide authorization that every endpoint without its own [AllowAnonymous] inherits: ASP.NET Core's FallbackPolicy, the controller-wide MapControllers().RequireAuthorization() / global AuthorizeFilter, and MVC 5's filters.Add(new AuthorizeAttribute()).

FILTER_REFUSAL_RE = /\b\w+\.Result\s*=\s*new\s+(?:ChallengeResult|ForbidResult|UnauthorizedResult|UnauthorizedObjectResult)\b|\b\w+\.Result\s*=\s*new\s+StatusCodeResult\s*\(\s*(?:40[13]\b|StatusCodes\.Status40[13]|\(int\)\s*HttpStatusCode\.(?:Unauthorized|Forbidden))/

An MVC filter refusing the request: an authentication challenge, a forbid, or a 401/403 status.

GROUP_DECLARATION_RE = /\b(\w+)\s*=\s*[\w.]*MapGroup\s*\(/
GROUP_DECLARATION_RECEIVER = /=\s*(\w+)\s*\.\s*MapGroup\b/

var v1 = api.MapGroup("/v1"): a nested group, which inherits api's.

GROUP_STATEMENT_RE = /^\s*(\w+)\s*\.\s*RequireAuthorization\s*\(/
HOST_PROJECT_RE = /Sdk\s*=\s*"Microsoft\.NET\.Sdk\.Web"|<OutputType>\s*(?:Win)?Exe\s*</i

Web SDK projects and executables are apps; the rest are libraries.

LOCAL_ASSIGNMENT_RE = /\b(?:var|[A-Z][\w<>]*\??)\s+(\w+)\s*=\s*([^;]+);/
MEMBER_DECLARATION = /^\s*(?:\[[^\]]*\]\s*)*(?:(?:public|private|protected|internal|static|async|override|virtual|sealed|unsafe|partial|new|extern)\s+)*(?:[\w?<>,\[\]]+\s+)?(?!(?:if|for|foreach|while|switch|catch|using|lock|fixed)\b)[\w]+\s*\(/

A method or local function, with or without an access modifier. The name is one identifier, so a call (group.MapGet(, Task.Delay() is not one, and if ( / using ( are not either.

MINIMAL_API_ALLOW_ANON = /\.AllowAnonymous\s*\(/
MINIMAL_API_RECEIVER = /^\s*(\w+)\s*\.\s*Map(?:Get|Post|Put|Delete|Patch|Methods|Group)\b/
MINIMAL_API_REQUIRE_AUTH = /\.RequireAuthorization\s*\(/

Minimal API fluent auth: app.MapGet("/x", h).RequireAuthorization();. This is a chained method call, never a [...] attribute, so it can't live in AUTHORIZE_PATTERNS (which is scanned in attribute position).

PERMISSION_CALL = /\A(?:await\s+)?_\w+\s*\.\s*\w*(?:Authorize|Permission)\w*\s*\(/

A permission lookup on an injected service: which roles hold the permission is data, so configuration decides who gets through.

PRINCIPAL_EXPR = /(?<![\w.])User\b|\bHttpContext\.User\b/

The request's ClaimsPrincipal: the controller's User, or HttpContext.User (not a model's request.User).

PROJECT_REFERENCE_RE = /<ProjectReference\s[^>]*?Include\s*=\s*"([^"]+)"/i
ROUTE_BUILDER_EXTENSION = /\bstatic\s[^;{}()=]*?\b(\w+)\s*(?:<[^<>()]*>\s*)?\(\s*this\s+(?:RouteGroupBuilder|IEndpointRouteBuilder|WebApplication)\s+(\w+)/

A route-builder extension method, static RouteGroupBuilder MapOrdersApi(this IEndpointRouteBuilder app): its name and the name of its this builder.

ROUTE_STATEMENT_TECHS = ["cs_aspnet_core_minimal_api", "cs_carter"] of ::String

Techs whose endpoint line is a route registration statement.

STATEMENT_LINES = 80

The statement starting on line_idx, through the ; outside every bracket: a block-bodied lambda's own ;s do not end it, so auth chained after its }) is part of it. A closing bracket the statement did not open (the end of the enclosing block) ends it too.

TEST_PATH = /\/(?:[Tt]ests?|testassets|[^\/]+\.\w*Tests?)\/|Tests?\.cs\z/

Test hosts (WebApplicationFactory, a Foo.Tests project's Startup) configure their own auth, which must not leak onto the app. The conventions of Analyzer::CSharp::Common.csharp_test_path?, plus the *.Tests project directory.

TYPEOF_RE = /\btypeof\s*\(\s*(\w+)\s*\)/
USING_RE = /^\s*(?:global\s+)?using\s+([\w.]+)\s*;/m

Constructors

new(options : Hash(String, YAML::Any))
Source

Class methods

target_techs
Source

Instance methods

perform(endpoints : Array(Endpoint)) : Array(Endpoint)

Collects the project roots (nearest .csproj) whose live, non-test code turns on app-wide authorization.

Source

Nested types