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;
}
}
}