using System; using System.IO; using System.Net; using System.Threading.Tasks; using System.Collections.Generic; using System.Threading; using System.Text; using Newtonsoft.Json; using System.Reflection; using System.Linq; using HeyRed.Mime; namespace Tesses.Http {/// /// Check username and password are correct or if request can be anonymous /// /// Username, can and will be "" on first request for resource /// Password, can and will be "" on first request for resource /// true for authorized, false for unauthorized public delegate bool Authenticate(string username, string password); /// /// Check username and password are correct or if request can be anonymous /// /// Server Context /// Username, can and will be "" on first request for resource /// Password, can and will be "" on first request for resource /// true for authorized, false for unauthorized public delegate bool AuthenticateWithContext(ServerContext context,string username,string password); /// /// Protect server with password /// public class BasicAuthRequestHandler : IRequestHandler { /// /// Construct server for user authorization /// /// callback for authorization /// server to protect /// realm parameter in WWW-Auhenticate Header public BasicAuthRequestHandler(Authenticate auth,IRequestHandler inner,string realm="SampleRealm") { Authenticate = auth; InnerServer = inner; Realm = realm; } /// /// Construct server for user authorization (With ServerContext in callback) /// /// callback for authorization /// server to protect /// realm parameter in WWW-Auhenticate Header public BasicAuthRequestHandler(AuthenticateWithContext auth,IRequestHandler inner,string realm = "SampleRealm") { AuthenticateWithContext=auth; InnerServer=inner; Realm = realm; } public async Task Handle(ServerContext ctx) { if(await Authorize(ctx)) { await RequestHandler.Guaranteed(ctx,InnerServer).Handle(ctx); } } /// /// Server to protect /// public IRequestHandler InnerServer { get; set; } /// /// Authentication callback without ServerContext /// public Authenticate Authenticate { get; set; } /// /// Authentication callback with ServerContext /// public AuthenticateWithContext AuthenticateWithContext {get;set;} /// /// Realm parameter in WWW-Authenticate header /// public string Realm { get; set; } private bool ValidAuth(ServerContext ctx) { string auth; if(Authenticate == null && AuthenticateWithContext == null) return true; if (ctx.Request.Headers.TryGetFirst("Authorization", out auth)) { string[] authorization = auth.Split(' '); //authorization_basic if (authorization[0] == "Basic") { string[] userPass = Encoding.UTF8.GetString(Convert.FromBase64String(authorization[1])).Split(new char[] { ':' },2); //return userPass.Equals($"{config.UserName}:{config.Password}", StringComparison.Ordinal); if(Authenticate != null) return Authenticate(userPass[0], userPass[1]); if(AuthenticateWithContext != null) return AuthenticateWithContext(ctx,userPass[0],userPass[2]); } }else{ if(Authenticate != null) return Authenticate("", ""); if(AuthenticateWithContext != null) return AuthenticateWithContext(ctx,"",""); } return false; } private async Task Authorize(ServerContext ctx) { if (Authenticate == null && AuthenticateWithContext == null) return true; if (ValidAuth(ctx)) return true; ctx.Response.Headers.Add("WWW-Authenticate", $"Basic realm=\"{Realm}\""); ctx.Response.StatusLine = 401; await UnauthorizedPage(ctx); return false; } protected virtual async Task UnauthorizedPage(ServerContext ctx) { ctx.Response.SendStatusCodeHtml(); await Task.CompletedTask; } } }