Created
December 8, 2020 09:43
-
-
Save Frisch12/0c061fc14d5fd38b123f1ba3db92d367 to your computer and use it in GitHub Desktop.
AuthorizationHandler becomes no Endpoint resource
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Net.Http.Headers; | |
| using System.Security.Claims; | |
| using System.Text; | |
| using System.Text.Encodings.Web; | |
| using System.Threading.Tasks; | |
| using Microsoft.AspNetCore.Authentication; | |
| using Microsoft.AspNetCore.Authorization; | |
| using Microsoft.AspNetCore.Builder; | |
| using Microsoft.AspNetCore.Hosting; | |
| using Microsoft.AspNetCore.Http; | |
| using Microsoft.AspNetCore.Mvc; | |
| using Microsoft.Extensions.DependencyInjection; | |
| using Microsoft.Extensions.Hosting; | |
| using Microsoft.Extensions.Logging; | |
| using Microsoft.Extensions.Options; | |
| namespace TestApplication | |
| { | |
| public class Program | |
| { | |
| public static void Main(string[] args) | |
| { | |
| CreateHostBuilder(args).Build().Run(); | |
| } | |
| public static IHostBuilder CreateHostBuilder(string[] args) => | |
| Host.CreateDefaultBuilder(args) | |
| .ConfigureWebHostDefaults(webBuilder => | |
| { | |
| webBuilder.UseStartup<Startup>(); | |
| }); | |
| } | |
| public class Startup | |
| { | |
| // This method gets called by the runtime. Use this method to add services to the container. | |
| // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 | |
| public void ConfigureServices(IServiceCollection services) | |
| { | |
| services.AddControllers(); | |
| services.AddTransient<NeedToBeAuthorizedHandler>(); | |
| services.AddAuthentication() | |
| .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>( | |
| AuthenticationSchemaNames.BasicAuthentication, | |
| null); | |
| services.AddAuthorization( | |
| options => | |
| { | |
| var policy = new AuthorizationPolicyBuilder() | |
| .AddAuthenticationSchemes(AuthenticationSchemaNames.BasicAuthentication) | |
| .AddRequirements(new NeedToBeAuthorizedRequirement()) | |
| .Build(); | |
| options.DefaultPolicy = policy; | |
| options.FallbackPolicy = policy; | |
| }); | |
| } | |
| // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |
| public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |
| { | |
| if (env.IsDevelopment()) | |
| { | |
| app.UseDeveloperExceptionPage(); | |
| } | |
| app.UseRouting(); | |
| app.UseAuthorization(); | |
| app.UseEndpoints(endpoints => endpoints.MapControllers()); | |
| } | |
| } | |
| [Route("/test")] | |
| public class TestController : ControllerBase | |
| { | |
| [HttpGet("info")] | |
| [NoAuthorization] | |
| public IActionResult TestActionPublic() | |
| { | |
| return Ok("Test result public"); | |
| } | |
| [HttpGet("info-private")] | |
| [AuthorizeOwn(Action = "read")] | |
| public IActionResult TestActionPrivate() | |
| { | |
| return Ok("Test private"); | |
| } | |
| [HttpGet("info-private")] | |
| [AuthorizeOwn(Action = "write")] | |
| public IActionResult TestActionPrivateNoAccess() | |
| { | |
| return Ok("Test no access"); | |
| } | |
| } | |
| public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> | |
| { | |
| public BasicAuthenticationHandler( | |
| IOptionsMonitor<AuthenticationSchemeOptions> options, | |
| ILoggerFactory logger, | |
| UrlEncoder encoder, | |
| ISystemClock clock) | |
| : base(options, logger, encoder, clock) | |
| { | |
| } | |
| protected override Task<AuthenticateResult> HandleAuthenticateAsync() | |
| { | |
| if (!Request.Headers.ContainsKey("Authorization")) | |
| { | |
| return Task.FromResult(AuthenticateResult.NoResult()); | |
| } | |
| ClaimsPrincipal user; | |
| try | |
| { | |
| var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); | |
| if (!string.Equals("Basic", authHeader.Scheme, StringComparison.InvariantCultureIgnoreCase)) | |
| { | |
| return Task.FromResult(AuthenticateResult.NoResult()); | |
| } | |
| var credentialBytes = Convert.FromBase64String(authHeader.Parameter); | |
| var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] {':'}, 2); | |
| var username = credentials[0]; | |
| var password = credentials[1]; | |
| if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) | |
| { | |
| return Task.FromResult(AuthenticateResult.Fail("Username and Password are mandatory")); | |
| } | |
| user = Validate(username, password); | |
| if (user == null) | |
| { | |
| return Task.FromResult(AuthenticateResult.Fail("Invalid Username or Password")); | |
| } | |
| } | |
| catch | |
| { | |
| return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header")); | |
| } | |
| var ticket = new AuthenticationTicket(user, Scheme.Name); | |
| return Task.FromResult(AuthenticateResult.Success(ticket)); | |
| } | |
| private ClaimsPrincipal Validate(string username, string password) | |
| { | |
| var isValid = IsUserNameAndPasswordValid(username, password); | |
| if (!isValid) | |
| { | |
| return null; | |
| } | |
| return new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new("username", username) }, "BasicAuth")); | |
| } | |
| private bool IsUserNameAndPasswordValid(string reqUserName, string reqPassword) | |
| { | |
| return reqPassword == "123456" && reqUserName == "user"; | |
| } | |
| } | |
| public class NeedToBeAuthorizedHandler : AuthorizationHandler<NeedToBeAuthorizedRequirement, Endpoint> | |
| { | |
| protected override Task HandleRequirementAsync( | |
| AuthorizationHandlerContext context, | |
| NeedToBeAuthorizedRequirement requirement, | |
| Endpoint endpoint) | |
| { | |
| var authorizeAttribute = endpoint.Metadata.GetMetadata<AuthorizeOwnAttribute>(); | |
| if (authorizeAttribute == null) | |
| { | |
| var noAuthorizationAttribute = endpoint.Metadata.GetMetadata<NoAuthorizationAttribute>(); | |
| if (noAuthorizationAttribute == null) | |
| { | |
| context.Fail(); | |
| return Task.CompletedTask; | |
| } | |
| context.Succeed(requirement); | |
| return Task.CompletedTask; | |
| } | |
| if (context.User.Identity?.IsAuthenticated != true) | |
| { | |
| context.Fail(); | |
| return Task.CompletedTask; | |
| } | |
| if (authorizeAttribute.Action == "write") | |
| { | |
| context.Fail(); | |
| return Task.CompletedTask; | |
| } | |
| context.Succeed(requirement); | |
| return Task.CompletedTask; | |
| } | |
| } | |
| public class NeedToBeAuthorizedRequirement : IAuthorizationRequirement | |
| { | |
| } | |
| public static class AuthenticationSchemaNames | |
| { | |
| public static readonly string BasicAuthentication = "Basic"; | |
| } | |
| public sealed class AuthorizeOwnAttribute : AuthorizeAttribute | |
| { | |
| public string Action { get; set; } | |
| } | |
| public class NoAuthorizationAttribute : AuthorizeAttribute | |
| { | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment